diff options
author | Vladimir Mezentsev <vladimir.mezentsev@oracle.com> | 2022-03-11 08:58:31 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2022-03-11 08:58:31 +0000 |
commit | bb368aad297fe3ad40cf397e6fc85aa471429a28 (patch) | |
tree | 0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng | |
parent | a655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff) | |
download | binutils-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip binutils-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz binutils-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.bz2 |
gprofng: a new GNU profiler
top-level
* Makefile.def: Add gprofng module.
* configure.ac: Add --enable-gprofng option.
* src-release.sh: Add gprofng.
* Makefile.in: Regenerate.
* configure: Regenerate.
* gprofng: New directory.
binutils
* MAINTAINERS: Add gprofng maintainer.
* README-how-to-make-a-release: Add gprofng.
include.
* collectorAPI.h: New file.
* libcollector.h: New file.
* libfcollector.h: New file.
Diffstat (limited to 'gprofng')
306 files changed, 222206 insertions, 0 deletions
diff --git a/gprofng/Makefile.am b/gprofng/Makefile.am new file mode 100644 index 0000000..3bf7074 --- /dev/null +++ b/gprofng/Makefile.am @@ -0,0 +1,79 @@ +## Process this file with automake to generate Makefile.in +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +ACLOCAL_AMFLAGS = -I . -I .. + +AUTOMAKE_OPTIONS = dejagnu foreign + +if BUILD_COLLECTOR + COLLECTOR_SUBDIRS = libcollector +endif +if BUILD_SRC + SRC_SUBDIRS = src gp-display-html doc +endif +SUBDIRS = $(COLLECTOR_SUBDIRS) $(SRC_SUBDIRS) +DIST_SUBDIRS = libcollector src gp-display-html doc + +# Setup the testing framework, if you have one +EXPECT = expect +RUNTEST = runtest +RUNTESTFLAGS = + +BASEDIR = $(srcdir)/.. +BFDDIR = $(BASEDIR)/bfd +jdk_inc = @jdk_inc@ +LD_NO_AS_NEEDED = @LD_NO_AS_NEEDED@ +GPROFNG_CFLAGS = @GPROFNG_CFLAGS@ +GPROFNG_CPPFLAGS = @GPROFNG_CPPFLAGS@ +GPROFNG_LIBDIR = @GPROFNG_LIBDIR@ + +AM_MAKEFLAGS = \ + jdk_inc="$(jdk_inc)" \ + LD_NO_AS_NEEDED="$(LD_NO_AS_NEEDED)" \ + GPROFNG_CFLAGS="$(GPROFNG_CFLAGS)" \ + GPROFNG_CPPFLAGS="$(GPROFNG_CPPFLAGS)" \ + GPROFNG_LIBDIR="$(GPROFNG_LIBDIR)" + +if TCL_TRY +check-DEJAGNU: site.exp development.exp + srcroot=`cd $(srcdir) && pwd`; export srcroot; \ + r=`pwd`; export r; \ + LC_ALL=C; export LC_ALL; \ + EXPECT=$(EXPECT); export EXPECT; \ + jdk_inc="$(jdk_inc)"; export jdk_inc; \ + runtest=$(RUNTEST); \ + if $(SHELL) -c "$$runtest --version" > /dev/null 2>&1; then \ + $$runtest --tool $(DEJATOOL) --srcdir $${srcroot}/testsuite \ + MAKE="$(MAKE)" CC="$(CC)" CFLAGS="$(CFLAGS) $(PTHREAD_CFLAGS)" \ + LDFLAGS="$(LDFLAGS)" LIBS="$(PTHREAD_LIBS) $(LIBS)" \ + BUILDDIR="$(abs_top_builddir)" $(RUNTESTFLAGS); \ + else echo "WARNING: could not find \`runtest'" 1>&2; :;\ + fi + +development.exp: $(BFDDIR)/development.sh + $(EGREP) "(development|experimental)=" $(BFDDIR)/development.sh \ + | $(AWK) -F= '{ print "set " $$1 " " $$2 }' > $@ + +# development.sh is used to determine -Werror default. +CONFIG_STATUS_DEPENDENCIES = $(BFDDIR)/development.sh + +EXTRA_DEJAGNU_SITE_CONFIG = development.exp + +DISTCLEANFILES = site.exp development.exp +endif + diff --git a/gprofng/Makefile.in b/gprofng/Makefile.in new file mode 100644 index 0000000..08ffdd2 --- /dev/null +++ b/gprofng/Makefile.in @@ -0,0 +1,940 @@ +# Makefile.in generated by automake 1.15.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2017 Free Software Foundation, Inc. + +# This Makefile.in 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. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/../libtool.m4 \ + $(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \ + $(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/../config/warnings.m4 \ + $(top_srcdir)/../config/enable.m4 \ + $(top_srcdir)/../config/ax_pthread.m4 \ + $(top_srcdir)/config/bison.m4 $(top_srcdir)/../bfd/version.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(am__DIST_COMMON) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope distdir dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +DEJATOOL = $(PACKAGE) +RUNTESTDEFAULTFLAGS = --tool $$tool --srcdir $$srcdir +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/../ar-lib \ + $(top_srcdir)/../compile $(top_srcdir)/../config.guess \ + $(top_srcdir)/../config.sub $(top_srcdir)/../install-sh \ + $(top_srcdir)/../ltmain.sh $(top_srcdir)/../missing \ + $(top_srcdir)/../mkinstalldirs \ + $(top_srcdir)/common/config.h.in README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_SUBDIRS = @BUILD_SUBDIRS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ + +# Setup the testing framework, if you have one +EXPECT = expect +FGREP = @FGREP@ +GPROFNG_CFLAGS = @GPROFNG_CFLAGS@ +GPROFNG_CPPFLAGS = @GPROFNG_CPPFLAGS@ +GPROFNG_LIBADD = @GPROFNG_LIBADD@ +GPROFNG_LIBDIR = @GPROFNG_LIBDIR@ +GREP = @GREP@ +HELP2MAN = @HELP2MAN@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JAVA = @JAVA@ +JAVAC = @JAVAC@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LD_NO_AS_NEEDED = @LD_NO_AS_NEEDED@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WERROR = @WERROR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +gprofng_cflags = @gprofng_cflags@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +jdk_inc = @jdk_inc@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I . -I .. +AUTOMAKE_OPTIONS = dejagnu foreign +@BUILD_COLLECTOR_TRUE@COLLECTOR_SUBDIRS = libcollector +@BUILD_SRC_TRUE@SRC_SUBDIRS = src gp-display-html doc +SUBDIRS = $(COLLECTOR_SUBDIRS) $(SRC_SUBDIRS) +DIST_SUBDIRS = libcollector src gp-display-html doc +RUNTEST = runtest +RUNTESTFLAGS = +BASEDIR = $(srcdir)/.. +BFDDIR = $(BASEDIR)/bfd +AM_MAKEFLAGS = \ + jdk_inc="$(jdk_inc)" \ + LD_NO_AS_NEEDED="$(LD_NO_AS_NEEDED)" \ + GPROFNG_CFLAGS="$(GPROFNG_CFLAGS)" \ + GPROFNG_CPPFLAGS="$(GPROFNG_CPPFLAGS)" \ + GPROFNG_LIBDIR="$(GPROFNG_LIBDIR)" + + +# development.sh is used to determine -Werror default. +@TCL_TRY_TRUE@CONFIG_STATUS_DEPENDENCIES = $(BFDDIR)/development.sh +@TCL_TRY_TRUE@EXTRA_DEJAGNU_SITE_CONFIG = development.exp +@TCL_TRY_TRUE@DISTCLEANFILES = site.exp development.exp +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(top_srcdir)/common/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(top_srcdir)/common/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool config.lt + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +@TCL_TRY_FALSE@check-DEJAGNU: site.exp +@TCL_TRY_FALSE@ srcdir='$(srcdir)'; export srcdir; \ +@TCL_TRY_FALSE@ EXPECT=$(EXPECT); export EXPECT; \ +@TCL_TRY_FALSE@ if $(SHELL) -c "$(RUNTEST) --version" > /dev/null 2>&1; then \ +@TCL_TRY_FALSE@ exit_status=0; l='$(DEJATOOL)'; for tool in $$l; do \ +@TCL_TRY_FALSE@ if $(RUNTEST) $(AM_RUNTESTFLAGS) $(RUNTESTDEFAULTFLAGS) $(RUNTESTFLAGS); \ +@TCL_TRY_FALSE@ then :; else exit_status=1; fi; \ +@TCL_TRY_FALSE@ done; \ +@TCL_TRY_FALSE@ else echo "WARNING: could not find '$(RUNTEST)'" 1>&2; :;\ +@TCL_TRY_FALSE@ fi; \ +@TCL_TRY_FALSE@ exit $$exit_status +site.exp: Makefile $(EXTRA_DEJAGNU_SITE_CONFIG) + @echo 'Making a new site.exp file ...' + @echo '## these variables are automatically generated by make ##' >site.tmp + @echo '# Do not edit here. If you wish to override these values' >>site.tmp + @echo '# edit the last section' >>site.tmp + @echo 'set srcdir "$(srcdir)"' >>site.tmp + @echo "set objdir `pwd`" >>site.tmp + @echo 'set build_alias "$(build_alias)"' >>site.tmp + @echo 'set build_triplet $(build_triplet)' >>site.tmp + @echo 'set host_alias "$(host_alias)"' >>site.tmp + @echo 'set host_triplet $(host_triplet)' >>site.tmp + @list='$(EXTRA_DEJAGNU_SITE_CONFIG)'; for f in $$list; do \ + echo "## Begin content included from file $$f. Do not modify. ##" \ + && cat `test -f "$$f" || echo '$(srcdir)/'`$$f \ + && echo "## End content included from file $$f. ##" \ + || exit 1; \ + done >> site.tmp + @echo "## End of auto-generated content; you can edit from here. ##" >> site.tmp + @if test -f site.exp; then \ + sed -e '1,/^## End of auto-generated content.*##/d' site.exp >> site.tmp; \ + fi + @-rm -f site.bak + @test ! -f site.exp || mv site.exp site.bak + @mv site.tmp site.exp + +distclean-DEJAGNU: + -rm -f site.exp site.bak + -l='$(DEJATOOL)'; for tool in $$l; do \ + rm -f $$tool.sum $$tool.log; \ + done + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-DEJAGNU +check: check-recursive +all-am: Makefile config.h +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-DEJAGNU distclean-generic \ + distclean-hdr distclean-libtool distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) all check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-DEJAGNU check-am clean clean-cscope \ + clean-generic clean-libtool cscope cscopelist-am ctags \ + ctags-am dist dist-all dist-bzip2 dist-gzip dist-lzip \ + dist-shar dist-tarZ dist-xz dist-zip distcheck distclean \ + distclean-DEJAGNU distclean-generic distclean-hdr \ + distclean-libtool distclean-tags distcleancheck distdir \ + distuninstallcheck dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +@TCL_TRY_TRUE@check-DEJAGNU: site.exp development.exp +@TCL_TRY_TRUE@ srcroot=`cd $(srcdir) && pwd`; export srcroot; \ +@TCL_TRY_TRUE@ r=`pwd`; export r; \ +@TCL_TRY_TRUE@ LC_ALL=C; export LC_ALL; \ +@TCL_TRY_TRUE@ EXPECT=$(EXPECT); export EXPECT; \ +@TCL_TRY_TRUE@ jdk_inc="$(jdk_inc)"; export jdk_inc; \ +@TCL_TRY_TRUE@ runtest=$(RUNTEST); \ +@TCL_TRY_TRUE@ if $(SHELL) -c "$$runtest --version" > /dev/null 2>&1; then \ +@TCL_TRY_TRUE@ $$runtest --tool $(DEJATOOL) --srcdir $${srcroot}/testsuite \ +@TCL_TRY_TRUE@ MAKE="$(MAKE)" CC="$(CC)" CFLAGS="$(CFLAGS) $(PTHREAD_CFLAGS)" \ +@TCL_TRY_TRUE@ LDFLAGS="$(LDFLAGS)" LIBS="$(PTHREAD_LIBS) $(LIBS)" \ +@TCL_TRY_TRUE@ BUILDDIR="$(abs_top_builddir)" $(RUNTESTFLAGS); \ +@TCL_TRY_TRUE@ else echo "WARNING: could not find \`runtest'" 1>&2; :;\ +@TCL_TRY_TRUE@ fi + +@TCL_TRY_TRUE@development.exp: $(BFDDIR)/development.sh +@TCL_TRY_TRUE@ $(EGREP) "(development|experimental)=" $(BFDDIR)/development.sh \ +@TCL_TRY_TRUE@ | $(AWK) -F= '{ print "set " $$1 " " $$2 }' > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gprofng/README b/gprofng/README new file mode 100644 index 0000000..66d2e7c --- /dev/null +++ b/gprofng/README @@ -0,0 +1,100 @@ +What is gprofng? + + Gprofng is the GNU Next Generation profiler for analyzing the performance + of Linux applications. Gprofng allows you to: + - Profile C / C++ / Java / Scala applications without needing to recompile + - Profile multi-threaded applications + - Analyze and compare multiple experiments + - Use time-based sampling and / or hardware event counters + +Building gprofng + + Gprofng is distributed with binutils. To build gprofng, you build binutils. + Overview: + 1. Set paths + 2. Verify prerequisites + 3. Git clone + 4. Configure, make, and make install + Details follow for each of these. + +1. Set paths + + If you are configuring binutils for the default location, it will use: + /usr/local + In your shell initialization procedure, set your paths using commands + similar to these: + export PATH=/usr/local/bin:$PATH + export MANPATH=/usr/local/share/man:$MANPATH + export INFOPATH=/usr/local/share/info/:$INFOPATH + +2. Verify prerequisites + + To build a recent version of binutils, it is useful to have a developer + system with the most recent compilers, libraries, and operating system. + Development systems will typically already include most of these: + + bison bison-devel bzip2 elfutils-debuginfod-client-devel + expat-devel flex gcc gcc-c++ git-core git-core-doc gmp-devel + help2man libbabeltrace-devel libipt-devel m4 make mpfr-devel + ncurses-devel perl-Data-Dumper tar texinfo xz zlib-devel + java-17-openjdk-devel + + CAUTION: The list of prerequisites changes depending on your operating system + and changes as binutils evolves. The list above is a snapshot of the useful + packages in early 2022 for Red Hat Enterprise Linux and Oracle Linux. + + Your system may use other packages; for example, you may be able to use a + different version of Java than shown above. If there are failures, you may + need to search for other packages as described in the "Hints" section below. + +3. Git clone + + Select a binutils repository and a branch that you would like + to start from. For example, to clone from the master at + sourceware.org, you could say: + git clone http://sourceware.org/git/binutils-gdb.git CloneDir + +4. Configure, make, and install + + There are many options for configure (see: configure --help). For example, + --prefix sets the destination, as described in the "Hints" section below. + If the default destination /usr/local is acceptable for your needs, then + after the clone operation finishes, you can simply say: + + mkdir build + cd build + ../CloneDir/configure + make + sudo make install + +Getting started + + To start using gprofng, see the tutorial available by saying: + info gprofng + +Hints and tips for building binutils + + - Use the script(1) command to write a log of your build. + + - If you run multiple commands at once (for example: make --jobs=10) then you + should also use make option: + --output-sync + Without --output-sync, the log would be difficult to interpret. + + - Search the log for errors and warnings, for example: + configure: WARNING: <package> is missing or unusable; some features + may be unavailable. + The above message suggests that <package> may be needed on your system. + + - Sometimes the above message is not sufficiently specific to guide you to + the right package. In the directory where the failure happens, config.log + may identify a specific missing file, and your package manager may allow + you to search for it. For example, if build/gprofng/config.log shows that + javac is missing, and if your package manager is dnf, you could try: + dnf --repo='*' whatprovides '*/javac' + + - You can set a custom destination directory using configure --prefix. + This is useful if you prefer not to change /usr/local, or if you are not + allowed to do so. If you set a custom prefix, be sure to change all three + paths mentioned in the PATH section above. + diff --git a/gprofng/acinclude.m4 b/gprofng/acinclude.m4 new file mode 100644 index 0000000..966da18 --- /dev/null +++ b/gprofng/acinclude.m4 @@ -0,0 +1,4 @@ +m4_include([../config/warnings.m4]) +m4_include([../config/enable.m4]) +m4_include([../config/ax_pthread.m4]) +m4_include([config/bison.m4]) diff --git a/gprofng/aclocal.m4 b/gprofng/aclocal.m4 new file mode 100644 index 0000000..02b07b9 --- /dev/null +++ b/gprofng/aclocal.m4 @@ -0,0 +1,1254 @@ +# generated automatically by aclocal 1.15.1 -*- Autoconf -*- + +# Copyright (C) 1996-2017 Free Software Foundation, Inc. + +# 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. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# Copyright (C) 2002-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.15' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.15.1], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.15.1])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# Copyright (C) 2011-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_PROG_AR([ACT-IF-FAIL]) +# ------------------------- +# Try to determine the archiver interface, and trigger the ar-lib wrapper +# if it is needed. If the detection of archiver interface fails, run +# ACT-IF-FAIL (default is to abort configure with a proper error message). +AC_DEFUN([AM_PROG_AR], +[AC_BEFORE([$0], [LT_INIT])dnl +AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl +AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([ar-lib])dnl +AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false]) +: ${AR=ar} + +AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface], + [AC_LANG_PUSH([C]) + am_cv_ar_interface=ar + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])], + [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([am_ar_try]) + if test "$ac_status" -eq 0; then + am_cv_ar_interface=ar + else + am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([am_ar_try]) + if test "$ac_status" -eq 0; then + am_cv_ar_interface=lib + else + am_cv_ar_interface=unknown + fi + fi + rm -f conftest.lib libconftest.a + ]) + AC_LANG_POP([C])]) + +case $am_cv_ar_interface in +ar) + ;; +lib) + # Microsoft lib, so override with the ar-lib wrapper script. + # FIXME: It is wrong to rewrite AR. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__AR in this case, + # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something + # similar. + AR="$am_aux_dir/ar-lib $AR" + ;; +unknown) + m4_default([$1], + [AC_MSG_ERROR([could not determine $AR interface])]) + ;; +esac +AC_SUBST([AR])dnl +]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# +# 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. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# +# 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. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# +# 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. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542> + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: <http://www.gnu.org/software/coreutils/>. + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2017 Free Software Foundation, Inc. +# +# 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. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_MAINTAINER_MODE([DEFAULT-MODE]) +# ---------------------------------- +# Control maintainer-specific portions of Makefiles. +# Default is to disable them, unless 'enable' is passed literally. +# For symmetry, 'disable' may be passed as well. Anyway, the user +# can override the default with the --enable/--disable switch. +AC_DEFUN([AM_MAINTAINER_MODE], +[m4_case(m4_default([$1], [disable]), + [enable], [m4_define([am_maintainer_other], [disable])], + [disable], [m4_define([am_maintainer_other], [enable])], + [m4_define([am_maintainer_other], [enable]) + m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) +AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode's default is 'disable' unless 'enable' is passed + AC_ARG_ENABLE([maintainer-mode], + [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], + am_maintainer_other[ make rules and dependencies not useful + (and sometimes confusing) to the casual installer])], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST([MAINT])dnl +] +) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# +# 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. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2017 Free Software Foundation, Inc. +# +# 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. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2017 Free Software Foundation, Inc. +# +# 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. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar <conftest.tar]) + AM_RUN_LOG([cat conftest.dir/file]) + grep GrepMe conftest.dir/file >/dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([../libtool.m4]) +m4_include([../ltoptions.m4]) +m4_include([../ltsugar.m4]) +m4_include([../ltversion.m4]) +m4_include([../lt~obsolete.m4]) +m4_include([acinclude.m4]) diff --git a/gprofng/common/cc_libcollector.h b/gprofng/common/cc_libcollector.h new file mode 100644 index 0000000..e078541 --- /dev/null +++ b/gprofng/common/cc_libcollector.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * This file describes the enum's, etc. shared by the collector control + * class and libcollector and its modules. It is #included in collctrl.h + * so any changes to it should follow the procedure described there. + */ + +#ifndef _CC_LIBCOLLECTOR_H +#define _CC_LIBCOLLECTOR_H + +/* definitions for synchronization tracing scope -- a bit mask */ +#define SYNCSCOPE_NATIVE 0x1 +#define SYNCSCOPE_JAVA 0x2 + +typedef enum +{ + FOLLOW_NONE = 0x0, + FOLLOW_EXEC = 0x1, + FOLLOW_FORK = 0x2, + FOLLOW_ON = 0x3, + FOLLOW_COMBO = 0x4, + FOLLOW_ALL = 0x7 +} Follow_type; + +#endif /* !__CC_LIBCOLLECTOR_H */ diff --git a/gprofng/common/config.h.in b/gprofng/common/config.h.in new file mode 100644 index 0000000..e46e64f --- /dev/null +++ b/gprofng/common/config.h.in @@ -0,0 +1,117 @@ +/* common/config.h.in. Generated from configure.ac by autoheader. */ + +/* Enable debugging output. */ +#undef DEBUG + +/* Enable java profiling */ +#undef GPROFNG_JAVA_PROFILING + +/* Define to 1 if you have the declaration of `basename', and to 0 if you + don't. */ +#undef HAVE_DECL_BASENAME + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Have PTHREAD_PRIO_INHERIT. */ +#undef HAVE_PTHREAD_PRIO_INHERIT + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strsignal' function. */ +#undef HAVE_STRSIGNAL + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Version number of package */ +#undef VERSION + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE diff --git a/gprofng/common/core_pcbe.c b/gprofng/common/core_pcbe.c new file mode 100644 index 0000000..6f746d8 --- /dev/null +++ b/gprofng/common/core_pcbe.c @@ -0,0 +1,3023 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Performance Counter Back-End for Intel Family 6 + * Models 15(06_0FH) 23(06_17H) (Core 2) + * Models 28(06_1CH) (Atom) + * Models 37(06_25H) 44(06_2CH) (Westmere) + * Models 26(06_1AH) 30(06_1EH) 31(06_1FH) 46(06_2EH) (Nehalem) + * Models 42(06_2AH) 45(06_2DH) (Sandy Bridge) + * Models 58(06_3AH) 62(06_3EH) (Ivy Bridge) + * Models 60(06_3CH) 63(06_3FH) 69(06_45H) 70(06_46H) (Haswell) + * Models 61(06_3DH) 71(06_47H) 79(06_4FH) 86(06_??H) (Broadwell) (79 not listed in Intel SDM as of June 2015) + * Models 78(06_4EH) 85(06_55H) 94(06_5EH) (Skylake) (Note Skylake and later: versionID==4) + * To add another model number: + * - add appropriate table data in the form + * #define EVENTS_FAM6_MODXX + * - add appropriate table definitions in the form + * const struct events_table_t events_fam6_modXX[] = + * - set events_table to the appropriate table + * using the "switch ( cpuid_getmodel(CPU) )" statement + * in core_pcbe_init() + * - check the date in core_pcbe_cpuref() + * Table data can be derived from: + * - the Intel SDM + * also https://download.01.org/perfmon/ + * - libcpc source code in usr/src/uts/intel/pcbe/ + * - libpfm4 + * but there are typically inconsistencies among these + * sources of data. So, judgment is required. + * Other things to do to add a new processor: + * x file hwc_cpus.h + * add a cpuver enumerator + * add lookup entry + * x file hwctable.c + * add a table (aliases, etc.) + * add a cputabs entry, including default metrics + * look for other places where the most-recently-added CPU is mentioned + * x file cpu_frequency.h + * function get_max_turbo_freq() + * go to "switch (model)", and add turbo boosts + */ + +#include <sys/types.h> +#include "hwcdrv.h" + +static uint64_t num_gpc; /* number of general purpose counters (e.g. 2-4) */ +static uint64_t num_ffc; /* number fixed function counters (e.g. 3) */ +static uint_t total_pmc; /* num_gpc + num_ffc */ + +/* + * Only the lower 32-bits can be written to in the general-purpose + * counters. The higher bits are extended from bit 31; all ones if + * bit 31 is one and all zeros otherwise. + * + * The fixed-function counters do not have this restriction. + */ + +static const char *ffc_names[] = { +/* + * While modern Intel processors have fixed-function counters (FFCs), + * on Linux we access HWCs through the perf_event_open() kernel interface, + * which does not allow us direct access to the FFCs. + * Rather, the Linux kernel manages registers opaquely. + * At best, it allows us extra HW events by off-loading + * HWCs to FFCs as available. Often, however, the FFCs + * are commandeered by other activities like the NMI watchdog. + * We will omit any explicit reference to them. + * https://lists.eecs.utk.edu/pipermail/perfapi-devel/2015-February/006895.html + * See also bug 21315497. + */ +#if 0 + "instr_retired.any", + "cpu_clk_unhalted.core", + "cpu_clk_unhalted.ref", +#endif + NULL +}; + +#define IMPL_NAME_LEN 100 +static char core_impl_name[IMPL_NAME_LEN]; + +/* + * Most events require only an event code and a umask. + * Some also require attributes, cmasks, or MSR programming. + * Until Sandy Bridge, the number of these other events + * was small and libcpc just ignored them. + * With Sandy Bridge, libcpc added for support for these + * additional events. + * + * We use an expanded events_table_t here -- patterned + * after snb_pcbe_events_table_t in libcpc's + * usr/src/uts/intel/pcbe/snb_pcbe.h -- for all processors. + * + * Correspondingly, we also define ATTR_* macros, but we + * define them to set bits as they will appear + * in bits 16-23 of the final eventsel. Definitions of those + * bits can be found in "struct ia32_perfevtsel" in libcpc's + * usr/src/uts/intel/pcbe/intel_pcbe_utils.h . + * + * For now, I don't know how to handle msr_offset. + * So, let's not include events that call for it. + * + * For now, don't do anything with ATTR_PEBS other than + * to note it in tables (starting with Haswell). + * + * Solaris tables also have ATTR_PEBS_ONLY. We cannot + * use these counters from "collect -h" and so do not + * include them. + */ +#define ATTR_NONE 0 +#define ATTR_EDGE (1 << 2) /* bit 18 - offset 16 */ +#define ATTR_ANY (1 << 5) /* bit 21 - offset 16 */ +#define ATTR_INV (1 << 7) /* bit 23 - offset 16 */ +#define ATTR_PEBS ATTR_NONE // PEBS not supported +#define ATTR_TSX ATTR_NONE // TSX MSRs not supported +#undef ATTR_PEBS_ONLY // PEBS-only event, not supported +#undef ATTR_PEBS_ONLY_LD_LAT // not supported + +struct events_table_t +{ + uint32_t eventselect; + uint32_t unitmask; + uint64_t supported_counters; + const char *name; + uint8_t cmask; + uint8_t attrs; + uint16_t msr_offset; +}; + +/* Used to describe which counters support an event */ +#define C(x) (1 << (x)) +#define C0 C(0) +#define C1 C(1) +#define C2 C(2) +#define C3 C(3) +#define C_ALL 0xFFFFFFFFFFFFFFFF +#define CDEAD 0 /* Counter that is broken */ + +/* note that regular events use the original spelling like "inst_retired.any_p" */ +#define ARCH_EVENTS /* NOTE: Order specified in PRM must be maintained! */ \ +{ 0x3C, 0x00, C_ALL, "unhalted-core-cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x01, C_ALL, "unhalted-reference-cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x00, C_ALL, "instruction-retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x4F, C_ALL, "llc-reference" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x41, C_ALL, "llc-misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x00, C_ALL, "branch-instruction-retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x00, C_ALL, "branch-misses-retired" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +/* + * FAM6/MOD15: + * Xeon 3000, 3200, 5100, 5300, 7300 + * Core 2 Quad, Extreme, and Duo + * Pentium dual-core processors + * FAM6/MOD23: + * Xeon 5200, 5400 series, Intel + * Core 2 Quad Q9650. + */ +#define EVENTS_FAM6_MOD23 \ +{ 0x03, 0x00, C0|C1, "load_block" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x02, C0|C1, "load_block.sta" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x04, C0|C1, "load_block.std" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x08, C0|C1, "load_block.overlap_store" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x10, C0|C1, "load_block.until_retire" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x20, C0|C1, "load_block.l1d" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x04, 0x00, C0|C1, "store_block" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x04, 0x01, C0|C1, "store_block.drain_cycles" /*spell-diff*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x04, 0x02, C0|C1, "store_block.order" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x04, 0x08, C0|C1, "store_block.snoop" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x05, 0x00, C0|C1, "misalign_mem_ref" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x06, 0x00, C0|C1, "segment_reg_loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x00, C0|C1, "sse_pre_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x00, C0|C1, "sse_pre_exec.nta" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x01, C0|C1, "sse_pre_exec.l1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x02, C0|C1, "sse_pre_exec.l2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x03, C0|C1, "sse_pre_exec.stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x00, C0|C1, "dtlb_misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x01, C0|C1, "dtlb_misses.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x02, C0|C1, "dtlb_misses.miss_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x04, C0|C1, "dtlb_misses.l0_miss_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x08, C0|C1, "dtlb_misses.miss_st" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x09, 0x00, C0|C1, "memory_disambiguation" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x09, 0x01, C0|C1, "memory_disambiguation.reset" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x09, 0x02, C0|C1, "memory_disambiguation.success" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0c, 0x00, C0|C1, "page_walks" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0c, 0x01, C0|C1, "page_walks.count" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0c, 0x02, C0|C1, "page_walks.cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x00, C0 , "fp_comp_ops_exe" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x11, 0x00, C1, "fp_assist" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x00, C1, "mul" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x00, C1, "div" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x00, C0 , "cycles_div_busy" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x18, 0x00, C0 , "idle_during_div" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x19, 0x00, C1, "delayed_bypass" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x19, 0x00, C1, "delayed_bypass.fp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x19, 0x01, C1, "delayed_bypass.simd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x19, 0x02, C1, "delayed_bypass.load" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x21, 0x00, C0|C1, "l2_ads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x23, 0x00, C0|C1, "l2_dbus_busy_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x00, C0|C1, "l2_lines_in" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x25, 0x00, C0|C1, "l2_m_lines_in" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x00, C0|C1, "l2_lines_out" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x00, C0|C1, "l2_m_lines_out" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x00, C0|C1, "l2_ifetch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x29, 0x00, C0|C1, "l2_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2a, 0x00, C0|C1, "l2_st" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2b, 0x00, C0|C1, "l2_lock" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2e, 0x00, C0|C1, "l2_rqsts" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2e, 0x41, C0|C1, "l2_rqsts.self.demand.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2e, 0x4f, C0|C1, "l2_rqsts.self.demand.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x30, 0x00, C0|C1, "l2_reject_busq" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x32, 0x00, C0|C1, "l2_no_req" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3a, 0x00, C0|C1, "eist_trans" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3b, 0xc0, C0|C1, "thermal_trip" /*non-zero umask*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3c, 0x00, C0|C1, "cpu_clk_unhalted" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3c, 0x00, C0|C1, "cpu_clk_unhalted.core_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3c, 0x01, C0|C1, "cpu_clk_unhalted.bus" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3c, 0x02, C0|C1, "cpu_clk_unhalted.no_other" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x40, 0x00, C0|C1, "l1d_cache_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x41, 0x00, C0|C1, "l1d_cache_st" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x42, 0x00, C0|C1, "l1d_cache_lock" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x42, 0x10, C0|C1, "l1d_cache_lock.duration" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x43, 0x00, C0|C1, "l1d_all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x43, 0x00, C0|C1, "l1d_all_ref" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x43, 0x01, C0|C1, "l1d_all.ref" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x43, 0x02, C0|C1, "l1d_all.cache_ref" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x45, 0x0f, C0|C1, "l1d_repl" /*non-zero umask*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x46, 0x00, C0|C1, "l1d_m_repl" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x47, 0x00, C0|C1, "l1d_m_evict" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x00, C0|C1, "l1d_pend_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x00, C0|C1, "l1d_split" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x01, C0|C1, "l1d_split.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x02, C0|C1, "l1d_split.stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4b, 0x00, C0|C1, "sse_pre_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4b, 0x00, C0|C1, "sse_pre_miss.nta" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4b, 0x01, C0|C1, "sse_pre_miss.l1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4b, 0x02, C0|C1, "sse_pre_miss.l2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4c, 0x00, C0|C1, "load_hit_pre" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4e, 0x00, C0|C1, "l1d_prefetch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4e, 0x10, C0|C1, "l1d_prefetch.requests" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x00, C0|C1, "bus_request_outstanding" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x61, 0x00, C0|C1, "bus_bnr_drv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x62, 0x00, C0|C1, "bus_drdy_clocks" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x00, C0|C1, "bus_lock_clocks" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x64, 0x00, C0|C1, "bus_data_rcv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x65, 0x00, C0|C1, "bus_trans_brd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x66, 0x00, C0|C1, "bus_trans_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x67, 0x00, C0|C1, "bus_trans_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x68, 0x00, C0|C1, "bus_trans_ifetch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x69, 0x00, C0|C1, "bus_trans_inval" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6a, 0x00, C0|C1, "bus_trans_pwr" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6b, 0x00, C0|C1, "bus_trans_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6c, 0x00, C0|C1, "bus_trans_io" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6d, 0x00, C0|C1, "bus_trans_def" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6e, 0x00, C0|C1, "bus_trans_burst" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6f, 0x00, C0|C1, "bus_trans_mem" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x70, 0x00, C0|C1, "bus_trans_any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x77, 0x00, C0|C1, "ext_snoop" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x78, 0x00, C0|C1, "cmp_snoop" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7a, 0x00, C0|C1, "bus_hit_drv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7b, 0x00, C0|C1, "bus_hitm_drv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7d, 0x00, C0|C1, "busq_empty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7e, 0x00, C0|C1, "snoop_stall_drv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7f, 0x00, C0|C1, "bus_io_wait" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x00, C0|C1, "l1i_reads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x81, 0x00, C0|C1, "l1i_misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x82, 0x00, C0|C1, "itlb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x82, 0x02, C0|C1, "itlb.small_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x82, 0x10, C0|C1, "itlb.large_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x82, 0x12, C0|C1, "itlb.misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x82, 0x40, C0|C1, "itlb.flush" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x83, 0x00, C0|C1, "inst_queue" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x83, 0x02, C0|C1, "inst_queue.full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x86, 0x00, C0|C1, "cycles_l1i_mem_stalled" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x00, C0|C1, "ild_stall" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x00, C0|C1, "br_inst_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x00, C0|C1, "br_missp_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x8a, 0x00, C0|C1, "br_bac_missp_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x8b, 0x00, C0|C1, "br_cnd_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x8c, 0x00, C0|C1, "br_cnd_missp_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x8d, 0x00, C0|C1, "br_ind_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x8e, 0x00, C0|C1, "br_ind_missp_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x8f, 0x00, C0|C1, "br_ret_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x90, 0x00, C0|C1, "br_ret_missp_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x91, 0x00, C0|C1, "br_ret_bac_missp_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x92, 0x00, C0|C1, "br_call_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x93, 0x00, C0|C1, "br_call_missp_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x94, 0x00, C0|C1, "br_ind_call_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x97, 0x00, C0|C1, "br_tkn_bubble_1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x98, 0x00, C0|C1, "br_tkn_bubble_2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xa0, 0x00, C0|C1, "rs_uops_dispatched" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xa1, 0x00, C0 , "rs_uops_dispatched_port" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xa1, 0x01, C0 , "rs_uops_dispatched_port.0" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xa1, 0x02, C0 , "rs_uops_dispatched_port.1" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xa1, 0x04, C0 , "rs_uops_dispatched_port.2" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xa1, 0x08, C0 , "rs_uops_dispatched_port.3" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xa1, 0x10, C0 , "rs_uops_dispatched_port.4" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xa1, 0x20, C0 , "rs_uops_dispatched_port.5" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xaa, 0x00, C0|C1, "macro_insts" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xaa, 0x01, C0|C1, "macro_insts.decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xaa, 0x08, C0|C1, "macro_insts.cisc_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xab, 0x00, C0|C1, "esp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xab, 0x01, C0|C1, "esp.synch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xab, 0x02, C0|C1, "esp.additions" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb0, 0x00, C0|C1, "simd_uops_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb1, 0x00, C0|C1, "simd_sat_uop_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x00, C0|C1, "simd_uop_type_exec" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x01, C0|C1, "simd_uop_type_exec.mul" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x02, C0|C1, "simd_uop_type_exec.shift" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x04, C0|C1, "simd_uop_type_exec.pack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x08, C0|C1, "simd_uop_type_exec.unpack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x10, C0|C1, "simd_uop_type_exec.logical" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x20, C0|C1, "simd_uop_type_exec.arithmetic" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc0, 0x00, C0|C1, "inst_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc0, 0x00, C0|C1, "inst_retired.any_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc0, 0x01, C0|C1, "inst_retired.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc0, 0x02, C0|C1, "inst_retired.stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc0, 0x04, C0|C1, "inst_retired.other" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc0, 0x08, C0|C1, "inst_retired.vm_h" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc1, 0x00, C0|C1, "x87_ops_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc1, 0x01, C0|C1, "x87_ops_retired.fxch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc1, 0xfe, C0|C1, "x87_ops_retired.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc2, 0x00, C0|C1, "uops_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc2, 0x01, C0|C1, "uops_retired.ld_ind_br" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc2, 0x02, C0|C1, "uops_retired.std_sta" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc2, 0x04, C0|C1, "uops_retired.macro_fusion" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc2, 0x07, C0|C1, "uops_retired.fused" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc2, 0x08, C0|C1, "uops_retired.non_fused" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc2, 0x0f, C0|C1, "uops_retired.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc3, 0x00, C0|C1, "machine_nukes" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc3, 0x01, C0|C1, "machine_nukes.smc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc3, 0x04, C0|C1, "machine_nukes.mem_order" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x00, C0|C1, "br_inst_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x00, C0|C1, "br_inst_retired.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x01, C0|C1, "br_inst_retired.pred_not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x02, C0|C1, "br_inst_retired.mispred_not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x04, C0|C1, "br_inst_retired.pred_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x08, C0|C1, "br_inst_retired.mispred_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x0c, C0|C1, "br_inst_retired.taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc5, 0x00, C0|C1, "br_inst_retired_mispred" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc5, 0x00, C0|C1, "br_inst_retired.mispred" /*alt-spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc6, 0x00, C0|C1, "cycles_int" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc6, 0x01, C0|C1, "cycles_int.masked" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc6, 0x02, C0|C1, "cycles_int.pending_and_masked" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x00, C0|C1, "simd_inst_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x01, C0|C1, "simd_inst_retired.packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x02, C0|C1, "simd_inst_retired.scalar_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x04, C0|C1, "simd_inst_retired.packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x08, C0|C1, "simd_inst_retired.scalar_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x10, C0|C1, "simd_inst_retired.vector" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x1f, C0|C1, "simd_inst_retired.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc8, 0x00, C0|C1, "hw_int_rcv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc9, 0x00, C0|C1, "itlb_miss_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xca, 0x00, C0|C1, "simd_comp_inst_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xca, 0x01, C0|C1, "simd_comp_inst_retired.packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xca, 0x02, C0|C1, "simd_comp_inst_retired.scalar_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xca, 0x04, C0|C1, "simd_comp_inst_retired.packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xca, 0x08, C0|C1, "simd_comp_inst_retired.scalar_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcb, 0x00, C0 , "mem_load_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcb, 0x01, C0 , "mem_load_retired.l1d_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcb, 0x02, C0 , "mem_load_retired.l1d_line_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcb, 0x04, C0 , "mem_load_retired.l2_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcb, 0x08, C0 , "mem_load_retired.l2_line_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcb, 0x10, C0 , "mem_load_retired.dtlb_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcc, 0x00, C0|C1, "fp_mmx_trans" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcc, 0x01, C0|C1, "fp_mmx_trans.to_mmx" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcc, 0x02, C0|C1, "fp_mmx_trans.to_fp" /*spelling*/ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcd, 0x00, C0|C1, "simd_assist" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xce, 0x00, C0|C1, "simd_instr_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcf, 0x00, C0|C1, "simd_sat_instr_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd2, 0x00, C0|C1, "rat_stalls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd2, 0x01, C0|C1, "rat_stalls.rob_read_port" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd2, 0x02, C0|C1, "rat_stalls.partial_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd2, 0x04, C0|C1, "rat_stalls.flags" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd2, 0x08, C0|C1, "rat_stalls.fpsw" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd2, 0x0f, C0|C1, "rat_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd2, 0x10, C0|C1, "rat_stalls.other_serialization_stalls", 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd4, 0x00, C0|C1, "seg_rename_stalls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd4, 0x01, C0|C1, "seg_rename_stalls.es" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd4, 0x02, C0|C1, "seg_rename_stalls.ds" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd4, 0x04, C0|C1, "seg_rename_stalls.fs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd4, 0x08, C0|C1, "seg_rename_stalls.gs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd4, 0x0f, C0|C1, "seg_rename_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd5, 0x00, C0|C1, "seg_reg_renames" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd5, 0x01, C0|C1, "seg_reg_renames.es" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd5, 0x02, C0|C1, "seg_reg_renames.ds" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd5, 0x04, C0|C1, "seg_reg_renames.fs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd5, 0x08, C0|C1, "seg_reg_renames.gs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xd5, 0x0f, C0|C1, "seg_reg_renames.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xdc, 0x00, C0|C1, "resource_stalls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xdc, 0x01, C0|C1, "resource_stalls.rob_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xdc, 0x02, C0|C1, "resource_stalls.rs_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xdc, 0x04, C0|C1, "resource_stalls.ld_st" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xdc, 0x08, C0|C1, "resource_stalls.fpcw" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xdc, 0x10, C0|C1, "resource_stalls.br_miss_clear" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xdc, 0x1f, C0|C1, "resource_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xe0, 0x00, C0|C1, "br_inst_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xe4, 0x00, C0|C1, "bogus_br" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xe6, 0x00, C0|C1, "baclears" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xf0, 0x00, C0|C1, "pref_rqsts_up" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xf8, 0x00, C0|C1, "pref_rqsts_dn" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +/* FAM6 MOD28: Intel Atom processor */ +#define EVENTS_FAM6_MOD28 \ +{ 0x02, 0x81, C0|C1, "store_forwards.good" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x06, 0x00, C0|C1, "segment_reg_loads.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x01, C0|C1, "prefetch.prefetcht0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x06, C0|C1, "prefetch.sw_l2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x08, C0|C1, "prefetch.prefetchnta" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x05, C0|C1, "data_tlb_misses.dtlb_miss_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x06, C0|C1, "data_tlb_misses.dtlb_miss_st" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x07, C0|C1, "data_tlb_misses.dtlb_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x09, C0|C1, "data_tlb_misses.l0_dtlb_miss_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0c, 0x03, C0|C1, "page_walks.cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x01, C0|C1, "x87_comp_ops_exe.any.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x81, C0|C1, "x87_comp_ops_exe.any.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x11, 0x01, C0|C1, "fp_assist" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x11, 0x81, C0|C1, "fp_assist.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x01, C0|C1, "mul.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x81, C0|C1, "mul.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x01, C0|C1, "div.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x81, C0|C1, "div.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x01, C0|C1, "cycles_div_busy" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x21, 0x00, C0|C1, "l2_ads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x22, 0x00, C0|C1, "l2_dbus_busy" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x00, C0|C1, "l2_lines_in" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x25, 0x00, C0|C1, "l2_m_lines_in" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x00, C0|C1, "l2_lines_out" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x00, C0|C1, "l2_m_lines_out" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x00, C0|C1, "l2_ifetch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x29, 0x00, C0|C1, "l2_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2a, 0x00, C0|C1, "l2_st" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2b, 0x00, C0|C1, "l2_lock" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2e, 0x00, C0|C1, "l2_rqsts" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2e, 0x41, C0|C1, "l2_rqsts.self.demand.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2e, 0x4f, C0|C1, "l2_rqsts.self.demand.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x30, 0x00, C0|C1, "l2_reject_busq" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x32, 0x00, C0|C1, "l2_no_req" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3a, 0x00, C0|C1, "eist_trans" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3b, 0xc0, C0|C1, "thermal_trip" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3c, 0x00, C0|C1, "cpu_clk_unhalted.core_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3c, 0x01, C0|C1, "cpu_clk_unhalted.bus" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3c, 0x02, C0|C1, "cpu_clk_unhalted.no_other" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x40, 0x21, C0|C1, "l1d_cache.ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x40, 0x22, C0|C1, "l1d_cache.st" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x00, C0|C1, "bus_request_outstanding" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x61, 0x00, C0|C1, "bus_bnr_drv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x62, 0x00, C0|C1, "bus_drdy_clocks" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x00, C0|C1, "bus_lock_clocks" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x64, 0x00, C0|C1, "bus_data_rcv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x65, 0x00, C0|C1, "bus_trans_brd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x66, 0x00, C0|C1, "bus_trans_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x67, 0x00, C0|C1, "bus_trans_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x68, 0x00, C0|C1, "bus_trans_ifetch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x69, 0x00, C0|C1, "bus_trans_inval" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6a, 0x00, C0|C1, "bus_trans_pwr" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6b, 0x00, C0|C1, "bus_trans_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6c, 0x00, C0|C1, "bus_trans_io" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6d, 0x00, C0|C1, "bus_trans_def" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6e, 0x00, C0|C1, "bus_trans_burst" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6f, 0x00, C0|C1, "bus_trans_mem" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x70, 0x00, C0|C1, "bus_trans_any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x77, 0x00, C0|C1, "ext_snoop" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7a, 0x00, C0|C1, "bus_hit_drv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7b, 0x00, C0|C1, "bus_hitm_drv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7d, 0x00, C0|C1, "busq_empty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7e, 0x00, C0|C1, "snoop_stall_drv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x7f, 0x00, C0|C1, "bus_io_wait" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x02, C0|C1, "icache.misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x03, C0|C1, "icache.accesses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x82, 0x02, C0|C1, "itlb.misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x82, 0x04, C0|C1, "itlb.flush" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xaa, 0x02, C0|C1, "macro_insts.cisc_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xaa, 0x03, C0|C1, "macro_insts.all_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb0, 0x00, C0|C1, "simd_uops_exec.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb0, 0x80, C0|C1, "simd_uops_exec.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb1, 0x00, C0|C1, "simd_sat_uop_exec.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb1, 0x80, C0|C1, "simd_sat_uop_exec.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x01, C0|C1, "simd_uop_type_exec.mul.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x02, C0|C1, "simd_uop_type_exec.shift.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x04, C0|C1, "simd_uop_type_exec.pack.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x08, C0|C1, "simd_uop_type_exec.unpack.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x10, C0|C1, "simd_uop_type_exec.logical.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x20, C0|C1, "simd_uop_type_exec.arithmetic.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x81, C0|C1, "simd_uop_type_exec.mul.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x82, C0|C1, "simd_uop_type_exec.shift.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x84, C0|C1, "simd_uop_type_exec.pack.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x88, C0|C1, "simd_uop_type_exec.unpack.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0x90, C0|C1, "simd_uop_type_exec.logical.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xb3, 0xa0, C0|C1, "simd_uop_type_exec.arithmetic.ar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc0, 0x00, C0|C1, "inst_retired.any_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc2, 0x10, C0|C1, "uops_retired.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc3, 0x01, C0|C1, "machine_clears.smc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x00, C0|C1, "br_inst_retired.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x01, C0|C1, "br_inst_retired.pred_not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x02, C0|C1, "br_inst_retired.mispred_not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x04, C0|C1, "br_inst_retired.pred_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x08, C0|C1, "br_inst_retired.mispred_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x0a, C0|C1, "br_inst_retired.mispred" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x0c, C0|C1, "br_inst_retired.taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x0f, C0|C1, "br_inst_retired.any1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc5, 0x00, C0|C1, "br_inst_retired.mispred" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc6, 0x01, C0|C1, "cycles_int_masked.cycles_int_masked" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc6, 0x02, C0|C1, "cycles_int_masked.cycles_int_pending_and_masked" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x01, C0|C1, "simd_inst_retired.packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x02, C0|C1, "simd_inst_retired.scalar_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x04, C0|C1, "simd_inst_retired.packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x08, C0|C1, "simd_inst_retired.scalar_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x10, C0|C1, "simd_inst_retired.vector" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc7, 0x1f, C0|C1, "simd_inst_retired.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc8, 0x00, C0|C1, "hw_int_rcv" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xca, 0x01, C0|C1, "simd_comp_inst_retired.packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xca, 0x02, C0|C1, "simd_comp_inst_retired.scalar_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xca, 0x04, C0|C1, "simd_comp_inst_retired.packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xca, 0x08, C0|C1, "simd_comp_inst_retired.scalar_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcb, 0x01, C0|C1, "mem_load_retired.l2_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcb, 0x02, C0|C1, "mem_load_retired.l2_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcb, 0x04, C0|C1, "mem_load_retired.dtlb_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcd, 0x00, C0|C1, "simd_assist" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xce, 0x00, C0|C1, "simd_instr_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xcf, 0x00, C0|C1, "simd_sat_instr_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xe0, 0x01, C0|C1, "br_inst_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xe4, 0x01, C0|C1, "bogus_br" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xe6, 0x01, C0|C1, "baclears.any" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +/* Intel Core i7 (Nehalem) Processor */ +/* + * The Nehalem tables are basically from Bug 16457009 + * libcpc counter names should be based on public Intel documentation -- Nehalem + * and those tables are basically from the + * Intel SDM, January 2013, Section 19.5, Table 19-11. + * We omit the Table 19-12 uncore events. + * + * Note that the table below includes some events from + * the Intel SDM that require cmask or attr settings. + * These events are not in libcpc, which did not include + * events requiring cmask or attr until Sandy Bridge. + */ + +#define EVENTS_FAM6_MOD26 \ +{ 0x04, 0x07, C0|C1|C2|C3, "sb_drain.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x06, 0x04, C0|C1|C2|C3, "store_blocks.at_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x06, 0x08, C0|C1|C2|C3, "store_blocks.l1d_block" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x01, C0|C1|C2|C3, "partial_address_alias" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x01, C0|C1|C2|C3, "dtlb_load_misses.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x02, C0|C1|C2|C3, "dtlb_load_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x10, C0|C1|C2|C3, "dtlb_load_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x20, C0|C1|C2|C3, "dtlb_load_misses.pde_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x80, C0|C1|C2|C3, "dtlb_load_misses.large_walk_completed", 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0B, 0x01, C0|C1|C2|C3, "mem_inst_retired.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0B, 0x02, C0|C1|C2|C3, "mem_inst_retired.stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0B, 0x10, C0|C1|C2|C3, "mem_inst_retired.latency_above_threshold" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0C, 0x01, C0|C1|C2|C3, "mem_store_retired.dtlb_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C0|C1|C2|C3, "uops_issued.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C0|C1|C2|C3, "uops_issued.stalled_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0x0E, 0x02, C0|C1|C2|C3, "uops_issued.fused" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x02, C0|C1|C2|C3, "mem_uncore_retired.other_core_l2_hitm", 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x08, C0|C1|C2|C3, "mem_uncore_retired.remote_cache_local_home_hit", 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x10, C0|C1|C2|C3, "mem_uncore_retired.remote_dram" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x20, C0|C1|C2|C3, "mem_uncore_retired.local_dram" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x01, C0|C1|C2|C3, "fp_comp_ops_exe.x87" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x02, C0|C1|C2|C3, "fp_comp_ops_exe.mmx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x04, C0|C1|C2|C3, "fp_comp_ops_exe.sse_fp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x08, C0|C1|C2|C3, "fp_comp_ops_exe.sse2_integer" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x10, C0|C1|C2|C3, "fp_comp_ops_exe.sse_fp_packed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x20, C0|C1|C2|C3, "fp_comp_ops_exe.sse_fp_scalar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x40, C0|C1|C2|C3, "fp_comp_ops_exe.sse_single_precision" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x80, C0|C1|C2|C3, "fp_comp_ops_exe.sse_double_precision" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x01, C0|C1|C2|C3, "simd_int_128.packed_mpy" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x02, C0|C1|C2|C3, "simd_int_128.packed_shift" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x04, C0|C1|C2|C3, "simd_int_128.pack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x08, C0|C1|C2|C3, "simd_int_128.unpack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x10, C0|C1|C2|C3, "simd_int_128.packed_logical" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x20, C0|C1|C2|C3, "simd_int_128.packed_arith" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x40, C0|C1|C2|C3, "simd_int_128.shuffle_move" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x01, C0|C1|C2|C3, "load_dispatch.rs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x02, C0|C1|C2|C3, "load_dispatch.rs_delayed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x04, C0|C1|C2|C3, "load_dispatch.mob" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x07, C0|C1|C2|C3, "load_dispatch.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x01, C0|C1|C2|C3, "arith.cycles_div_busy" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x01, C0|C1|C2|C3, "arith.fpu_div" , 0x1, ATTR_EDGE | ATTR_INV, 0x0 }, \ +{ 0x14, 0x02, C0|C1|C2|C3, "arith.mul" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x17, 0x01, C0|C1|C2|C3, "inst_queue_writes" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x18, 0x01, C0|C1|C2|C3, "inst_decoded.dec0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x19, 0x01, C0|C1|C2|C3, "two_uop_insts_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x1E, 0x01, C0|C1|C2|C3, "inst_queue_write_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x20, 0x01, C0|C1|C2|C3, "lsd_overflow" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x01, C0|C1|C2|C3, "l2_rqsts.ld_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x02, C0|C1|C2|C3, "l2_rqsts.ld_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x03, C0|C1|C2|C3, "l2_rqsts.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x04, C0|C1|C2|C3, "l2_rqsts.rfo_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x08, C0|C1|C2|C3, "l2_rqsts.rfo_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x0C, C0|C1|C2|C3, "l2_rqsts.rfos" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x10, C0|C1|C2|C3, "l2_rqsts.ifetch_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x20, C0|C1|C2|C3, "l2_rqsts.ifetch_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x30, C0|C1|C2|C3, "l2_rqsts.ifetches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x40, C0|C1|C2|C3, "l2_rqsts.prefetch_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x80, C0|C1|C2|C3, "l2_rqsts.prefetch_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xAA, C0|C1|C2|C3, "l2_rqsts.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xC0, C0|C1|C2|C3, "l2_rqsts.prefetches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xFF, C0|C1|C2|C3, "l2_rqsts.references" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x01, C0|C1|C2|C3, "l2_data_rqsts.demand.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x02, C0|C1|C2|C3, "l2_data_rqsts.demand.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x04, C0|C1|C2|C3, "l2_data_rqsts.demand.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x08, C0|C1|C2|C3, "l2_data_rqsts.demand.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x0F, C0|C1|C2|C3, "l2_data_rqsts.demand.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x10, C0|C1|C2|C3, "l2_data_rqsts.prefetch.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x20, C0|C1|C2|C3, "l2_data_rqsts.prefetch.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x40, C0|C1|C2|C3, "l2_data_rqsts.prefetch.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x80, C0|C1|C2|C3, "l2_data_rqsts.prefetch.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0xF0, C0|C1|C2|C3, "l2_data_rqsts.prefetch.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0xFF, C0|C1|C2|C3, "l2_data_rqsts.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x01, C0|C1|C2|C3, "l2_write.rfo.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x02, C0|C1|C2|C3, "l2_write.rfo.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x08, C0|C1|C2|C3, "l2_write.rfo.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x0E, C0|C1|C2|C3, "l2_write.rfo.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x0F, C0|C1|C2|C3, "l2_write.rfo.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x10, C0|C1|C2|C3, "l2_write.lock.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x20, C0|C1|C2|C3, "l2_write.lock.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x40, C0|C1|C2|C3, "l2_write.lock.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x80, C0|C1|C2|C3, "l2_write.lock.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0xE0, C0|C1|C2|C3, "l2_write.lock.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0xF0, C0|C1|C2|C3, "l2_write.lock.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x01, C0|C1|C2|C3, "l1d_wb_l2.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x02, C0|C1|C2|C3, "l1d_wb_l2.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x04, C0|C1|C2|C3, "l1d_wb_l2.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x08, C0|C1|C2|C3, "l1d_wb_l2.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x0F, C0|C1|C2|C3, "l1d_wb_l2.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x41, C0|C1|C2|C3, "l3_lat_cache.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x4F, C0|C1|C2|C3, "l3_lat_cache.reference" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x00, C0|C1|C2|C3, "cpu_clk_unhalted.thread_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x01, C0|C1|C2|C3, "cpu_clk_unhalted.ref_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x40, 0x01, C0|C1 , "l1d_cache_ld.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x40, 0x02, C0|C1 , "l1d_cache_ld.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x40, 0x04, C0|C1 , "l1d_cache_ld.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x40, 0x08, C0|C1 , "l1d_cache_ld.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x40, 0x0F, C0|C1 , "l1d_cache_ld.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x41, 0x02, C0|C1 , "l1d_cache_st.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x41, 0x04, C0|C1 , "l1d_cache_st.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x41, 0x08, C0|C1 , "l1d_cache_st.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x42, 0x01, C0|C1 , "l1d_cache_lock.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x42, 0x02, C0|C1 , "l1d_cache_lock.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x42, 0x04, C0|C1 , "l1d_cache_lock.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x42, 0x08, C0|C1 , "l1d_cache_lock.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x43, 0x01, C0|C1 , "l1d_all_ref.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x43, 0x02, C0|C1 , "l1d_all_ref.cacheable" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x01, C0|C1|C2|C3, "dtlb_misses.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x02, C0|C1|C2|C3, "dtlb_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x10, C0|C1|C2|C3, "dtlb_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x20, C0|C1|C2|C3, "dtlb_misses.pde_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x80, C0|C1|C2|C3, "dtlb_misses.large_walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x01, C0|C1|C2|C3, "load_hit_pre" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4E, 0x01, C0|C1|C2|C3, "l1d_prefetch.requests" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4E, 0x02, C0|C1|C2|C3, "l1d_prefetch.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4E, 0x04, C0|C1|C2|C3, "l1d_prefetch.triggers" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x01, C0|C1 , "l1d.repl" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x02, C0|C1 , "l1d.m_repl" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x04, C0|C1 , "l1d.m_evict" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x08, C0|C1 , "l1d.m_snoop_evict" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x52, 0x01, C0|C1|C2|C3, "l1d_cache_prefetch_lock_fb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x53, 0x01, C0|C1|C2|C3, "l1d_cache_lock_fb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x01, C0|C1 , "cache_lock_cycles.l1d_l2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x02, C0|C1 , "cache_lock_cycles.l1d" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6C, 0x01, C0|C1|C2|C3, "io_transactions" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x01, C0|C1|C2|C3, "l1i.hits" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x02, C0|C1|C2|C3, "l1i.misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x03, C0|C1|C2|C3, "l1i.reads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x04, C0|C1|C2|C3, "l1i.cycles_stalled" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x82, 0x01, C0|C1|C2|C3, "large_itlb.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x01, C0|C1|C2|C3, "itlb_misses.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x02, C0|C1|C2|C3, "itlb_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x01, C0|C1|C2|C3, "ild_stall.lcp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x02, C0|C1|C2|C3, "ild_stall.mru" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x04, C0|C1|C2|C3, "ild_stall.iq_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x08, C0|C1|C2|C3, "ild_stall.regen" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x0F, C0|C1|C2|C3, "ild_stall.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x01, C0|C1|C2|C3, "br_inst_exec.cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x02, C0|C1|C2|C3, "br_inst_exec.direct" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x04, C0|C1|C2|C3, "br_inst_exec.indirect_non_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x07, C0|C1|C2|C3, "br_inst_exec.non_calls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x08, C0|C1|C2|C3, "br_inst_exec.return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x10, C0|C1|C2|C3, "br_inst_exec.direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x20, C0|C1|C2|C3, "br_inst_exec.indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x30, C0|C1|C2|C3, "br_inst_exec.near_calls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x40, C0|C1|C2|C3, "br_inst_exec.taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x7F, C0|C1|C2|C3, "br_inst_exec.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x01, C0|C1|C2|C3, "br_misp_exec.cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x02, C0|C1|C2|C3, "br_misp_exec.direct" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x04, C0|C1|C2|C3, "br_misp_exec.indirect_non_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x07, C0|C1|C2|C3, "br_misp_exec.non_calls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x08, C0|C1|C2|C3, "br_misp_exec.return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x10, C0|C1|C2|C3, "br_misp_exec.direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x20, C0|C1|C2|C3, "br_misp_exec.indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x30, C0|C1|C2|C3, "br_misp_exec.near_calls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x40, C0|C1|C2|C3, "br_misp_exec.taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x7F, C0|C1|C2|C3, "br_misp_exec.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x01, C0|C1|C2|C3, "resource_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x02, C0|C1|C2|C3, "resource_stalls.load" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x04, C0|C1|C2|C3, "resource_stalls.rs_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x08, C0|C1|C2|C3, "resource_stalls.store" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x10, C0|C1|C2|C3, "resource_stalls.rob_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x20, C0|C1|C2|C3, "resource_stalls.fpcw" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x40, C0|C1|C2|C3, "resource_stalls.mxcsr" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x80, C0|C1|C2|C3, "resource_stalls.other" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA6, 0x01, C0|C1|C2|C3, "macro_insts.fusions_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA7, 0x01, C0|C1|C2|C3, "baclear_force_iq" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA8, 0x01, C0|C1|C2|C3, "lsd.uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA8, 0x01, C0|C1|C2|C3, "lsd.cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xAE, 0x01, C0|C1|C2|C3, "itlb_flush" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x40, C0|C1|C2|C3, "offcore_requests.l1d_writeback" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C0|C1|C2|C3, "uops_executed.port0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C0|C1|C2|C3, "uops_executed.port1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x04, C0|C1|C2|C3, "uops_executed.port2_core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x08, C0|C1|C2|C3, "uops_executed.port3_core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x10, C0|C1|C2|C3, "uops_executed.port4_core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x1F, C0|C1|C2|C3, "uops_executed.core_active_cycles_no_port5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x20, C0|C1|C2|C3, "uops_executed.port5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x3F, C0|C1|C2|C3, "uops_executed.core_active_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x40, C0|C1|C2|C3, "uops_executed.port015" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x40, C0|C1|C2|C3, "uops_executed.port015_stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xB1, 0x80, C0|C1|C2|C3, "uops_executed.port234" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB2, 0x01, C0|C1|C2|C3, "offcore_requests_sq_full" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xB7, 0x01, C0|C1|C2|C3, "off_core_response_0" , 0x0, ATTR_NONE, 0x1A6 }, ignore events that require msr_offset */ \ +{ 0xB8, 0x01, C0|C1|C2|C3, "snoop_response.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB8, 0x02, C0|C1|C2|C3, "snoop_response.hite" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB8, 0x04, C0|C1|C2|C3, "snoop_response.hitm" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xBB, 0x01, C0|C1|C2|C3, "off_core_response_1" , 0x0, ATTR_NONE, 0x1A7 }, ignore events that require msr_offset */ \ +{ 0xC0, 0x00, C0|C1|C2|C3, "inst_retired.any_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x02, C0|C1|C2|C3, "inst_retired.x87" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x04, C0|C1|C2|C3, "inst_retired.mmx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C0|C1|C2|C3, "uops_retired.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C0|C1|C2|C3, "uops_retired.active_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C0|C1|C2|C3, "uops_retired.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xC2, 0x02, C0|C1|C2|C3, "uops_retired.retire_slots" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x04, C0|C1|C2|C3, "uops_retired.macro_fused" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x01, C0|C1|C2|C3, "machine_clears.cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x02, C0|C1|C2|C3, "machine_clears.mem_order" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x04, C0|C1|C2|C3, "machine_clears.smc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x00, C0|C1|C2|C3, "br_inst_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x01, C0|C1|C2|C3, "br_inst_retired.conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x02, C0|C1|C2|C3, "br_inst_retired.near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x00, C0|C1|C2|C3, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x02, C0|C1|C2|C3, "br_misp_retired.near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x01, C0|C1|C2|C3, "ssex_uops_retired.packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x02, C0|C1|C2|C3, "ssex_uops_retired.scalar_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x04, C0|C1|C2|C3, "ssex_uops_retired.packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x08, C0|C1|C2|C3, "ssex_uops_retired.scalar_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x10, C0|C1|C2|C3, "ssex_uops_retired.vector_integer" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x20, C0|C1|C2|C3, "itlb_miss_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x01, C0|C1|C2|C3, "mem_load_retired.l1d_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x02, C0|C1|C2|C3, "mem_load_retired.l2_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x04, C0|C1|C2|C3, "mem_load_retired.llc_unshared_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x08, C0|C1|C2|C3, "mem_load_retired.other_core_l2_hit_hitm" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x10, C0|C1|C2|C3, "mem_load_retired.llc_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x40, C0|C1|C2|C3, "mem_load_retired.hit_lfb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x80, C0|C1|C2|C3, "mem_load_retired.dtlb_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x01, C0|C1|C2|C3, "fp_mmx_trans.to_fp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x02, C0|C1|C2|C3, "fp_mmx_trans.to_mmx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x03, C0|C1|C2|C3, "fp_mmx_trans.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x01, C0|C1|C2|C3, "macro_insts.decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x02, C0|C1|C2|C3, "uops_decoded.ms" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x04, C0|C1|C2|C3, "uops_decoded.esp_folding" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x08, C0|C1|C2|C3, "uops_decoded.esp_sync" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x01, C0|C1|C2|C3, "rat_stalls.flags" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x02, C0|C1|C2|C3, "rat_stalls.registers" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x04, C0|C1|C2|C3, "rat_stalls.rob_read_port" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x08, C0|C1|C2|C3, "rat_stalls.scoreboard" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x0F, C0|C1|C2|C3, "rat_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD4, 0x01, C0|C1|C2|C3, "seg_rename_stalls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD5, 0x01, C0|C1|C2|C3, "es_reg_renames" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xDB, 0x01, C0|C1|C2|C3, "uop_unfusion" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE0, 0x01, C0|C1|C2|C3, "br_inst_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE5, 0x01, C0|C1|C2|C3, "bpu_missed_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE6, 0x01, C0|C1|C2|C3, "baclear.clear" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE6, 0x02, C0|C1|C2|C3, "baclear.bad_target" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE8, 0x01, C0|C1|C2|C3, "bpu_clears.early" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE8, 0x02, C0|C1|C2|C3, "bpu_clears.late" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x01, C0|C1|C2|C3, "l2_transactions.load" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x02, C0|C1|C2|C3, "l2_transactions.rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x04, C0|C1|C2|C3, "l2_transactions.ifetch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x08, C0|C1|C2|C3, "l2_transactions.prefetch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x10, C0|C1|C2|C3, "l2_transactions.l1d_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x20, C0|C1|C2|C3, "l2_transactions.fill" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x40, C0|C1|C2|C3, "l2_transactions.wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x80, C0|C1|C2|C3, "l2_transactions.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x02, C0|C1|C2|C3, "l2_lines_in.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x04, C0|C1|C2|C3, "l2_lines_in.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x07, C0|C1|C2|C3, "l2_lines_in.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x01, C0|C1|C2|C3, "l2_lines_out.demand_clean" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x02, C0|C1|C2|C3, "l2_lines_out.demand_dirty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x04, C0|C1|C2|C3, "l2_lines_out.prefetch_clean" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x08, C0|C1|C2|C3, "l2_lines_out.prefetch_dirty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x0F, C0|C1|C2|C3, "l2_lines_out.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF4, 0x10, C0|C1|C2|C3, "sq_misc.split_lock" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF6, 0x01, C0|C1|C2|C3, "sq_full_stall_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF7, 0x01, C0|C1|C2|C3, "fp_assist.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF7, 0x02, C0|C1|C2|C3, "fp_assist.output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF7, 0x04, C0|C1|C2|C3, "fp_assist.input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x01, C0|C1|C2|C3, "simd_int_64.packed_mpy" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x02, C0|C1|C2|C3, "simd_int_64.packed_shift" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x04, C0|C1|C2|C3, "simd_int_64.pack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x08, C0|C1|C2|C3, "simd_int_64.unpack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x10, C0|C1|C2|C3, "simd_int_64.packed_logical" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x20, C0|C1|C2|C3, "simd_int_64.packed_arith" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x40, C0|C1|C2|C3, "simd_int_64.shuffle_move" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +#define EVENTS_FAM6_MOD46_ONLY \ +{ 0x0F, 0x01, C0|C1|C2|C3, "mem_uncore_retired.l3_data_miss_unknown" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x80, C0|C1|C2|C3, "mem_uncore_retired.uncacheable" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +/* Intel Westmere Processor */ +/* + * The Westmere tables are basically from Bug 16173963 + * libcpc counter names should be based on public Intel documentation -- Westmere + * and those tables are basically from the + * Intel SDM, January 2013, Section 19.6, Table 19-13. + * We omit the Table 19-14 uncore events. + * + * Note that the table below includes some events from + * the Intel SDM that require cmask or attr settings. + * These events are not in libcpc, which did not include + * events requiring cmask or attr until Sandy Bridge. + */ + +#define EVENTS_FAM6_MOD37 \ +{ 0x03, 0x02, C0|C1|C2|C3, "load_block.overlap_store" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x04, 0x07, C0|C1|C2|C3, "sb_drain.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x05, 0x02, C0|C1|C2|C3, "misalign_mem_ref.store" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x06, 0x04, C0|C1|C2|C3, "store_blocks.at_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x06, 0x08, C0|C1|C2|C3, "store_blocks.l1d_block" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x01, C0|C1|C2|C3, "partial_address_alias" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x01, C0|C1|C2|C3, "dtlb_load_misses.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x02, C0|C1|C2|C3, "dtlb_load_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x04, C0|C1|C2|C3, "dtlb_load_misses.walk_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x10, C0|C1|C2|C3, "dtlb_load_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x20, C0|C1|C2|C3, "dtlb_load_misses.pde_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0B, 0x01, C0|C1|C2|C3, "mem_inst_retired.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0B, 0x02, C0|C1|C2|C3, "mem_inst_retired.stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0B, 0x10, C0|C1|C2|C3, "mem_inst_retired.latency_above_threshold" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0C, 0x01, C0|C1|C2|C3, "mem_store_retired.dtlb_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C0|C1|C2|C3, "uops_issued.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x02, C0|C1|C2|C3, "uops_issued.fused" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x01, C0|C1|C2|C3, "mem_uncore_retired.unknown_source" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x80, C0|C1|C2|C3, "mem_uncore_retired.uncacheable" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x01, C0|C1|C2|C3, "fp_comp_ops_exe.x87" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x02, C0|C1|C2|C3, "fp_comp_ops_exe.mmx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x04, C0|C1|C2|C3, "fp_comp_ops_exe.sse_fp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x08, C0|C1|C2|C3, "fp_comp_ops_exe.sse2_integer" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x10, C0|C1|C2|C3, "fp_comp_ops_exe.sse_fp_packed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x20, C0|C1|C2|C3, "fp_comp_ops_exe.sse_fp_scalar" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x40, C0|C1|C2|C3, "fp_comp_ops_exe.sse_single_precision" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x80, C0|C1|C2|C3, "fp_comp_ops_exe.sse_double_precision" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x01, C0|C1|C2|C3, "simd_int_128.packed_mpy" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x02, C0|C1|C2|C3, "simd_int_128.packed_shift" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x04, C0|C1|C2|C3, "simd_int_128.pack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x08, C0|C1|C2|C3, "simd_int_128.unpack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x10, C0|C1|C2|C3, "simd_int_128.packed_logical" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x20, C0|C1|C2|C3, "simd_int_128.packed_arith" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x12, 0x40, C0|C1|C2|C3, "simd_int_128.shuffle_move" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x01, C0|C1|C2|C3, "load_dispatch.rs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x02, C0|C1|C2|C3, "load_dispatch.rs_delayed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x04, C0|C1|C2|C3, "load_dispatch.mob" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x13, 0x07, C0|C1|C2|C3, "load_dispatch.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x01, C0|C1|C2|C3, "arith.cycles_div_busy" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x02, C0|C1|C2|C3, "arith.mul" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x17, 0x01, C0|C1|C2|C3, "inst_queue_writes" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x18, 0x01, C0|C1|C2|C3, "inst_decoded.dec0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x19, 0x01, C0|C1|C2|C3, "two_uop_insts_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x1E, 0x01, C0|C1|C2|C3, "inst_queue_write_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x20, 0x01, C0|C1|C2|C3, "lsd_overflow" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x01, C0|C1|C2|C3, "l2_rqsts.ld_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x02, C0|C1|C2|C3, "l2_rqsts.ld_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x03, C0|C1|C2|C3, "l2_rqsts.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x04, C0|C1|C2|C3, "l2_rqsts.rfo_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x08, C0|C1|C2|C3, "l2_rqsts.rfo_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x0C, C0|C1|C2|C3, "l2_rqsts.rfos" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x10, C0|C1|C2|C3, "l2_rqsts.ifetch_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x20, C0|C1|C2|C3, "l2_rqsts.ifetch_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x30, C0|C1|C2|C3, "l2_rqsts.ifetches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x40, C0|C1|C2|C3, "l2_rqsts.prefetch_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x80, C0|C1|C2|C3, "l2_rqsts.prefetch_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xAA, C0|C1|C2|C3, "l2_rqsts.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xC0, C0|C1|C2|C3, "l2_rqsts.prefetches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xFF, C0|C1|C2|C3, "l2_rqsts.references" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x01, C0|C1|C2|C3, "l2_data_rqsts.demand.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x02, C0|C1|C2|C3, "l2_data_rqsts.demand.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x04, C0|C1|C2|C3, "l2_data_rqsts.demand.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x08, C0|C1|C2|C3, "l2_data_rqsts.demand.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x0F, C0|C1|C2|C3, "l2_data_rqsts.demand.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x10, C0|C1|C2|C3, "l2_data_rqsts.prefetch.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x20, C0|C1|C2|C3, "l2_data_rqsts.prefetch.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x40, C0|C1|C2|C3, "l2_data_rqsts.prefetch.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0x80, C0|C1|C2|C3, "l2_data_rqsts.prefetch.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0xF0, C0|C1|C2|C3, "l2_data_rqsts.prefetch.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x26, 0xFF, C0|C1|C2|C3, "l2_data_rqsts.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x01, C0|C1|C2|C3, "l2_write.rfo.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x02, C0|C1|C2|C3, "l2_write.rfo.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x08, C0|C1|C2|C3, "l2_write.rfo.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x0E, C0|C1|C2|C3, "l2_write.rfo.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x0F, C0|C1|C2|C3, "l2_write.rfo.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x10, C0|C1|C2|C3, "l2_write.lock.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x20, C0|C1|C2|C3, "l2_write.lock.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x40, C0|C1|C2|C3, "l2_write.lock.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x80, C0|C1|C2|C3, "l2_write.lock.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0xE0, C0|C1|C2|C3, "l2_write.lock.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0xF0, C0|C1|C2|C3, "l2_write.lock.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x01, C0|C1|C2|C3, "l1d_wb_l2.i_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x02, C0|C1|C2|C3, "l1d_wb_l2.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x04, C0|C1|C2|C3, "l1d_wb_l2.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x08, C0|C1|C2|C3, "l1d_wb_l2.m_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x0F, C0|C1|C2|C3, "l1d_wb_l2.mesi" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x41, C0|C1|C2|C3, "l3_lat_cache.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x4F, C0|C1|C2|C3, "l3_lat_cache.reference" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x00, C0|C1|C2|C3, "cpu_clk_unhalted.thread_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x01, C0|C1|C2|C3, "cpu_clk_unhalted.ref_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x01, C0|C1|C2|C3, "dtlb_misses.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x02, C0|C1|C2|C3, "dtlb_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x04, C0|C1|C2|C3, "dtlb_misses.walk_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x10, C0|C1|C2|C3, "dtlb_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x20, C0|C1|C2|C3, "dtlb_misses.pde_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x80, C0|C1|C2|C3, "dtlb_misses.large_walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x01, C0|C1 , "load_hit_pre" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4E, 0x01, C0|C1 , "l1d_prefetch.requests" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4E, 0x02, C0|C1 , "l1d_prefetch.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4E, 0x04, C0|C1 , "l1d_prefetch.triggers" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4F, 0x10, C0|C1|C2|C3, "ept.walk_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x01, C0|C1 , "l1d.repl" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x02, C0|C1 , "l1d.m_repl" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x04, C0|C1 , "l1d.m_evict" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x08, C0|C1 , "l1d.m_snoop_evict" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x52, 0x01, C0|C1|C2|C3, "l1d_cache_prefetch_lock_fb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C0 , "offcore_requests_outstanding.demand.read_data", 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x02, C0 , "offcore_requests_outstanding.demand.read_code", 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C0 , "offcore_requests_outstanding.demand.rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C0 , "offcore_requests_outstanding.any_read", 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x01, C0|C1 , "cache_lock_cycles.l1d_l2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x02, C0|C1 , "cache_lock_cycles.l1d" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x6C, 0x01, C0|C1|C2|C3, "io_transactions" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x01, C0|C1|C2|C3, "l1i.hits" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x02, C0|C1|C2|C3, "l1i.misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x03, C0|C1|C2|C3, "l1i.reads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x04, C0|C1|C2|C3, "l1i.cycles_stalled" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x82, 0x01, C0|C1|C2|C3, "large_itlb.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x01, C0|C1|C2|C3, "itlb_misses.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x02, C0|C1|C2|C3, "itlb_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x04, C0|C1|C2|C3, "itlb_misses.walk_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x10, C0|C1|C2|C3, "itlb_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x80, C0|C1|C2|C3, "itlb_misses.large_walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x01, C0|C1|C2|C3, "ild_stall.lcp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x02, C0|C1|C2|C3, "ild_stall.mru" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x04, C0|C1|C2|C3, "ild_stall.iq_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x08, C0|C1|C2|C3, "ild_stall.regen" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x0F, C0|C1|C2|C3, "ild_stall.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x01, C0|C1|C2|C3, "br_inst_exec.cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x02, C0|C1|C2|C3, "br_inst_exec.direct" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x04, C0|C1|C2|C3, "br_inst_exec.indirect_non_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x07, C0|C1|C2|C3, "br_inst_exec.non_calls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x08, C0|C1|C2|C3, "br_inst_exec.return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x10, C0|C1|C2|C3, "br_inst_exec.direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x20, C0|C1|C2|C3, "br_inst_exec.indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x30, C0|C1|C2|C3, "br_inst_exec.near_calls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x40, C0|C1|C2|C3, "br_inst_exec.taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x7F, C0|C1|C2|C3, "br_inst_exec.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x01, C0|C1|C2|C3, "br_misp_exec.cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x02, C0|C1|C2|C3, "br_misp_exec.direct" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x04, C0|C1|C2|C3, "br_misp_exec.indirect_non_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x07, C0|C1|C2|C3, "br_misp_exec.non_calls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x08, C0|C1|C2|C3, "br_misp_exec.return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x10, C0|C1|C2|C3, "br_misp_exec.direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x20, C0|C1|C2|C3, "br_misp_exec.indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x30, C0|C1|C2|C3, "br_misp_exec.near_calls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x40, C0|C1|C2|C3, "br_misp_exec.taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x7F, C0|C1|C2|C3, "br_misp_exec.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x01, C0|C1|C2|C3, "resource_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x02, C0|C1|C2|C3, "resource_stalls.load" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x04, C0|C1|C2|C3, "resource_stalls.rs_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x08, C0|C1|C2|C3, "resource_stalls.store" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x10, C0|C1|C2|C3, "resource_stalls.rob_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x20, C0|C1|C2|C3, "resource_stalls.fpcw" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x40, C0|C1|C2|C3, "resource_stalls.mxcsr" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x80, C0|C1|C2|C3, "resource_stalls.other" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA6, 0x01, C0|C1|C2|C3, "macro_insts.fusions_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA7, 0x01, C0|C1|C2|C3, "baclear_force_iq" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA8, 0x01, C0|C1|C2|C3, "lsd.uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAE, 0x01, C0|C1|C2|C3, "itlb_flush" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x01, C0|C1|C2|C3, "offcore_requests.demand.read_data" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x02, C0|C1|C2|C3, "offcore_requests.demand.read_code" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x04, C0|C1|C2|C3, "offcore_requests.demand.rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x08, C0|C1|C2|C3, "offcore_requests.any.read" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x10, C0|C1|C2|C3, "offcore_requests.any.rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x40, C0|C1|C2|C3, "offcore_requests.l1d_writeback" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x80, C0|C1|C2|C3, "offcore_requests.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C0|C1|C2|C3, "uops_executed.port0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C0|C1|C2|C3, "uops_executed.port1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x04, C0|C1|C2|C3, "uops_executed.port2_core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x08, C0|C1|C2|C3, "uops_executed.port3_core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x10, C0|C1|C2|C3, "uops_executed.port4_core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x1F, C0|C1|C2|C3, "uops_executed.core_active_cycles_no_port5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x20, C0|C1|C2|C3, "uops_executed.port5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x3F, C0|C1|C2|C3, "uops_executed.core_active_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x40, C0|C1|C2|C3, "uops_executed.port015" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x80, C0|C1|C2|C3, "uops_executed.port234" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB2, 0x01, C0|C1|C2|C3, "offcore_requests_sq_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB3, 0x01, C0, "snoopq_requests_outstanding.data" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB3, 0x02, C0, "snoopq_requests_outstanding.invalidate" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB3, 0x04, C0, "snoopq_requests_outstanding.code" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB4, 0x01, C0|C1|C2|C3, "snoopq_requests.code" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB4, 0x02, C0|C1|C2|C3, "snoopq_requests.data" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB4, 0x04, C0|C1|C2|C3, "snoopq_requests.invalidate" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xB7, 0x01, C0|C1|C2|C3, "off_core_response_0" , 0x0, ATTR_NONE, 0x1A6 }, ignore events that require msr_offset */ \ +{ 0xB8, 0x01, C0|C1|C2|C3, "snoop_response.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB8, 0x02, C0|C1|C2|C3, "snoop_response.hite" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB8, 0x04, C0|C1|C2|C3, "snoop_response.hitm" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xBB, 0x01, C0|C1|C2|C3, "off_core_response_1" , 0x0, ATTR_NONE, 0x1A7 }, ignore events that require msr_offset */ \ +{ 0xC0, 0x00, C0|C1|C2|C3, "inst_retired.any_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x02, C0|C1|C2|C3, "inst_retired.x87" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x04, C0|C1|C2|C3, "inst_retired.mmx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C0|C1|C2|C3, "uops_retired.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x02, C0|C1|C2|C3, "uops_retired.retire_slots" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x04, C0|C1|C2|C3, "uops_retired.macro_fused" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x01, C0|C1|C2|C3, "machine_clears.cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x02, C0|C1|C2|C3, "machine_clears.mem_order" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x04, C0|C1|C2|C3, "machine_clears.smc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x00, C0|C1|C2|C3, "br_inst_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x01, C0|C1|C2|C3, "br_inst_retired.conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x02, C0|C1|C2|C3, "br_inst_retired.near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x00, C0|C1|C2|C3, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x01, C0|C1|C2|C3, "br_misp_retired.conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x02, C0|C1|C2|C3, "br_misp_retired.near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x04, C0|C1|C2|C3, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x01, C0|C1|C2|C3, "ssex_uops_retired.packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x02, C0|C1|C2|C3, "ssex_uops_retired.scalar_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x04, C0|C1|C2|C3, "ssex_uops_retired.packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x08, C0|C1|C2|C3, "ssex_uops_retired.scalar_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x10, C0|C1|C2|C3, "ssex_uops_retired.vector_integer" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x20, C0|C1|C2|C3, "itlb_miss_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x01, C0|C1|C2|C3, "mem_load_retired.l1d_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x02, C0|C1|C2|C3, "mem_load_retired.l2_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x04, C0|C1|C2|C3, "mem_load_retired.llc_unshared_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x08, C0|C1|C2|C3, "mem_load_retired.other_core_l2_hit_hitm" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x10, C0|C1|C2|C3, "mem_load_retired.llc_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x40, C0|C1|C2|C3, "mem_load_retired.hit_lfb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x80, C0|C1|C2|C3, "mem_load_retired.dtlb_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x01, C0|C1|C2|C3, "fp_mmx_trans.to_fp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x02, C0|C1|C2|C3, "fp_mmx_trans.to_mmx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x03, C0|C1|C2|C3, "fp_mmx_trans.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x01, C0|C1|C2|C3, "macro_insts.decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x01, C0|C1|C2|C3, "uops_decoded.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xD1, 0x02, C0|C1|C2|C3, "uops_decoded.ms" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x04, C0|C1|C2|C3, "uops_decoded.esp_folding" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x08, C0|C1|C2|C3, "uops_decoded.esp_sync" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x01, C0|C1|C2|C3, "rat_stalls.flags" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x02, C0|C1|C2|C3, "rat_stalls.registers" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x04, C0|C1|C2|C3, "rat_stalls.rob_read_port" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x08, C0|C1|C2|C3, "rat_stalls.scoreboard" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x0F, C0|C1|C2|C3, "rat_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD4, 0x01, C0|C1|C2|C3, "seg_rename_stalls" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD5, 0x01, C0|C1|C2|C3, "es_reg_renames" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xDB, 0x01, C0|C1|C2|C3, "uop_unfusion" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE0, 0x01, C0|C1|C2|C3, "br_inst_decoded" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE5, 0x01, C0|C1|C2|C3, "bpu_missed_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE6, 0x01, C0|C1|C2|C3, "baclear.clear" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE6, 0x02, C0|C1|C2|C3, "baclear.bad_target" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE8, 0x02, C0|C1|C2|C3, "bpu_clears.late" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xEC, 0x01, C0|C1|C2|C3, "thread_active" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x01, C0|C1|C2|C3, "l2_transactions.load" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x02, C0|C1|C2|C3, "l2_transactions.rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x04, C0|C1|C2|C3, "l2_transactions.ifetch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x08, C0|C1|C2|C3, "l2_transactions.prefetch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x10, C0|C1|C2|C3, "l2_transactions.l1d_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x20, C0|C1|C2|C3, "l2_transactions.fill" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x40, C0|C1|C2|C3, "l2_transactions.wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x80, C0|C1|C2|C3, "l2_transactions.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x02, C0|C1|C2|C3, "l2_lines_in.s_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x04, C0|C1|C2|C3, "l2_lines_in.e_state" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x07, C0|C1|C2|C3, "l2_lines_in.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x01, C0|C1|C2|C3, "l2_lines_out.demand_clean" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x02, C0|C1|C2|C3, "l2_lines_out.demand_dirty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x04, C0|C1|C2|C3, "l2_lines_out.prefetch_clean" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x08, C0|C1|C2|C3, "l2_lines_out.prefetch_dirty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x0F, C0|C1|C2|C3, "l2_lines_out.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF4, 0x04, C0|C1|C2|C3, "sq_misc.lru_hints" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF4, 0x10, C0|C1|C2|C3, "sq_misc.split_lock" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF6, 0x01, C0|C1|C2|C3, "sq_full_stall_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF7, 0x01, C0|C1|C2|C3, "fp_assist.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF7, 0x02, C0|C1|C2|C3, "fp_assist.output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF7, 0x04, C0|C1|C2|C3, "fp_assist.input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x01, C0|C1|C2|C3, "simd_int_64.packed_mpy" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x02, C0|C1|C2|C3, "simd_int_64.packed_shift" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x04, C0|C1|C2|C3, "simd_int_64.pack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x08, C0|C1|C2|C3, "simd_int_64.unpack" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x10, C0|C1|C2|C3, "simd_int_64.packed_logical" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x20, C0|C1|C2|C3, "simd_int_64.packed_arith" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xFD, 0x40, C0|C1|C2|C3, "simd_int_64.shuffle_move" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +/* + * This special omission of the following events from Model 47 + * is due to usr/src/uts/intel/pcbe/wm_pcbe.h . There seems + * to be no substantiation for this treatment in the Intel SDM. + */ +#define EVENTS_FAM6_MOD37_ALSO \ +{ 0x0F, 0x02, C0|C1|C2|C3, "mem_uncore_retired.other_core_l2_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x04, C0|C1|C2|C3, "mem_uncore_retired.remote_hitm" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x08, C0|C1|C2|C3, "mem_uncore_retired.local_dram_remote_cache_hit", 0x0, ATTR_NONE, 0x0 },\ +{ 0x0F, 0x10, C0|C1|C2|C3, "mem_uncore_retired.remote_dram" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0F, 0x20, C0|C1|C2|C3, "mem_uncore_retired.other_llc_miss" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +/* Intel Sandy Bridge Processor */ +/* + * The Sandy Bridge tables are basically from Bug 16457080 + * libcpc counter names should be based on public Intel documentation -- Sandy Bridge + * and those tables are basically from the + * Intel SDM, January 2013, Section 19.4, Table 19-7. + * Additionally, there are + * Table 19-8. Model 42 only. + * Table 19-9. Model 45 only. + * We omit the Table 19-10 uncore events. + */ + +#define EVENTS_FAM6_MOD42 \ +{ 0x03, 0x01, C_ALL, "ld_blocks.data_unknown" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x02, C_ALL, "ld_blocks.store_forward" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x08, C_ALL, "ld_blocks.no_sr" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x10, C_ALL, "ld_blocks.all_block" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x05, 0x01, C_ALL, "misalign_mem_ref.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x05, 0x02, C_ALL, "misalign_mem_ref.stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x01, C_ALL, "ld_blocks_partial.address_alias" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x08, C_ALL, "ld_blocks_partial.all_sta_block" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x01, C_ALL, "dtlb_load_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x02, C_ALL, "dtlb_load_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x04, C_ALL, "dtlb_load_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x10, C_ALL, "dtlb_load_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0D, 0x03, C_ALL, "int_misc.recovery_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x0D, 0x03, C_ALL, "int_misc.recovery_stalls_count" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x0D, 0x40, C_ALL, "int_misc.rat_stall_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.core_stall_cycles" , 0x1, ATTR_INV | ATTR_ANY, 0x0 }, \ +{ 0x10, 0x01, C_ALL, "fp_comp_ops_exe.x87" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x10, C_ALL, "fp_comp_ops_exe.sse_fp_packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x20, C_ALL, "fp_comp_ops_exe.sse_fp_scalar_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x40, C_ALL, "fp_comp_ops_exe.sse_packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x80, C_ALL, "fp_comp_ops_exe.sse_scalar_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x11, 0x01, C_ALL, "simd_fp_256.packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x11, 0x02, C_ALL, "simd_fp_256.packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x01, C_ALL, "arith.fpu_div_active" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x01, C_ALL, "arith.fpu_div" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x17, 0x01, C_ALL, "insts_written_to_iq.insts" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x01, C_ALL, "l2_rqsts.demand_data_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x03, C_ALL, "l2_rqsts.all_demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x04, C_ALL, "l2_rqsts.rfo_hits" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x08, C_ALL, "l2_rqsts.rfo_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x0C, C_ALL, "l2_rqsts.all_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x10, C_ALL, "l2_rqsts.code_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x20, C_ALL, "l2_rqsts.code_rd_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x30, C_ALL, "l2_rqsts.all_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x40, C_ALL, "l2_rqsts.pf_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x80, C_ALL, "l2_rqsts.pf_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xC0, C_ALL, "l2_rqsts.all_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x01, C_ALL, "l2_store_lock_rqsts.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x04, C_ALL, "l2_store_lock_rqsts.hit_e" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x08, C_ALL, "l2_store_lock_rqsts.hit_m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x0F, C_ALL, "l2_store_lock_rqsts.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x01, C_ALL, "l2_l1d_wb_rqsts.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x02, C_ALL, "l2_l1d_wb_rqsts.hit_s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x04, C_ALL, "l2_l1d_wb_rqsts.hit_e" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x08, C_ALL, "l2_l1d_wb_rqsts.hit_m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x0F, C_ALL, "l2_l1d_wb_rqsts.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x41, C_ALL, "longest_lat_cache.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x4F, C_ALL, "longest_lat_cache.reference" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x00, C_ALL, "cpu_clk_unhalted.thread_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x01, C_ALL, "cpu_clk_thread_unhalted.ref_xclk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C2 , "l1d_pend_miss.pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C2 , "l1d_pend_miss.pending_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C2 , "l1d_pend_miss.occurrences" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x49, 0x01, C_ALL, "dtlb_store_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x02, C_ALL, "dtlb_store_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x04, C_ALL, "dtlb_store_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x10, C_ALL, "dtlb_store_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x01, C_ALL, "load_hit_pre.sw_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x02, C_ALL, "load_hit_pre.hw_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4E, 0x02, C_ALL, "hw_pre_req.dl1_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x01, C_ALL, "l1d.replacement" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x02, C_ALL, "l1d.allocated_in_m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x04, C_ALL, "l1d.eviction" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x08, C_ALL, "l1d.all_m_replacement" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x59, 0x20, C_ALL, "partial_rat_stalls.flags_merge_uop" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x59, 0x20, C_ALL, "partial_rat_stalls.flags_merge_uop_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x59, 0x40, C_ALL, "partial_rat_stalls.slow_lea_window" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x59, 0x80, C_ALL, "partial_rat_stalls.mul_single_uop" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5B, 0x0C, C0|C1|C2|C3, "resource_stalls2.all_fl_empty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5B, 0x0F, C_ALL, "resource_stalls2.all_prf_control" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5B, 0x40, C_ALL, "resource_stalls2.bob_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5B, 0x4F, C_ALL, "resource_stalls2.ooo_rsrc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5C, 0x01, C_ALL, "cpl_cycles.ring0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5C, 0x01, C_ALL, "cpl_cycles.ring0_transition" , 0x0, ATTR_EDGE, 0x0 }, \ +{ 0x5C, 0x02, C_ALL, "cpl_cycles.ring123" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5E, 0x01, C_ALL, "rs_events.empty_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.demand_data_rd_cycles", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.demand_rfo_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.all_data_rd_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x01, C_ALL, "lock_cycles.split_lock_uc_lock_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x02, C_ALL, "lock_cycles.cache_lock_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x02, C_ALL, "idq.empty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_activations" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x79, 0x20, C_ALL, "idq.ms_mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x20, C_ALL, "idq.ms_mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x3C, C_ALL, "idq.mite_all_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x3C, C_ALL, "idq.mite_all_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x02, C_ALL, "icache.misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x01, C_ALL, "itlb_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x02, C_ALL, "itlb_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x04, C_ALL, "itlb_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x10, C_ALL, "itlb_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x01, C_ALL, "ild_stall.lcp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x04, C_ALL, "ild_stall.iq_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x41, C_ALL, "br_inst_exec.nontaken_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x81, C_ALL, "br_inst_exec.taken_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x82, C_ALL, "br_inst_exec.taken_direct_jmp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x84, C_ALL, "br_inst_exec.taken_indirect_jmp_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x88, C_ALL, "br_inst_exec.taken_return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x90, C_ALL, "br_inst_exec.taken_direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xA0, C_ALL, "br_inst_exec.taken_indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xC1, C_ALL, "br_inst_exec.all_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xFF, C_ALL, "br_inst_exec.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x41, C_ALL, "br_misp_exec.nontaken_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x81, C_ALL, "br_misp_exec.taken_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x84, C_ALL, "br_misp_exec.taken_indirect_jmp_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x88, C_ALL, "br_misp_exec.taken_return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x90, C_ALL, "br_misp_exec.taken_direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xA0, C_ALL, "br_misp_exec.taken_indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xC1, C_ALL, "br_misp_exec.all_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xFF, C_ALL, "br_misp_exec.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x01, C_ALL, "uops_dispatched_port.port_0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x02, C_ALL, "uops_dispatched_port.port_1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x04, C_ALL, "uops_dispatched_port.port_2_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x08, C_ALL, "uops_dispatched_port.port_2_sta" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x0C, C_ALL, "uops_dispatched_port.port_2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x10, C_ALL, "uops_dispatched_port.port_3_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x20, C_ALL, "uops_dispatched_port.port_3_sta" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x30, C_ALL, "uops_dispatched_port.port_3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x40, C_ALL, "uops_dispatched_port.port_4" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x80, C_ALL, "uops_dispatched_port.port_5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x01, C_ALL, "resource_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x02, C_ALL, "resource_stalls.lb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x04, C_ALL, "resource_stalls.rs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x08, C_ALL, "resource_stalls.sb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x10, C_ALL, "resource_stalls.rob" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x20, C_ALL, "resource_stalls.fcsw" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x40, C_ALL, "resource_stalls.mxcsr" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x80, C_ALL, "resource_stalls.other" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x02, C2 , "cycle_activity.cycles_l1d_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x01, C_ALL, "cycle_activity.cycles_l2_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x04, C0|C1|C2|C3, "cycle_activity.cycles_no_dispatch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAB, 0x01, C_ALL, "dsb2mite_switches.count" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAB, 0x02, C_ALL, "dsb2mite_switches.penalty_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAC, 0x02, C_ALL, "dsb_fill.other_cancel" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAC, 0x08, C_ALL, "dsb_fill.exceed_dsb_lines" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAC, 0x0A, C_ALL, "dsb_fill.all_cancel" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAE, 0x01, C_ALL, "itlb.itlb_flush" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x01, C_ALL, "offcore_requests.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x04, C_ALL, "offcore_requests.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x08, C_ALL, "offcore_requests.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C0|C1|C2|C3, "uops_dispatched.thread" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C0|C1|C2|C3, "uops_dispatched.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_dispatched.core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB2, 0x01, C_ALL, "offcore_requests_buffer.sq_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB6, 0x01, C_ALL, "agu_bypass_cancel.count" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xB7, 0x01, C_ALL, "off_core_response_0" , 0x0, ATTR_NONE, 0x1A6 }, ignore events that require msr_offset */ \ +/* { 0xBB, 0x01, C_ALL, "off_core_response_1" , 0x0, ATTR_NONE, 0x1A7 }, ignore events that require msr_offset */ \ +{ 0xBD, 0x01, C_ALL, "tlb_flush.dtlb_thread" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBD, 0x20, C_ALL, "tlb_flush.stlb_any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBF, 0x05, C_ALL, "l1d_blocks.bank_conflict_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x00, C_ALL, "inst_retired.any_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x01, C1, "inst_retired.prec_dist" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x02, C_ALL, "other_assists.itlb_miss_retired" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x08, C_ALL, "other_assists.avx_store" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x10, C_ALL, "other_assists.avx_to_sse" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x20, C_ALL, "other_assists.sse_to_avx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.active_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xC2, 0x02, C_ALL, "uops_retired.retire_slots" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x02, C_ALL, "machine_clears.memory_ordering" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x04, C_ALL, "machine_clears.smc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x20, C_ALL, "machine_clears.maskmov" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x00, C_ALL, "br_inst_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x01, C_ALL, "br_inst_retired.conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x02, C_ALL, "br_inst_retired.near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x04, C_ALL, "br_inst_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x08, C_ALL, "br_inst_retired.near_return" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x10, C_ALL, "br_inst_retired.not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x20, C_ALL, "br_inst_retired.near_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x40, C_ALL, "br_inst_retired.far_branch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x00, C_ALL, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x00, C_ALL, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x01, C_ALL, "br_misp_retired.conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x02, C_ALL, "br_misp_retired.near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x04, C_ALL, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x10, C_ALL, "br_misp_retired.not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x20, C_ALL, "br_misp_retired.taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x02, C_ALL, "fp_assist.x87_output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x04, C_ALL, "fp_assist.x87_input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x08, C_ALL, "fp_assist.simd_output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x10, C_ALL, "fp_assist.simd_input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x1E, C_ALL, "fp_assist.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x20, C_ALL, "rob_misc_events.lbr_inserts" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xCD, 0x01, C3, "mem_trans_retired.load_latency" , 0x0, ATTR_NONE, 0x3F6 }, ignore events that require msr_offset */ /* See Section "MSR_PEBS_LD_LAT_THRESHOLD" */ \ +{ 0xCD, 0x02, C3, "mem_trans_retired.precise_store" , 0x0, ATTR_NONE, 0x0 }, /* See Section "Precise Store Facility" */ \ +{ 0xD0, 0x11, C_ALL, "mem_uops_retired.stlb_miss_loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x12, C_ALL, "mem_uops_retired.stlb_miss_stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x21, C_ALL, "mem_uops_retired.lock_loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x22, C_ALL, "mem_uops_retired.lock_stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x41, C_ALL, "mem_uops_retired.split_loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x42, C_ALL, "mem_uops_retired.split_stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x81, C_ALL, "mem_uops_retired.all_loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x82, C_ALL, "mem_uops_retired.all_stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x01, C0|C1|C2|C3, "mem_load_uops_retired.l1_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x02, C_ALL, "mem_load_uops_retired.l2_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x04, C_ALL, "mem_load_uops_retired.llc_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x20, C_ALL, "mem_load_uops_retired.llc_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x40, C_ALL, "mem_load_uops_retired.hit_lfb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x01, C_ALL, "mem_load_uops_llc_hit_retired.xsnp_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x02, C_ALL, "mem_load_uops_llc_hit_retired.xsnp_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x04, C_ALL, "mem_load_uops_llc_hit_retired.xsnp_hitm" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x08, C_ALL, "mem_load_uops_llc_hit_retired.xsnp_none" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE6, 0x01, C_ALL, "baclears.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x01, C_ALL, "l2_trans.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x02, C_ALL, "l2_trans.rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x04, C_ALL, "l2_trans.code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x08, C_ALL, "l2_trans.all_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x10, C_ALL, "l2_trans.l1d_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x20, C_ALL, "l2_trans.l2_fill" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x40, C_ALL, "l2_trans.l2_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x80, C_ALL, "l2_trans.all_requests" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x01, C_ALL, "l2_lines_in.i" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x02, C_ALL, "l2_lines_in.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x04, C_ALL, "l2_lines_in.e" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x07, C_ALL, "l2_lines_in.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x01, C_ALL, "l2_lines_out.demand_clean" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x02, C_ALL, "l2_lines_out.demand_dirty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x04, C_ALL, "l2_lines_out.pf_clean" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x08, C_ALL, "l2_lines_out.pf_dirty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x0A, C_ALL, "l2_lines_out.dirty_all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF4, 0x10, C_ALL, "sq_misc.split_lock" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +#define EVENTS_FAM6_MOD42_ONLY \ +{ 0xD4, 0x02, C0|C1|C2|C3, "mem_load_uops_misc_retired.llc_miss" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +#define EVENTS_FAM6_MOD45_ONLY \ +/* { 0xD3, 0x01, C_ALL, "mem_load_uops_llc_miss_retired.local_dram" , 0x0, ATTR_NONE, 0x3C9 }, ignore events that require msr_offset */ \ +/* { 0xD3, 0x04, C_ALL, "mem_load_uops_llc_miss_retired.remote_dram" , 0x0, ATTR_NONE, 0x3C9 }, ignore events that require msr_offset */ \ +/* end of #define */ + +/* Intel Ivy Bridge Processor */ +/* + * The Ivy Bridge tables are basically from Bug 16457100 + * libcpc counter names should be based on public Intel documentation -- Ivy Bridge + * and those tables are basically from the + * Intel SDM, January 2013, Section 19.3, Table 19-5. + * Additionally, there is + * Table 19-6. Model 62 only. + */ + +#define EVENTS_FAM6_MOD58 \ +{ 0x03, 0x02, C_ALL, "ld_blocks.store_forward" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x05, 0x01, C_ALL, "misalign_mem_ref.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x05, 0x02, C_ALL, "misalign_mem_ref.stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x01, C_ALL, "ld_blocks_partial.address_alias" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x81, C_ALL, "dtlb_load_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x82, C_ALL, "dtlb_load_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x84, C_ALL, "dtlb_load_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.core_stall_cycles" , 0x1, ATTR_INV | ATTR_ANY, 0x0 }, \ +{ 0x0E, 0x10, C_ALL, "uops_issued.flags_merge" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x20, C_ALL, "uops_issued.slow_lea" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x40, C_ALL, "uops_issued.sIngle_mul" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x01, C_ALL, "fp_comp_ops_exe.x87" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x10, C_ALL, "fp_comp_ops_exe.sse_fp_packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x20, C_ALL, "fp_comp_ops_exe.sse_fp_scalar_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x40, C_ALL, "fp_comp_ops_exe.sse_packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x10, 0x80, C_ALL, "fp_comp_ops_exe.sse_scalar_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x11, 0x01, C_ALL, "simd_fp_256.packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x11, 0x02, C_ALL, "simd_fp_256.packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x01, C_ALL, "arith.fpu_div_active" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x01, C_ALL, "arith.fpu_div" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x24, 0x01, C_ALL, "l2_rqsts.demand_data_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x03, C_ALL, "l2_rqsts.all_demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x04, C_ALL, "l2_rqsts.rfo_hits" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x08, C_ALL, "l2_rqsts.rfo_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x0C, C_ALL, "l2_rqsts.all_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x10, C_ALL, "l2_rqsts.code_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x20, C_ALL, "l2_rqsts.code_rd_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x30, C_ALL, "l2_rqsts.all_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x40, C_ALL, "l2_rqsts.pf_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x80, C_ALL, "l2_rqsts.pf_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xC0, C_ALL, "l2_rqsts.all_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x01, C_ALL, "l2_store_lock_rqsts.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x08, C_ALL, "l2_store_lock_rqsts.hit_m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x0F, C_ALL, "l2_store_lock_rqsts.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x01, C_ALL, "l2_l1d_wb_rqsts.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x04, C_ALL, "l2_l1d_wb_rqsts.hit_e" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x08, C_ALL, "l2_l1d_wb_rqsts.hit_m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x28, 0x0F, C_ALL, "l2_l1d_wb_rqsts.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x41, C_ALL, "longest_lat_cache.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x4F, C_ALL, "longest_lat_cache.reference" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x00, C_ALL, "cpu_clk_unhalted.thread_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x01, C_ALL, "cpu_clk_thread_unhalted.ref_xclk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C(2), "l1d_pend_miss.pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C(2), "l1d_pend_miss.pending_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C(2), "l1d_pend_miss.occurrences" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x49, 0x01, C_ALL, "dtlb_store_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x02, C_ALL, "dtlb_store_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x04, C_ALL, "dtlb_store_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x10, C_ALL, "dtlb_store_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x01, C_ALL, "load_hit_pre.sw_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x02, C_ALL, "load_hit_pre.hw_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x01, C_ALL, "l1d.replacement" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x04, C_ALL, "move_elimination.int_not_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x08, C_ALL, "move_elimination.simd_not_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x01, C_ALL, "move_elimination.int_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x02, C_ALL, "move_elimination.simd_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5C, 0x01, C_ALL, "cpl_cycles.ring0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5C, 0x01, C_ALL, "cpl_cycles.ring0_trans" , 0x0, ATTR_EDGE, 0x0 }, \ +{ 0x5C, 0x02, C_ALL, "cpl_cycles.ring123" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5E, 0x01, C_ALL, "rs_events.empty_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5F, 0x04, C_ALL, "dtlb_load_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.demand_data_rd_cycles", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x02, C_ALL, "offcore_requests_outstanding.demand_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x02, C_ALL, "offcore_requests_outstanding.demand_code_rd_cycles", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.demand_rfo_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.all_data_rd_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x01, C_ALL, "lock_cycles.split_lock_uc_lock_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x02, C_ALL, "lock_cycles.cache_lock_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x02, C_ALL, "idq.empty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_activations" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles_any_uops" /* synonym, from Intel SDM */ , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x20, C_ALL, "idq.ms_mite_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x20, C_ALL, "idq.ms_mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles_any_uops" /* synonym, from Intel SDM */ , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x3C, C_ALL, "idq.mite_all_uops" /* weird name suggested by Intel docs */ , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x3C, C_ALL, "idq.mite_all_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x02, C_ALL, "icache.misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x01, C_ALL, "itlb_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x02, C_ALL, "itlb_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x04, C_ALL, "itlb_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x10, C_ALL, "itlb_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x01, C_ALL, "ild_stall.lcp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x04, C_ALL, "ild_stall.iq_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x41, C_ALL, "br_inst_exec.nontaken_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x81, C_ALL, "br_inst_exec.taken_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x82, C_ALL, "br_inst_exec.taken_direct_jmp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x84, C_ALL, "br_inst_exec.taken_indirect_jmp_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x88, C_ALL, "br_inst_exec.taken_return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x90, C_ALL, "br_inst_exec.taken_direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xA0, C_ALL, "br_inst_exec.taken_indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xFF, C_ALL, "br_inst_exec.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x41, C_ALL, "br_misp_exec.nontaken_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x81, C_ALL, "br_misp_exec.taken_cond" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x84, C_ALL, "br_misp_exec.taken_indirect_jmp_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x88, C_ALL, "br_misp_exec.taken_return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x90, C_ALL, "br_misp_exec.taken_direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xA0, C_ALL, "br_misp_exec.taken_indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xFF, C_ALL, "br_misp_exec.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x01, C_ALL, "uops_dispatched_port.port_0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x02, C_ALL, "uops_dispatched_port.port_1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x04, C_ALL, "uops_dispatched_port.port_2_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x08, C_ALL, "uops_dispatched_port.port_2_sta" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x0C, C_ALL, "uops_dispatched_port.port_2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x10, C_ALL, "uops_dispatched_port.port_3_ld" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x20, C_ALL, "uops_dispatched_port.port_3_sta" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x30, C_ALL, "uops_dispatched_port.port_3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x40, C_ALL, "uops_dispatched_port.port_4" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x80, C_ALL, "uops_dispatched_port.port_5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x01, C_ALL, "resource_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x04, C_ALL, "resource_stalls.rs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x08, C_ALL, "resource_stalls.sb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x10, C_ALL, "resource_stalls.rob" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x01, C_ALL, "cycle_activity.cycles_l2_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x01, C_ALL, "cycle_activity.cycles_l2_pending_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA3, 0x02, C0|C1|C2|C3, "cycle_activity.cycles_ldm_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x02, C0|C1|C2|C3, "cycle_activity.cycles_ldm_pending_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA3, 0x08, C(2), "cycle_activity.cycles_l1d_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x08, C(2), "cycle_activity.cycles_l1d_pending_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA3, 0x04, C_ALL, "cycle_activity.cycles_no_execute" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x04, C_ALL, "cycle_activity.cycles_no_execute_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xAB, 0x01, C_ALL, "dsb2mite_switches.count" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAB, 0x02, C_ALL, "dsb2mite_switches.penalty_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAC, 0x08, C_ALL, "dsb_fill.exceed_dsb_lines" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAE, 0x01, C_ALL, "itlb.itlb_flush" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x01, C_ALL, "offcore_requests.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x02, C_ALL, "offcore_requests.demand_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x04, C_ALL, "offcore_requests.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x08, C_ALL, "offcore_requests.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.thread" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xB7, 0x01, C_ALL, "offcore_response_0" , 0x0, ATTR_NONE, 0x1A6 }, ignore events that require msr_offset */ \ +/* { 0xBB, 0x01, C_ALL, "offcore_response_1" , 0x0, ATTR_NONE, 0x1A7 }, ignore events that require msr_offset */ \ +{ 0xBD, 0x01, C_ALL, "tlb_flush.dtlb_thread" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBD, 0x20, C_ALL, "tlb_flush.stlb_any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x00, C_ALL, "inst_retired.any_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x01, C(1), "inst_retired.prec_dist" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x08, C_ALL, "other_assists.avx_store" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x10, C_ALL, "other_assists.avx_to_sse" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x20, C_ALL, "other_assists.sse_to_avx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.active_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xC2, 0x02, C_ALL, "uops_retired.retire_slots" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x02, C_ALL, "machine_clears.memory_ordering" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x04, C_ALL, "machine_clears.smc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x20, C_ALL, "machine_clears.maskmov" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x00, C_ALL, "br_inst_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x01, C_ALL, "br_inst_retired.conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x02, C_ALL, "br_inst_retired.near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x04, C_ALL, "br_inst_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x08, C_ALL, "br_inst_retired.near_return" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x10, C_ALL, "br_inst_retired.not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x20, C_ALL, "br_inst_retired.near_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x40, C_ALL, "br_inst_retired.far_branch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x00, C_ALL, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x01, C_ALL, "br_misp_retired.conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x02, C_ALL, "br_misp_retired.near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x04, C_ALL, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x10, C_ALL, "br_misp_retired.not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x20, C_ALL, "br_misp_retired.taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x02, C_ALL, "fp_assist.x87_output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x04, C_ALL, "fp_assist.x87_input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x08, C_ALL, "fp_assist.simd_output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x10, C_ALL, "fp_assist.simd_input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x1E, C_ALL, "fp_assist.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x20, C_ALL, "rob_misc_events.lbr_inserts" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xCD, 0x01, C3 , "mem_trans_retired.load_latency" , 0x0, ATTR_NONE, 0x3F6 }, ignore events that require msr_offset */ /* See Section "MSR_PEBS_LD_LAT_THRESHOLD" */ \ +{ 0xCD, 0x02, C3 , "mem_trans_retired.precise_store" , 0x0, ATTR_NONE, 0x0 }, /* See Section "Precise Store Facility" */ \ +{ 0xD0, 0x11, C_ALL, "mem_uops_retired.stlb_miss_loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x12, C_ALL, "mem_uops_retired.stlb_miss_stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x21, C_ALL, "mem_uops_retired.lock_loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x22, C_ALL, "mem_uops_retired.lock_stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x41, C_ALL, "mem_uops_retired.split_loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x42, C_ALL, "mem_uops_retired.split_stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x81, C_ALL, "mem_uops_retired.all_loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD0, 0x82, C_ALL, "mem_uops_retired.all_stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x01, C_ALL, "mem_load_uops_retired.l1_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x02, C_ALL, "mem_load_uops_retired.l2_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x04, C_ALL, "mem_load_uops_retired.llc_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x08, C_ALL, "mem_load_uops_retired.l1_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x10, C_ALL, "mem_load_uops_retired.l2_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x20, C_ALL, "mem_load_uops_retired.llc_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD1, 0x40, C_ALL, "mem_load_uops_retired.hit_lfb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x01, C_ALL, "mem_load_uops_llc_hit_retired.xsnp_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x02, C_ALL, "mem_load_uops_llc_hit_retired.xsnp_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x04, C_ALL, "mem_load_uops_llc_hit_retired.xsnp_hitm" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x08, C_ALL, "mem_load_uops_llc_hit_retired.xsnp_none" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD3, 0x01, C_ALL, "mem_load_uops_llc_miss_retired.local_dram" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xE6, 0x1F, C_ALL, "baclears.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x01, C_ALL, "l2_trans.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x02, C_ALL, "l2_trans.rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x04, C_ALL, "l2_trans.code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x08, C_ALL, "l2_trans.all_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x10, C_ALL, "l2_trans.l1d_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x20, C_ALL, "l2_trans.l2_fill" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x40, C_ALL, "l2_trans.l2_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x80, C_ALL, "l2_trans.all_requests" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x01, C_ALL, "l2_lines_in.i" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x02, C_ALL, "l2_lines_in.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x04, C_ALL, "l2_lines_in.e" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x07, C_ALL, "l2_lines_in.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x01, C_ALL, "l2_lines_out.demand_clean" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x02, C_ALL, "l2_lines_out.demand_dirty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x04, C_ALL, "l2_lines_out.pf_clean" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x08, C_ALL, "l2_lines_out.pf_dirty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x0A, C_ALL, "l2_lines_out.dirty_all" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +#define EVENTS_FAM6_MOD62_ONLY \ +{ 0xD3, 0x01, C_ALL, "mem_load_uops_llc_miss_retired.local_dram" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD3, 0x04, C_ALL, "mem_load_uops_llc_miss_retired.remote_dram" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD3, 0x10, C_ALL, "mem_load_uops_llc_miss_retired.remote_hitm" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD3, 0x20, C_ALL, "mem_load_uops_llc_miss_retired.remote_fwd" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +/* Intel Haswell Processor */ +/* + * The Haswell tables take into account Bug 17006019 + * libcpc counter names should be based on public Intel documentation -- Haswell + * and are basically from the + * Intel SDM, June 2013, Section 19.3, Table 19-2 and Table 19-3. + * We omit the Table 19-4 uncore events. + */ + +#define EVENTS_FAM6_MOD60 \ +{ 0x03, 0x02, C_ALL, "ld_blocks.store_forward" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x08, C_ALL, "ld_blocks.no_sr" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x05, 0x01, C_ALL, "misalign_mem_ref.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x05, 0x02, C_ALL, "misalign_mem_ref.stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x01, C_ALL, "ld_blocks_partial.address_alias" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x01, C_ALL, "dtlb_load_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x02, C_ALL, "dtlb_load_misses.walk_completed_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x04, C_ALL, "dtlb_load_misses.walk_completed_2m_4m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x0E, C_ALL, "dtlb_load_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x10, C_ALL, "dtlb_load_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x20, C_ALL, "dtlb_load_misses.stlb_hit_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x40, C_ALL, "dtlb_load_misses.stlb_hit_2m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x60, C_ALL, "dtlb_load_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x80, C_ALL, "dtlb_load_misses.pde_cache_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0D, 0x03, C_ALL, "int_misc.recovery_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x0D, 0x03, C_ALL, "int_misc.recovery_cycles_occurrences" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.core_stall_cycles" , 0x1, ATTR_INV | ATTR_ANY, 0x0 }, \ +{ 0x0E, 0x10, C_ALL, "uops_issued.flags_merge" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x20, C_ALL, "uops_issued.slow_lea" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x40, C_ALL, "uops_issued.single_mul" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x21, C_ALL, "l2_rqsts.demand_data_rd_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x22, C_ALL, "l2_rqsts.rfo_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x24, C_ALL, "l2_rqsts.code_rd_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x27, C_ALL, "l2_rqsts.all_demand_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x30, C_ALL, "l2_rqsts.l2_pf_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x3F, C_ALL, "l2_rqsts.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x41, C_ALL, "l2_rqsts.demand_data_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x42, C_ALL, "l2_rqsts.rfo_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x44, C_ALL, "l2_rqsts.code_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x50, C_ALL, "l2_rqsts.l2_pf_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE1, C_ALL, "l2_rqsts.all_demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE2, C_ALL, "l2_rqsts.all_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE4, C_ALL, "l2_rqsts.all_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE7, C_ALL, "l2_rqsts.all_demand_references" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xF8, C_ALL, "l2_rqsts.all_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xFF, C_ALL, "l2_rqsts.references" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x27, 0x50, C_ALL, "l2_demand_rqsts.wb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x4F, C_ALL, "longest_lat_cache.reference" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2E, 0x41, C_ALL, "longest_lat_cache.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x00, C_ALL, "cpu_clk_unhalted.thread_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x01, C_ALL, "cpu_clk_thread_unhalted.ref_xclk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C(2) , "l1d_pend_miss.pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C(2) , "l1d_pend_miss.pending_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C(2) , "l1d_pend_miss.occurences" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x49, 0x01, C_ALL, "dtlb_store_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x02, C_ALL, "dtlb_store_misses.walk_completed_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x04, C_ALL, "dtlb_store_misses.walk_completed_2m_4m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x0E, C_ALL, "dtlb_store_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x10, C_ALL, "dtlb_store_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x20, C_ALL, "dtlb_store_misses.stlb_hit_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x40, C_ALL, "dtlb_store_misses.stlb_hit_2m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x60, C_ALL, "dtlb_store_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x80, C_ALL, "dtlb_store_misses.pde_cache_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x01, C_ALL, "load_hit_pre.sw_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x02, C_ALL, "load_hit_pre.hw_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x01, C_ALL, "l1d.replacement" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x54, 0x01, C_ALL, "tx_mem.abort_conflict" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x54, 0x02, C_ALL, "tx_mem.abort_capacity" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x54, 0x04, C_ALL, "tx_mem.abort_hle_store_to_elided_lock" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x54, 0x08, C_ALL, "tx_mem.abort_hle_elision_buffer_not_empty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x54, 0x10, C_ALL, "tx_mem.abort_hle_elision_buffer_mismatch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x54, 0x20, C_ALL, "tx_mem.abort_hle_elision_buffer_unsupported_alignment" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x54, 0x40, C_ALL, "tx_mem.abort_hle_elision_buffer_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x01, C_ALL, "move_elimination.int_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x02, C_ALL, "move_elimination.simd_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x04, C_ALL, "move_elimination.int_not_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x08, C_ALL, "move_elimination.simd_not_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5C, 0x01, C_ALL, "cpl_cycles.ring0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5C, 0x01, C_ALL, "cpl_cycles.ring0_trans" , 0x0, ATTR_EDGE, 0x0 }, \ +{ 0x5C, 0x02, C_ALL, "cpl_cycles.ring123" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5D, 0x01, C_ALL, "tx_exec.misc1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5D, 0x02, C_ALL, "tx_exec.misc2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5D, 0x04, C_ALL, "tx_exec.misc3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5D, 0x08, C_ALL, "tx_exec.misc4" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5D, 0x10, C_ALL, "tx_exec.misc5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5E, 0x01, C_ALL, "rs_events.empty_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.cycles_with_demand_data_rd", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x02, C_ALL, "offcore_requests_outstanding.demand_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x02, C_ALL, "offcore_requests_outstanding.demand_code_rd_cycles", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.demand_rfo_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.cycles_with_data_rd" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x01, C_ALL, "lock_cycles.split_lock_uc_lock_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x02, C_ALL, "lock_cycles.cache_lock_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x02, C_ALL, "idq.empty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x20, C_ALL, "idq.ms_mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x20, C_ALL, "idq.ms_mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles_any_uops" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_occur" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles_any_uops" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x3C, C_ALL, "idq.mite_all_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x02, C_ALL, "icache.misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x01, C_ALL, "itlb_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x02, C_ALL, "itlb_misses.walk_completed_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x04, C_ALL, "itlb_misses.walk_completed_2m_4m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x0E, C_ALL, "itlb_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x10, C_ALL, "itlb_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x20, C_ALL, "itlb_misses.stlb_hit_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x40, C_ALL, "itlb_misses.stlb_hit_2m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x60, C_ALL, "itlb_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x01, C_ALL, "ild_stall.lcp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x04, C_ALL, "ild_stall.iq_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x41, C_ALL, "br_inst_exec.nontaken_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x81, C_ALL, "br_inst_exec.taken_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x82, C_ALL, "br_inst_exec.taken_direct_jump" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x84, C_ALL, "br_inst_exec.taken_indirect_jump_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x88, C_ALL, "br_inst_exec.taken_indirect_near_return" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x90, C_ALL, "br_inst_exec.taken_direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xA0, C_ALL, "br_inst_exec.taken_indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xFF, C_ALL, "br_inst_exec.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x41, C_ALL, "br_misp_exec.nontaken_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x81, C_ALL, "br_misp_exec.taken_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x84, C_ALL, "br_misp_exec.taken_indirect_jump_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x88, C_ALL, "br_misp_exec.taken_return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x90, C_ALL, "br_misp_exec.taken_direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xA0, C_ALL, "br_misp_exec.taken_indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xFF, C_ALL, "br_misp_exec.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_0_uops_deliv.core" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_le_1_uop_deliv.core" , 0x3, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_le_2_uop_deliv.core" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_le_3_uop_deliv.core" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_fe_was_ok" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x01, C_ALL, "uops_executed_port.port_0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x02, C_ALL, "uops_executed_port.port_1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x04, C_ALL, "uops_executed_port.port_2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x08, C_ALL, "uops_executed_port.port_3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x10, C_ALL, "uops_executed_port.port_4" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x20, C_ALL, "uops_executed_port.port_5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x40, C_ALL, "uops_executed_port.port_6" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x80, C_ALL, "uops_executed_port.port_7" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x01, C_ALL, "uops_executed_port.port_0_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x02, C_ALL, "uops_executed_port.port_1_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x04, C_ALL, "uops_executed_port.port_2_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x08, C_ALL, "uops_executed_port.port_3_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x10, C_ALL, "uops_executed_port.port_4_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x20, C_ALL, "uops_executed_port.port_5_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x40, C_ALL, "uops_executed_port.port_6_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x80, C_ALL, "uops_executed_port.port_7_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA2, 0x01, C_ALL, "resource_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x04, C_ALL, "resource_stalls.rs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x08, C_ALL, "resource_stalls.sb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x10, C_ALL, "resource_stalls.rob" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x01, C_ALL, "cycle_activity.cycles_l2_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x01, C_ALL, "cycle_activity.cycles_l2_pending_cycles" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x02, C_ALL, "cycle_activity.cycles_ldm_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x02, C_ALL, "cycle_activity.cycles_ldm_pending_cycles" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x05, C_ALL, "cycle_activity.stalls_l2_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x08, C(2) , "cycle_activity.cycles_l1d_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x08, C(2) , "cycle_activity.cycles_l1d_pending_cycles" , 0x8, ATTR_NONE, 0x0 }, \ +{ 0xA8, 0x01, C_ALL, "lsd.uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAE, 0x01, C_ALL, "itlb.itlb_flush" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x01, C_ALL, "offcore_requests.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x02, C_ALL, "offcore_requests.demand_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x04, C_ALL, "offcore_requests.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x08, C_ALL, "offcore_requests.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xB7, 0x01, C_ALL, "off_core_response_0" , 0x0, ATTR_NONE, 0x1A6 }, omit events requiring MSR programming */ \ +/* { 0xBB, 0x01, C_ALL, "off_core_response_1" , 0x0, ATTR_NONE, 0x1A7 }, omit events requiring MSR programming */ \ +{ 0xBC, 0x11, C_ALL, "page_walker_loads.dtlb_l1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x21, C_ALL, "page_walker_loads.itlb_l1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x12, C_ALL, "page_walker_loads.dtlb_l2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x22, C_ALL, "page_walker_loads.itlb_l2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x14, C_ALL, "page_walker_loads.dtlb_l3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x24, C_ALL, "page_walker_loads.itlb_l3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x18, C_ALL, "page_walker_loads.dtlb_memory" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x28, C_ALL, "page_walker_loads.itlb_memory" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBD, 0x01, C_ALL, "tlb_flush.dtlb_thread" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBD, 0x20, C_ALL, "tlb_flush.stlb_any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x00, C_ALL, "inst_retired.any_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x01, C(1) , "inst_retired.prec_dist" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x08, C_ALL, "other_assists.avx_to_sse" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x10, C_ALL, "other_assists.sse_to_avx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x40, C_ALL, "other_assists.any_wb_assist" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.all" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xC2, 0x02, C_ALL, "uops_retired.retire_slots" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC3, 0x02, C_ALL, "machine_clears.memory_ordering" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x04, C_ALL, "machine_clears.smc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x20, C_ALL, "machine_clears.maskmov" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x00, C_ALL, "br_inst_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x01, C_ALL, "br_inst_retired.conditional" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x02, C_ALL, "br_inst_retired.near_call" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x04, C_ALL, "br_inst_retired.all_branches" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x08, C_ALL, "br_inst_retired.near_return" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x10, C_ALL, "br_inst_retired.not_taken" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x20, C_ALL, "br_inst_retired.near_taken" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x40, C_ALL, "br_inst_retired.far_branch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x00, C_ALL, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x01, C_ALL, "br_misp_retired.conditional" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC5, 0x04, C_ALL, "br_misp_retired.all_branches" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC5, 0x20, C_ALL, "br_misp_retired.near_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x01, C_ALL, "hle_retired.start" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x02, C_ALL, "hle_retired.commit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x04, C_ALL, "hle_retired.aborted" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC8, 0x08, C_ALL, "hle_retired.aborted_misc1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x10, C_ALL, "hle_retired.aborted_misc2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x20, C_ALL, "hle_retired.aborted_misc3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x40, C_ALL, "hle_retired.aborted_misc4" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x80, C_ALL, "hle_retired.aborted_misc5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC9, 0x01, C_ALL, "rtm_retired.start" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC9, 0x02, C_ALL, "rtm_retired.commit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC9, 0x04, C_ALL, "rtm_retired.aborted" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC9, 0x08, C_ALL, "rtm_retired.aborted_misc1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC9, 0x10, C_ALL, "rtm_retired.aborted_misc2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC9, 0x20, C_ALL, "rtm_retired.aborted_misc3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC9, 0x40, C_ALL, "rtm_retired.aborted_misc4" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC9, 0x80, C_ALL, "rtm_retired.aborted_misc5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x02, C_ALL, "fp_assist.x87_output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x04, C_ALL, "fp_assist.x87_input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x08, C_ALL, "fp_assist.simd_output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x10, C_ALL, "fp_assist.simd_input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x1E, C_ALL, "fp_assist.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x20, C_ALL, "rob_misc_events.lbr_inserts" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xCD, 0x01, C_ALL, "mem_trans_retired.load_latency" , 0x0, ATTR_NONE, 0x3F6 }, omit events requiring MSR programming */ \ +{ 0xD0, 0x11, C_ALL, "mem_uops_retired.stlb_miss_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x12, C_ALL, "mem_uops_retired.stlb_miss_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x21, C_ALL, "mem_uops_retired.lock_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x22, C_ALL, "mem_uops_retired.lock_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x41, C_ALL, "mem_uops_retired.split_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x42, C_ALL, "mem_uops_retired.split_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x81, C_ALL, "mem_uops_retired.all_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x82, C_ALL, "mem_uops_retired.all_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x01, C_ALL, "mem_load_uops_retired.l1_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x02, C_ALL, "mem_load_uops_retired.l2_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x04, C_ALL, "mem_load_uops_retired.l3_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x08, C_ALL, "mem_load_uops_retired.l1_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x10, C_ALL, "mem_load_uops_retired.l2_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x20, C_ALL, "mem_load_uops_retired.l3_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x40, C_ALL, "mem_load_uops_retired.hit_lfb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xD2, 0x01, C_ALL, "mem_load_uops_l3_hit_retired.xsnp_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x02, C_ALL, "mem_load_uops_l3_hit_retired.xsnp_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x04, C_ALL, "mem_load_uops_l3_hit_retired.xsnp_hitm" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x08, C_ALL, "mem_load_uops_l3_hit_retired.xsnp_none" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD3, 0x01, C_ALL, "mem_load_uops_l3_miss_retired.local_dram" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xE6, 0x1F, C_ALL, "baclears.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x01, C_ALL, "l2_trans.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x02, C_ALL, "l2_trans.rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x04, C_ALL, "l2_trans.code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x08, C_ALL, "l2_trans.all_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x10, C_ALL, "l2_trans.l1d_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x20, C_ALL, "l2_trans.l2_fill" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x40, C_ALL, "l2_trans.l2_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x80, C_ALL, "l2_trans.all_requests" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x01, C_ALL, "l2_lines_in.i" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x02, C_ALL, "l2_lines_in.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x04, C_ALL, "l2_lines_in.e" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x07, C_ALL, "l2_lines_in.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x05, C_ALL, "l2_lines_out.demand_clean" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x06, C_ALL, "l2_lines_out.demand_dirty" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +/* Intel Broadwell Processor */ +/* + * This table is essentially taken from: + * https://grok.cz.oracle.com/source/xref/on12-clone/usr/src/uts/intel/pcbe/bdw_pcbe_tbl.c + */ + +#define EVENTS_FAM6_MOD61 \ +{ 0x03, 0x02, C_ALL, "ld_blocks.store_forward" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x08, C_ALL, "ld_blocks.no_sr" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x05, 0x01, C_ALL, "misalign_mem_ref.loads" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x05, 0x02, C_ALL, "misalign_mem_ref.stores" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x07, 0x01, C_ALL, "ld_blocks_partial.address_alias" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x08, 0x01, C_ALL, "dtlb_load_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x02, C_ALL, "dtlb_load_misses.walk_completed_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x04, C_ALL, "dtlb_load_misses.walk_completed_2m_4m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x0E, C_ALL, "dtlb_load_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x10, C_ALL, "dtlb_load_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x20, C_ALL, "dtlb_load_misses.stlb_hit_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x40, C_ALL, "dtlb_load_misses.stlb_hit_2m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x60, C_ALL, "dtlb_load_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x80, C_ALL, "dtlb_load_misses.pde_cache_miss" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x0D, 0x03, C_ALL, "int_misc.recovery_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x0D, 0x03, C_ALL, "int_misc.recovery_cycles_any" , 0x1, ATTR_ANY , 0x0 }, \ +/* Private event, not public by Intel */ \ +{ 0x0D, 0x03, C_ALL, "int_misc.recovery_cycles_occurrences" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x0D, 0x08, C_ALL, "int_misc.rat_stall_cycles" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x0E, 0x01, C_ALL, "uops_issued.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x10, C_ALL, "uops_issued.flags_merge" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x20, C_ALL, "uops_issued.slow_lea" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x40, C_ALL, "uops_issued.single_mul" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.core_stall_cycles" , 0x1,(ATTR_INV | ATTR_ANY), 0x0 }, \ + \ +{ 0x14, 0x01, C_ALL, "arith.fpu_div_active" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x24, 0x21, C_ALL, "l2_rqsts.demand_data_rd_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x41, C_ALL, "l2_rqsts.demand_data_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x30, C_ALL, "l2_rqsts.l2_pf_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x50, C_ALL, "l2_rqsts.l2_pf_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE1, C_ALL, "l2_rqsts.all_demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE2, C_ALL, "l2_rqsts.all_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE4, C_ALL, "l2_rqsts.all_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xF8, C_ALL, "l2_rqsts.all_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x42, C_ALL, "l2_rqsts.rfo_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x22, C_ALL, "l2_rqsts.rfo_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x44, C_ALL, "l2_rqsts.code_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x24, C_ALL, "l2_rqsts.code_rd_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x27, C_ALL, "l2_rqsts.all_demand_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE7, C_ALL, "l2_rqsts.all_demand_references" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x3F, C_ALL, "l2_rqsts.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xFF, C_ALL, "l2_rqsts.references" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x27, 0x50, C_ALL, "l2_demand_rqsts.wb_hit" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x3C, 0x00, C_ALL, "cpu_clk_unhalted.thread_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x00, C_ALL, "cpu_clk_unhalted.thread_p_any" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0x3C, 0x01, C_ALL, "cpu_clk_thread_unhalted.ref_xclk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x01, C_ALL, "cpu_clk_thread_unhalted.ref_xclk_any" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0x3C, 0x02, C_ALL, "cpu_clk_thread_unhalted.one_thread_active" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x48, 0x01, C(2) , "l1d_pend_miss.pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C(2) , "l1d_pend_miss.pending_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C(2) , "l1d_pend_miss.pending_cycles_any" , 0x1, ATTR_ANY , 0x0 }, \ +/* Private event, not public by Intel */ \ +{ 0x48, 0x01, C(2) , "l1d_pend_miss.occurences" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x48, 0x02, C_ALL, "l1d_pend_miss.fb_full" , 0x1, ATTR_NONE, 0x0 }, \ + \ +{ 0x49, 0x01, C_ALL, "dtlb_store_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x02, C_ALL, "dtlb_store_misses.walk_completed_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x04, C_ALL, "dtlb_store_misses.walk_completed_2m_4m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x0E, C_ALL, "dtlb_store_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x10, C_ALL, "dtlb_store_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x20, C_ALL, "dtlb_store_misses.stlb_hit_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x40, C_ALL, "dtlb_store_misses.stlb_hit_2m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x60, C_ALL, "dtlb_store_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x80, C_ALL, "dtlb_store_misses.pde_cache_miss" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x4C, 0x01, C_ALL, "load_hit_pre.sw_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x02, C_ALL, "load_hit_pre.hw_pf" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x4F, 0x10, C_ALL, "ept.walk_cycles" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x51, 0x01, C_ALL, "l1d.replacement" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x54, 0x01, C_ALL, "tx_mem.abort_conflict" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x54, 0x02, C_ALL, "tx_mem.abort_capacity_write" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x54, 0x04, C_ALL, "tx_mem.abort_hle_store_to_elided_lock" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x54, 0x08, C_ALL, "tx_mem.abort_hle_elision_buffer_not_empty" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x54, 0x10, C_ALL, "tx_mem.abort_hle_elision_buffer_mismatch" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x54, 0x20, C_ALL, "tx_mem.abort_hle_elision_buffer_unsupported_alignment" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x54, 0x40, C_ALL, "tx_mem.hle_elision_buffer_full" , 0x0, ATTR_TSX , 0x0 }, \ + \ +{ 0x58, 0x01, C_ALL, "move_elimination.int_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x02, C_ALL, "move_elimination.simd_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x04, C_ALL, "move_elimination.int_not_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x58, 0x08, C_ALL, "move_elimination.simd_not_eliminated" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x5C, 0x01, C_ALL, "cpl_cycles.ring0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5C, 0x01, C_ALL, "cpl_cycles.ring0_trans" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x5C, 0x02, C_ALL, "cpl_cycles.ring123" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x5D, 0x01, C_ALL, "tx_exec.misc1" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x5D, 0x02, C_ALL, "tx_exec.misc2" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x5D, 0x04, C_ALL, "tx_exec.misc3" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x5D, 0x08, C_ALL, "tx_exec.misc4" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0x5D, 0x10, C_ALL, "tx_exec.misc5" , 0x0, ATTR_TSX , 0x0 }, \ + \ +{ 0x5E, 0x01, C_ALL, "rs_events.empty_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5E, 0x01, C_ALL, "rs_events.empty_end" , 0x1, (ATTR_INV | ATTR_EDGE), 0x0 }, \ + \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.cycles_with_demand_data_rd", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.demand_data_rd_ge_6 " , 0x6, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x02, C_ALL, "offcore_requests_outstanding.demand_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +/* Private event, not public by Intel */ \ +{ 0x60, 0x02, C_ALL, "offcore_requests_outstanding.demand_code_rd_cycles", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +/* Private event, not public by Intel */ \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.demand_rfo_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.cycles_with_data_rd" , 0x1, ATTR_NONE, 0x0 }, \ + \ +{ 0x63, 0x01, C_ALL, "lock_cycles.split_lock_uc_lock_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x63, 0x02, C_ALL, "lock_cycles.cache_lock_duration" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x79, 0x02, C_ALL, "idq.empty" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_occur" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles_any_uops" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x20, C_ALL, "idq.ms_mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles_any_uops" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_switches" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x79, 0x3C, C_ALL, "idq.mite_all_uops" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x80, 0x01, C_ALL, "icache.hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x02, C_ALL, "icache.misses" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x80, 0x04, C_ALL, "icache.ifdata_stall" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x85, 0x01, C_ALL, "itlb_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x02, C_ALL, "itlb_misses.walk_completed_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x04, C_ALL, "itlb_misses.walk_completed_2m_4m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x0E, C_ALL, "itlb_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x10, C_ALL, "itlb_misses.walk_duration" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x20, C_ALL, "itlb_misses.stlb_hit_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x40, C_ALL, "itlb_misses.stlb_hit_2m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x60, C_ALL, "itlb_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x87, 0x01, C_ALL, "ild_stall.lcp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x04, C_ALL, "ild_stall.iq_full" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x88, 0x41, C_ALL, "br_inst_exec.nontaken_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x81, C_ALL, "br_inst_exec.taken_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x82, C_ALL, "br_inst_exec.taken_direct_jump" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x84, C_ALL, "br_inst_exec.taken_indirect_jump_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x88, C_ALL, "br_inst_exec.taken_indirect_near_return" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0x90, C_ALL, "br_inst_exec.taken_direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xA0, C_ALL, "br_inst_exec.taken_indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xC1, C_ALL, "br_inst_exec.all_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xC2, C_ALL, "br_inst_exec.all_direct_jmp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xC4, C_ALL, "br_inst_exec.all_indirect_jump_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xC8, C_ALL, "br_inst_exec.all_indirect_near_return" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xD0, C_ALL, "br_inst_exec.all_direct_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x88, 0xFF, C_ALL, "br_inst_exec.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0x89, 0x41, C_ALL, "br_misp_exec.nontaken_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x81, C_ALL, "br_misp_exec.taken_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0x84, C_ALL, "br_misp_exec.taken_indirect_jump_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +/* Private event, not public by Intel */ \ +{ 0x89, 0x88, C_ALL, "br_misp_exec.taken_return_near" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xC1, C_ALL, "br_misp_exec.all_conditional" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xC4, C_ALL, "br_misp_exec.all_indirect_jump_non_call_ret" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xA0, C_ALL, "br_misp_exec.taken_indirect_near_call" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x89, 0xFF, C_ALL, "br_misp_exec.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ + \ +/* Use Cmask to qualify uop b/w */ \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_0_uops_deliv.core" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_le_1_uop_deliv.core" , 0x3, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_le_2_uop_deliv.core" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_le_3_uop_deliv.core" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_fe_was_ok" , 0x1, ATTR_INV , 0x0 }, \ + \ +{ 0xA0, 0x03, C_ALL, "uop_dispatches_cancelled.simd_prf" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xA1, 0x01, C_ALL, "uops_executed_port.port_0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x02, C_ALL, "uops_executed_port.port_1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x04, C_ALL, "uops_executed_port.port_2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x08, C_ALL, "uops_executed_port.port_3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x10, C_ALL, "uops_executed_port.port_4" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x20, C_ALL, "uops_executed_port.port_5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x40, C_ALL, "uops_executed_port.port_6" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x80, C_ALL, "uops_executed_port.port_7" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x01, C_ALL, "uops_executed_port.port_0_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x02, C_ALL, "uops_executed_port.port_1_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x04, C_ALL, "uops_executed_port.port_2_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x08, C_ALL, "uops_executed_port.port_3_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x10, C_ALL, "uops_executed_port.port_4_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x20, C_ALL, "uops_executed_port.port_5_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x40, C_ALL, "uops_executed_port.port_6_core" , 0x0, ATTR_ANY , 0x0 }, \ +{ 0xA1, 0x80, C_ALL, "uops_executed_port.port_7_core" , 0x0, ATTR_ANY , 0x0 }, \ + \ +{ 0xA2, 0x01, C_ALL, "resource_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x04, C_ALL, "resource_stalls.rs" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x08, C_ALL, "resource_stalls.sb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x10, C_ALL, "resource_stalls.rob" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xA3, 0x01, C_ALL, "cycle_activity.cycles_l2_pending" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x02, C_ALL, "cycle_activity.cycles_ldm_pending" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x04, C_ALL, "cycle_activity.cycles_no_execute" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x05, C_ALL, "cycle_activity.stalls_l2_pending" , 0x5, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x06, C_ALL, "cycle_activity.stalls_ldm_pending" , 0x6, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x08, C(2) , "cycle_activity.cycles_l1d_pending" , 0x8, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x0C, C(2) , "cycle_activity.stalls_l1d_pending" , 0xC, ATTR_NONE, 0x0 }, \ + \ +{ 0xA8, 0x01, C_ALL, "lsd.uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA8, 0x01, C_ALL, "lsd.cycles_active" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xA8, 0x01, C_ALL, "lsd.cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ + \ +{ 0xAB, 0x02, C_ALL, "dsb2mite_switches.penalty_cycles" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xAE, 0x01, C_ALL, "itlb.itlb_flush" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xB0, 0x01, C_ALL, "offcore_requests.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x02, C_ALL, "offcore_requests.demand_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x04, C_ALL, "offcore_requests.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x08, C_ALL, "offcore_requests.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xB1, 0x01, C_ALL, "uops_executed.thread" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.cycles_ge_1_uop_exec" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.cycles_ge_2_uops_exec" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.cycles_ge_3_uops_exec" , 0x3, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.cycles_ge_4_uops_exec" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_none" , 0x0, ATTR_INV , 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_ge_1" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_ge_2" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_ge_3" , 0x3, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_ge_4" , 0x4, ATTR_NONE, 0x0 }, \ + \ +{ 0xB2, 0x01, C_ALL, "offcore_requests_buffer.sq_full" , 0x0, ATTR_NONE, 0x0 }, \ + \ +/* \ + * See Section "Off-core Response Performance Monitoring" \ + * \ + * Though these two off_core events support all counters, only 1 of \ + * them can be used at any given time. This is due to the extra MSR \ + * programming required. \ + */ \ +/* { 0xB7, 0x01, C_ALL, "offcore_response_0" , 0x0, ATTR_NONE, OFFCORE_RSP_0 }, omit events requiring MSR programming */ \ +/* { 0xBB, 0x01, C_ALL, "offcore_response_1" , 0x0, ATTR_NONE, OFFCORE_RSP_1 }, omit events requiring MSR programming */ \ + \ +{ 0xBC, 0x11, C_ALL, "page_walker_loads.dtlb_l1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x21, C_ALL, "page_walker_loads.itlb_l1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x12, C_ALL, "page_walker_loads.dtlb_l2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x22, C_ALL, "page_walker_loads.itlb_l2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x14, C_ALL, "page_walker_loads.dtlb_l3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x24, C_ALL, "page_walker_loads.itlb_l3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBC, 0x18, C_ALL, "page_walker_loads.dtlb_memory" , 0x0, ATTR_NONE, 0x0 }, \ +/* itlb_memory is not in the Intel SDM or spreadsheet for Broadwell; "cputrack -h" does have it though */ \ +{ 0xBC, 0x28, C_ALL, "page_walker_loads.itlb_memory" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xBD, 0x01, C_ALL, "tlb_flush.dtlb_thread" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBD, 0x20, C_ALL, "tlb_flush.stlb_any" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xC0, 0x00, C_ALL, "inst_retired.any_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC0, 0x02, C_ALL, "inst_retired.x87" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xC1, 0x08, C_ALL, "other_assists.avx_to_sse" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x10, C_ALL, "other_assists.sse_to_avx" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC1, 0x40, C_ALL, "other_assists.any_wb_assist" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xC2, 0x01, C_ALL, "uops_retired.all" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.stall_cycles" , 0x1, ATTR_INV , 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.total_cycles" , 0xA, ATTR_INV , 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.core_stall_cycles" , 0x1, (ATTR_INV | ATTR_ANY), 0x0 }, \ +{ 0xC2, 0x02, C_ALL, "uops_retired.retire_slots" , 0x0, ATTR_PEBS, 0x0 }, \ + \ +{ 0xC3, 0x01, C_ALL, "machine_clears.cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x01, C_ALL, "machine_clears.count" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0xC3, 0x02, C_ALL, "machine_clears.memory_ordering" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x04, C_ALL, "machine_clears.smc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x20, C_ALL, "machine_clears.maskmov" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xC4, 0x01, C_ALL, "br_inst_retired.conditional" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x02, C_ALL, "br_inst_retired.near_call" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x08, C_ALL, "br_inst_retired.near_return" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x10, C_ALL, "br_inst_retired.not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x20, C_ALL, "br_inst_retired.near_taken" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x40, C_ALL, "br_inst_retired.far_branch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x02, C_ALL, "br_inst_retired.near_call_r3" , 0x0, ATTR_PEBS, 0x0 }, \ + \ +{ 0xC5, 0x01, C_ALL, "br_misp_retired.conditional" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC5, 0x20, C_ALL, "br_misp_retired.near_taken" , 0x0, ATTR_PEBS, 0x0 }, \ + \ +{ 0xC7, 0x01, C_ALL, "fp_arith_inst_retired.scalar_double" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC7, 0x02, C_ALL, "fp_arith_inst_retired.scalar_single" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC7, 0x03, C_ALL, "fp_arith_inst_retired.scalar" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC7, 0x04, C_ALL, "fp_arith_inst_retired.128b_packed_double" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC7, 0x08, C_ALL, "fp_arith_inst_retired.128b_packed_single" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC7, 0x10, C_ALL, "fp_arith_inst_retired.256b_packed_double" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC7, 0x15, C_ALL, "fp_arith_inst_retired.double" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC7, 0x20, C_ALL, "fp_arith_inst_retired.256b_packed_single" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC7, 0x2A, C_ALL, "fp_arith_inst_retired.single" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC7, 0x3C, C_ALL, "fp_arith_inst_retired.packed" , 0x0, ATTR_PEBS, 0x0 }, \ + \ +{ 0xC8, 0x01, C_ALL, "hle_retired.start" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC8, 0x02, C_ALL, "hle_retired.commit" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC8, 0x04, C_ALL, "hle_retired.aborted" , 0x0, ATTR_PEBS | ATTR_TSX, 0x0 }, \ +{ 0xC8, 0x08, C_ALL, "hle_retired.aborted_misc1" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC8, 0x10, C_ALL, "hle_retired.aborted_misc2" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC8, 0x20, C_ALL, "hle_retired.aborted_misc3" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC8, 0x40, C_ALL, "hle_retired.aborted_misc4" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC8, 0x80, C_ALL, "hle_retired.aborted_misc5" , 0x0, ATTR_TSX , 0x0 }, \ + \ +{ 0xC9, 0x01, C_ALL, "rtm_retired.start" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC9, 0x02, C_ALL, "rtm_retired.commit" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC9, 0x04, C_ALL, "rtm_retired.aborted" , 0x0, ATTR_PEBS | ATTR_TSX, 0x0 }, \ +{ 0xC9, 0x08, C_ALL, "rtm_retired.aborted_misc1" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC9, 0x10, C_ALL, "rtm_retired.aborted_misc2" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC9, 0x20, C_ALL, "rtm_retired.aborted_misc3" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC9, 0x40, C_ALL, "rtm_retired.aborted_misc4" , 0x0, ATTR_TSX , 0x0 }, \ +{ 0xC9, 0x80, C_ALL, "rtm_retired.aborted_misc5" , 0x0, ATTR_TSX , 0x0 }, \ + \ +{ 0xCA, 0x02, C_ALL, "fp_assist.x87_output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x04, C_ALL, "fp_assist.x87_input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x08, C_ALL, "fp_assist.simd_output" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x10, C_ALL, "fp_assist.simd_input" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCA, 0x1E, C_ALL, "fp_assist.any" , 0x1, ATTR_NONE, 0x0 }, \ + \ +{ 0xCC, 0x20, C_ALL, "rob_misc_events.lbr_inserts" , 0x0, ATTR_NONE, 0x0 }, \ + \ +/* See Section "MSR_PEBS_LD_LAT_THRESHOLD" */ \ +/* { 0xCD, 0x01, C(3) , "mem_trans_retired.load_latency" , 0x0, ATTR_PEBS_ONLY_LD_LAT, PEBS_LD_LAT_THRESHOLD }, omit events requiring MSR programming */ \ + \ +/* \ + * Event 0xD0 must be combined with umasks 0x1(loads) or 0x2(stores) \ + */ \ +{ 0xD0, 0x11, C_ALL, "mem_uops_retired.stlb_miss_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x12, C_ALL, "mem_uops_retired.stlb_miss_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x21, C_ALL, "mem_uops_retired.lock_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +/* Private event, not public by Intel */ \ +{ 0xD0, 0x22, C_ALL, "mem_uops_retired.lock_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x41, C_ALL, "mem_uops_retired.split_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x42, C_ALL, "mem_uops_retired.split_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x81, C_ALL, "mem_uops_retired.all_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x82, C_ALL, "mem_uops_retired.all_stores" , 0x0, ATTR_PEBS, 0x0 }, \ + \ +{ 0xD1, 0x01, C_ALL, "mem_load_uops_retired.l1_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x02, C_ALL, "mem_load_uops_retired.l2_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x04, C_ALL, "mem_load_uops_retired.l3_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x08, C_ALL, "mem_load_uops_retired.l1_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x10, C_ALL, "mem_load_uops_retired.l2_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x20, C_ALL, "mem_load_uops_retired.l3_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x40, C_ALL, "mem_load_uops_retired.hit_lfb" , 0x0, ATTR_PEBS, 0x0 }, \ + \ +{ 0xD2, 0x01, C_ALL, "mem_load_uops_l3_hit_retired.xsnp_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x02, C_ALL, "mem_load_uops_l3_hit_retired.xsnp_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x04, C_ALL, "mem_load_uops_l3_hit_retired.xsnp_hitm" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x08, C_ALL, "mem_load_uops_l3_hit_retired.xsnp_none" , 0x0, ATTR_PEBS, 0x0 }, \ + \ +{ 0xD3, 0x01, C_ALL, "mem_load_uops_l3_miss_retired.local_dram" , 0x0, ATTR_PEBS, 0x0 }, \ + \ +/* The mem_load_l4_miss_retired events are not in "cputrack -h" output nor in the Intel spreadsheet. */ \ +/* { 0xD5, 0x01, C_ALL, "mem_load_l4_miss_retired.local_hit" , 0x0, ATTR_NONE, 0x0 }, */ \ +/* { 0xD5, 0x04, C_ALL, "mem_load_l4_miss_retired.local_miss" , 0x0, ATTR_NONE, 0x0 }, */ \ + \ +{ 0xE6, 0x1F, C_ALL, "baclears.any" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xF0, 0x01, C_ALL, "l2_trans.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x02, C_ALL, "l2_trans.rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x04, C_ALL, "l2_trans.code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x08, C_ALL, "l2_trans.all_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x10, C_ALL, "l2_trans.l1d_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x20, C_ALL, "l2_trans.l2_fill" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x40, C_ALL, "l2_trans.l2_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x80, C_ALL, "l2_trans.all_requests" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xF1, 0x01, C_ALL, "l2_lines_in.i" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x02, C_ALL, "l2_lines_in.s" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x04, C_ALL, "l2_lines_in.e" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x07, C_ALL, "l2_lines_in.all" , 0x0, ATTR_NONE, 0x0 }, \ + \ +{ 0xF2, 0x05, C_ALL, "l2_lines_out.demand_clean" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + + +/* Intel Skylake Processor */ +/* + * This table is essentially taken from: + * https://grok.cz.oracle.com/source/xref/on12-clone/usr/src/uts/intel/pcbe/skl_pcbe_tbl.c + * Also: + * https://grok.cz.oracle.com/source/xref/on12-clone/usr/src/uts/intel/pcbe/fam6_pcbe.h + * { 0xc0, 0x00, C_ALL, "inst_retired.any_p" }, \ + * { 0x3c, 0x01, C_ALL, "cpu_clk_unhalted.ref_p" }, \ + * { 0x2e, 0x4f, C_ALL, "longest_lat_cache.reference" }, \ + * { 0x2e, 0x41, C_ALL, "longest_lat_cache.miss" }, \ + * { 0xc4, 0x00, C_ALL, "br_inst_retired.all_branches" }, \ + * { 0xc5, 0x00, C_ALL, "br_misp_retired.all_branches" } + * And: + * https://grok.cz.oracle.com/source/xref/on12-clone/usr/src/uts/intel/pcbe/core_pcbe.c + * { 0x3c, 0x00, C_ALL, "cpu_clk_unhalted.core" }, + * { 0x3c, 0x00, C_ALL, "cpu_clk_unhalted.thread_p" }, + */ +#define EVENTS_FAM6_MOD78 \ +{ 0x03, 0x02, C_ALL, "ld_blocks.store_forward" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x03, 0x08, C_ALL, "ld_blocks.no_sr" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x07, 0x01, C_ALL, "ld_blocks_partial.address_alias" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x01, C_ALL, "dtlb_load_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x02, C_ALL, "dtlb_load_misses.walk_completed_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x04, C_ALL, "dtlb_load_misses.walk_completed_2m_4m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x08, C_ALL, "dtlb_load_misses.walk_completed_1g" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x0E, C_ALL, "dtlb_load_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x10, C_ALL, "dtlb_load_misses.walk_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x10, C_ALL, "dtlb_load_misses.walk_active" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x08, 0x20, C_ALL, "dtlb_load_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0D, 0x01, C_ALL, "int_misc.recovery_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0D, 0x01, C_ALL, "int_misc.recovery_cycles_any" , 0x0, ATTR_ANY, 0x0 }, \ +{ 0x0D, 0x80, C_ALL, "int_misc.clear_resteer_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x01, C_ALL, "uops_issued.stall_cycles" , 0x1, ATTR_INV, 0x0 }, \ +{ 0x0E, 0x02, C_ALL, "uops_issued.vector_width_mismatch" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x0E, 0x20, C_ALL, "uops_issued.slow_lea" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x14, 0x01, C_ALL, "arith.divider_active" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x21, C_ALL, "l2_rqsts.demand_data_rd_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x22, C_ALL, "l2_rqsts.rfo_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x24, C_ALL, "l2_rqsts.code_rd_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x27, C_ALL, "l2_rqsts.all_demand_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x38, C_ALL, "l2_rqsts.pf_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x3F, C_ALL, "l2_rqsts.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x41, C_ALL, "l2_rqsts.demand_data_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x42, C_ALL, "l2_rqsts.rfo_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0x44, C_ALL, "l2_rqsts.code_rd_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xD8, C_ALL, "l2_rqsts.pf_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE1, C_ALL, "l2_rqsts.all_demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE2, C_ALL, "l2_rqsts.all_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE4, C_ALL, "l2_rqsts.all_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xE7, C_ALL, "l2_rqsts.all_demand_references" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xF8, C_ALL, "l2_rqsts.all_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x24, 0xFF, C_ALL, "l2_rqsts.references" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2e, 0x4f, C_ALL, "longest_lat_cache.reference" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x2e, 0x41, C_ALL, "longest_lat_cache.miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3c, 0x00, C_ALL, "cpu_clk_unhalted.thread_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x00, C_ALL, "cpu_clk_unhalted.thread_p_any" , 0x0, ATTR_ANY, 0x0 }, \ +{ 0x3C, 0x00, C_ALL, "cpu_clk_unhalted.ring0_trans" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x3C, 0x01, C_ALL, "cpu_clk_unhalted.ref_p" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x01, C_ALL, "cpu_clk_thread_unhalted.ref_xclk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x3C, 0x01, C_ALL, "cpu_clk_thread_unhalted.ref_xclk_any" , 0x0, ATTR_ANY, 0x0 }, \ +{ 0x3C, 0x02, C_ALL, "cpu_clk_thread_unhalted.one_thread_active" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C_ALL, "l1d_pend_miss.pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C_ALL, "l1d_pend_miss.pending_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x48, 0x01, C_ALL, "l1d_pend_miss.pending_cycles_any" , 0x1, ATTR_ANY, 0x0 }, \ +{ 0x48, 0x02, C_ALL, "l1d_pend_miss.fb_full" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x01, C_ALL, "dtlb_store_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x02, C_ALL, "dtlb_store_misses.walk_completed_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x04, C_ALL, "dtlb_store_misses.walk_completed_2m_4m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x08, C_ALL, "dtlb_store_misses.walk_completed_1g" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x0E, C_ALL, "dtlb_store_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x10, C_ALL, "dtlb_store_misses.walk_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x10, C_ALL, "dtlb_store_misses.walk_active" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x49, 0x20, C_ALL, "dtlb_store_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4C, 0x01, C_ALL, "load_hit_pre.sw_pf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x4F, 0x10, C_ALL, "ept.walk_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x51, 0x01, C_ALL, "l1d.replacement" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x54, 0x01, C_ALL, "tx_mem.abort_conflict" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x54, 0x02, C_ALL, "tx_mem.abort_capacity" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x54, 0x04, C_ALL, "tx_mem.abort_hle_store_to_elided_lock" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x54, 0x08, C_ALL, "tx_mem.abort_hle_elision_buffer_not_empty" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x54, 0x10, C_ALL, "tx_mem.abort_hle_elision_buffer_mismatch" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x54, 0x20, C_ALL, "tx_mem.abort_hle_elision_buffer_unsupported_alignment", 0x0, ATTR_TSX, 0x0 }, \ +{ 0x54, 0x40, C_ALL, "tx_mem.hle_elision_buffer_full" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x5D, 0x01, C_ALL, "tx_exec.misc1" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x5D, 0x02, C_ALL, "tx_exec.misc2" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x5D, 0x04, C_ALL, "tx_exec.misc3" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x5D, 0x08, C_ALL, "tx_exec.misc4" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x5D, 0x10, C_ALL, "tx_exec.misc5" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0x5E, 0x01, C_ALL, "rs_events.empty_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x5E, 0x01, C_ALL, "rs_events.empty_end" , 0x1, (ATTR_INV | ATTR_EDGE), 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.cycles_with_demand_data_rd", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x01, C_ALL, "offcore_requests_outstanding.demand_data_rd_ge_6" , 0x6, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x02, C_ALL, "offcore_requests_outstanding.demand_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x02, C_ALL, "offcore_requests_outstanding.cycles_with_demand_code_rd", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x04, C_ALL, "offcore_requests_outstanding.cycles_with_demand_rfo",0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x08, C_ALL, "offcore_requests_outstanding.cycles_with_data_rd" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x10, C_ALL, "offcore_requests_outstanding.l3_miss_demand_data_rd",0x0, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x10, C_ALL, "offcore_requests_outstanding.cycles_with_l3_miss_demand_data_rd", 0x1, ATTR_NONE, 0x0 }, \ +{ 0x60, 0x10, C_ALL, "offcore_requests_outstanding.l3_miss_demand_data_rd_ge_6",0x6, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x04, C_ALL, "idq.mite_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x08, C_ALL, "idq.dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x10, C_ALL, "idq.ms_dsb_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x18, C_ALL, "idq.all_dsb_cycles_any_uops" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x20, C_ALL, "idq.ms_mite_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x24, C_ALL, "idq.all_mite_cycles_any_uops" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_cycles" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x79, 0x30, C_ALL, "idq.ms_switches" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0x80, 0x04, C_ALL, "icache_16b.ifdata_stall" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x83, 0x01, C_ALL, "icache_64b.iftag_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x83, 0x02, C_ALL, "icache_64b.iftag_miss" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x83, 0x04, C_ALL, "icache_64b.iftag_stall" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x01, C_ALL, "itlb_misses.miss_causes_a_walk" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x02, C_ALL, "itlb_misses.walk_completed_4k" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x04, C_ALL, "itlb_misses.walk_completed_2m_4m" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x08, C_ALL, "itlb_misses.walk_completed_1g" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x0E, C_ALL, "itlb_misses.walk_completed" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x10, C_ALL, "itlb_misses.walk_pending" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x10, C_ALL, "itlb_misses.walk_active" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x85, 0x20, C_ALL, "itlb_misses.stlb_hit" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x87, 0x01, C_ALL, "ild_stall.lcp" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_0_uops_deliv.core" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_le_1_uop_deliv.core" , 0x3, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_le_2_uop_deliv.core" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_le_3_uop_deliv.core" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0x9C, 0x01, C_ALL, "idq_uops_not_delivered.cycles_fe_was_ok" , 0x1, ATTR_INV, 0x0 }, \ +{ 0xA1, 0x01, C_ALL, "uops_dispatched_port.port_0" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x02, C_ALL, "uops_dispatched_port.port_1" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x04, C_ALL, "uops_dispatched_port.port_2" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x08, C_ALL, "uops_dispatched_port.port_3" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x10, C_ALL, "uops_dispatched_port.port_4" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x20, C_ALL, "uops_dispatched_port.port_5" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x40, C_ALL, "uops_dispatched_port.port_6" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA1, 0x80, C_ALL, "uops_dispatched_port.port_7" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x01, C_ALL, "resource_stalls.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA2, 0x08, C_ALL, "resource_stalls.sb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x01, C_ALL, "cycle_activity.cycles_l2_miss" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x02, C_ALL, "cycle_activity.cycles_l3_miss" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x04, C_ALL, "cycle_activity.stalls_total" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x05, C_ALL, "cycle_activity.stalls_l2_miss" , 0x5, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x06, C_ALL, "cycle_activity.stalls_l3_miss" , 0x6, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x08, C_ALL, "cycle_activity.cycles_l1d_miss" , 0x8, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x0C, C_ALL, "cycle_activity.stalls_l1d_miss" , 0xC, ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x10, C_ALL, "cycle_activity.cycles_mem_any" , 0x10,ATTR_NONE, 0x0 }, \ +{ 0xA3, 0x14, C_ALL, "cycle_activity.stalls_mem_any" , 0x14,ATTR_NONE, 0x0 }, \ +{ 0xA6, 0x01, C_ALL, "exe_activity.exe_bound_0_ports" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA6, 0x02, C_ALL, "exe_activity.1_ports_util" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA6, 0x04, C_ALL, "exe_activity.2_ports_util" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA6, 0x08, C_ALL, "exe_activity.3_ports_util" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA6, 0x10, C_ALL, "exe_activity.4_ports_util" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA6, 0x40, C_ALL, "exe_activity.bound_on_stores" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA8, 0x01, C_ALL, "lsd.uops" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xA8, 0x01, C_ALL, "lsd.cycles_active" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xA8, 0x01, C_ALL, "lsd.cycles_4_uops" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0xAB, 0x02, C_ALL, "dsb2mite_switches.penalty_cycles" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xAE, 0x01, C_ALL, "itlb.itlb_flush" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x01, C_ALL, "offcore_requests.demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x02, C_ALL, "offcore_requests.demand_code_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x04, C_ALL, "offcore_requests.demand_rfo" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x08, C_ALL, "offcore_requests.all_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x10, C_ALL, "offcore_requests.l3_miss_demand_data_rd" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB0, 0x80, C_ALL, "offcore_requests.all_requests" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.thread" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.cycles_ge_1_uop_exec" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.cycles_ge_2_uops_exec" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.cycles_ge_3_uops_exec" , 0x3, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.cycles_ge_4_uops_exec" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x01, C_ALL, "uops_executed.stall_cycles" , 0x1, ATTR_INV, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_none" , 0x1, ATTR_INV, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_ge_1" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_ge_2" , 0x2, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_ge_3" , 0x3, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x02, C_ALL, "uops_executed.core_cycles_ge_4" , 0x4, ATTR_NONE, 0x0 }, \ +{ 0xB1, 0x10, C_ALL, "uops_executed.x87" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xB2, 0x01, C_ALL, "offcore_requests_buffer.sq_full" , 0x0, ATTR_NONE, 0x0 }, \ +\ + /* \ + * See Section "Off-core Response Performance Monitoring" \ + * \ + * Though these two off_core events support all counters, only 1 of \ + * them can be used at any given time. This is due to the extra MSR \ + * programming required. \ + */ \ +/* { 0xB7, 0x01, C_ALL, "offcore_response_0" , 0x0, ATTR_NONE, OFFCORE_RSP_0 }, omit events requiring MSR programming */ \ +/* { 0xBB, 0x01, C_ALL, "offcore_response_1" , 0x0, ATTR_NONE, OFFCORE_RSP_1 }, omit events requiring MSR programming */ \ +{ 0xBD, 0x01, C_ALL, "tlb_flush.dtlb_thread" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xBD, 0x20, C_ALL, "tlb_flush.stlb_any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc0, 0x00, C_ALL, "inst_retired.any_p" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xC0, 0x1, C(1), "inst_retired.prec_dist" , 0x0, ATTR_PEBS_ONLY, 0x0 }, omit PEBS-only events */ \ +/* { 0xC0, 0x1, (C(0) | C(2) | C(3)), "inst_retired.total_cycles_ps" , 0x0A, (ATTR_PEBS_ONLY | ATTR_INV), 0x0 }, omit PEBS-only events */ \ +{ 0xC1, 0x3F, C_ALL, "other_assists.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.stall_cycles" , 0x1, ATTR_INV, 0x0 }, \ +{ 0xC2, 0x01, C_ALL, "uops_retired.total_cycles" , 0x0A, ATTR_INV, 0x0 }, \ +{ 0xC2, 0x02, C_ALL, "uops_retired.retire_slots" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x01, C_ALL, "machine_clears.count" , 0x1, ATTR_EDGE, 0x0 }, \ +{ 0xC3, 0x02, C_ALL, "machine_clears.memory_ordering" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC3, 0x04, C_ALL, "machine_clears.smc" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xc4, 0x00, C_ALL, "br_inst_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x01, C_ALL, "br_inst_retired.conditional" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x02, C_ALL, "br_inst_retired.near_call" , 0x0, ATTR_PEBS, 0x0 }, \ +/* { 0xC4, 0x04, C_ALL, "br_inst_retired.all_branches_pebs" , 0x0, ATTR_PEBS_ONLY, 0x0 }, omit PEBS-only events */ \ +{ 0xC4, 0x08, C_ALL, "br_inst_retired.near_return" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x10, C_ALL, "br_inst_retired.not_taken" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC4, 0x20, C_ALL, "br_inst_retired.near_taken" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC4, 0x40, C_ALL, "br_inst_retired.far_branch" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xc5, 0x00, C_ALL, "br_misp_retired.all_branches" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC5, 0x01, C_ALL, "br_misp_retired.conditional" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xC5, 0x02, C_ALL, "br_misp_retired.near_call" , 0x0, ATTR_PEBS, 0x0 }, \ +/* { 0xC5, 0x04, C_ALL, "br_misp_retired.all_branches_pebs" , 0x0, ATTR_PEBS_ONLY, 0x0 }, omit PEBS-only events */ \ +{ 0xC5, 0x20, C_ALL, "br_misp_retired.near_taken" , 0x0, ATTR_PEBS, 0x0 }, \ +/* { 0xC6, 0x01, C_ALL, "frontend_retired" , 0x0, ATTR_PEBS, MSR_PEBS_FRONTEND}, omit events requiring MSR programming */ \ +{ 0xC7, 0x01, C_ALL, "fp_arith_inst_retired.scalar_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x02, C_ALL, "fp_arith_inst_retired.scalar_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x04, C_ALL, "fp_arith_inst_retired.128b_packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x08, C_ALL, "fp_arith_inst_retired.128b_packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x10, C_ALL, "fp_arith_inst_retired.256b_packed_double" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC7, 0x20, C_ALL, "fp_arith_inst_retired.256b_packed_single" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xC8, 0x01, C_ALL, "hle_retired.start" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC8, 0x02, C_ALL, "hle_retired.commit" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC8, 0x04, C_ALL, "hle_retired.aborted" , 0x0, ATTR_PEBS | ATTR_TSX, 0x0 }, \ +{ 0xC8, 0x08, C_ALL, "hle_retired.aborted_mem" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC8, 0x10, C_ALL, "hle_retired.aborted_timer" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC8, 0x20, C_ALL, "hle_retired.aborted_unfriendly" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC8, 0x40, C_ALL, "hle_retired.aborted_memtype" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC8, 0x80, C_ALL, "hle_retired.aborted_events" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC9, 0x01, C_ALL, "rtm_retired.start" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC9, 0x02, C_ALL, "rtm_retired.commit" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC9, 0x04, C_ALL, "rtm_retired.aborted" , 0x0, ATTR_PEBS | ATTR_TSX, 0x0 }, \ +{ 0xC9, 0x08, C_ALL, "rtm_retired.aborted_mem" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC9, 0x10, C_ALL, "rtm_retired.aborted_timer" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC9, 0x20, C_ALL, "rtm_retired.aborted_unfriendly" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC9, 0x40, C_ALL, "rtm_retired.aborted_memtype" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xC9, 0x80, C_ALL, "rtm_retired.aborted_events" , 0x0, ATTR_TSX, 0x0 }, \ +{ 0xCA, 0x1E, C_ALL, "fp_assist.any" , 0x1, ATTR_NONE, 0x0 }, \ +{ 0xCB, 0x01, C_ALL, "hw_interrupts.received" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xCC, 0x20, C_ALL, "rob_misc_events.lbr_inserts" , 0x0, ATTR_NONE, 0x0 }, \ +/* { 0xCD, 0x01, C_ALL, "mem_trans_retired.load_latency" , 0x0, ATTR_PEBS_ONLY_LD_LAT, PEBS_LD_LAT_THRESHOLD }, omit events requiring MSR programming */ \ +{ 0xD0, 0x11, C_ALL, "mem_inst_retired.stlb_miss_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x12, C_ALL, "mem_inst_retired.stlb_miss_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x21, C_ALL, "mem_inst_retired.lock_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x41, C_ALL, "mem_inst_retired.split_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x42, C_ALL, "mem_inst_retired.split_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x81, C_ALL, "mem_inst_retired.all_loads" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD0, 0x82, C_ALL, "mem_inst_retired.all_stores" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x01, C_ALL, "mem_load_retired.l1_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x02, C_ALL, "mem_load_retired.l2_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x04, C_ALL, "mem_load_retired.l3_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x08, C_ALL, "mem_load_retired.l1_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x10, C_ALL, "mem_load_retired.l2_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x20, C_ALL, "mem_load_retired.l3_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD1, 0x40, C_ALL, "mem_load_retired.fb_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x01, C_ALL, "mem_load_l3_hit_retired.xsnp_miss" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x02, C_ALL, "mem_load_l3_hit_retired.xsnp_hit" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x04, C_ALL, "mem_load_l3_hit_retired.xsnp_hitm" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD2, 0x08, C_ALL, "mem_load_l3_hit_retired.xsnp_none" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xD4, 0x04, C_ALL, "mem_load_misc_retired.uc" , 0x0, ATTR_PEBS, 0x0 }, \ +{ 0xE6, 0x01, C_ALL, "baclears.any" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF0, 0x40, C_ALL, "l2_trans.l2_wb" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF1, 0x1F, C_ALL, "l2_lines_in.all" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x01, C_ALL, "l2_lines_out.silent" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x02, C_ALL, "l2_lines_out.non_silent" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF2, 0x04, C_ALL, "l2_lines_out.useless_hwpf" , 0x0, ATTR_NONE, 0x0 }, \ +{ 0xF4, 0x10, C_ALL, "sq_misc.split_lock" , 0x0, ATTR_NONE, 0x0 }, \ +/* end of #define */ + +#define NT_END {0, 0, 0, NULL, 0x0, ATTR_NONE, 0x0 } /* end-of-table */ + +static const struct events_table_t *events_table = NULL; + +const struct events_table_t events_fam6_mod23[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD23 + NT_END +}; + +const struct events_table_t events_fam6_mod28[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD28 + NT_END +}; + +const struct events_table_t events_fam6_mod26[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD26 + NT_END +}; + +const struct events_table_t events_fam6_mod46[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD26 + EVENTS_FAM6_MOD46_ONLY + NT_END +}; + +const struct events_table_t events_fam6_mod37[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD37 + EVENTS_FAM6_MOD37_ALSO + NT_END +}; + +const struct events_table_t events_fam6_mod47[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD37 + NT_END +}; + +const struct events_table_t events_fam6_mod42[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD42 + EVENTS_FAM6_MOD42_ONLY + NT_END +}; + +const struct events_table_t events_fam6_mod45[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD42 + EVENTS_FAM6_MOD45_ONLY + NT_END +}; + +const struct events_table_t events_fam6_mod58[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD58 + NT_END +}; + +const struct events_table_t events_fam6_mod62[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD58 + EVENTS_FAM6_MOD62_ONLY + NT_END +}; + +const struct events_table_t events_fam6_mod60[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD60 + NT_END +}; + +const struct events_table_t events_fam6_mod61[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD61 + NT_END +}; + +const struct events_table_t events_fam6_mod78[] = { + ARCH_EVENTS + EVENTS_FAM6_MOD78 + NT_END +}; + +const struct events_table_t events_fam6_unknown[] = { + ARCH_EVENTS + NT_END +}; + +const struct events_table_t events_fam_arm[] = { +// ARCH_EVENTS +// *eventnum = pevent->eventselect; +// *eventnum |= (pevent->unitmask << PERFCTR_UMASK_SHIFT); +// *eventnum |= (pevent->attrs << 16); +// *eventnum |= (pevent->cmask << 24); +// eventselect, unitmask, supported_counters, name, cmask, attrs, msr_offset + +// Hardware event +#define HWE(nm, id) { id, 0, C_ALL, nm, PERF_TYPE_HARDWARE, 0, 0 }, + HWE("branch-instructions", PERF_COUNT_HW_BRANCH_INSTRUCTIONS) + HWE("branch-misses", PERF_COUNT_HW_BRANCH_MISSES) + HWE("bus-cycles", PERF_COUNT_HW_BUS_CYCLES) + HWE("cache-misses", PERF_COUNT_HW_CACHE_MISSES) + HWE("cache-references", PERF_COUNT_HW_CACHE_REFERENCES) + HWE("cycles", PERF_COUNT_HW_CPU_CYCLES) + HWE("instructions", PERF_COUNT_HW_INSTRUCTIONS) + HWE("ref-cycles", PERF_COUNT_HW_REF_CPU_CYCLES) + HWE("stalled-cycles-backend", PERF_COUNT_HW_STALLED_CYCLES_BACKEND) + HWE("stalled-cycles-frontend", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND) + +// Software event +#define SWE(nm, id) { id, 0, C_ALL, nm, PERF_TYPE_SOFTWARE, 0, 0 }, + SWE("alignment-faults", PERF_COUNT_SW_ALIGNMENT_FAULTS) + SWE("context-switches", PERF_COUNT_SW_CONTEXT_SWITCHES) + SWE("cpu-clock", PERF_COUNT_SW_CPU_CLOCK) + SWE("cpu-migrations", PERF_COUNT_SW_CPU_MIGRATIONS) + SWE("emulation-faults", PERF_COUNT_SW_EMULATION_FAULTS) + SWE("major-faults", PERF_COUNT_SW_PAGE_FAULTS_MAJ) + SWE("minor-faults", PERF_COUNT_SW_PAGE_FAULTS_MIN) + SWE("page-faults", PERF_COUNT_SW_PAGE_FAULTS) + SWE("task-clock", PERF_COUNT_SW_TASK_CLOCK) + +// Hardware cache event +#define HWCE(nm, id, op, res) { id | (op << 8) | (res << 16), 0, C_ALL, nm, PERF_TYPE_HW_CACHE, 0, 0 }, + HWCE("L1-dcache-load-misses", PERF_COUNT_HW_CACHE_L1D, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS) + HWCE("L1-dcache-loads", PERF_COUNT_HW_CACHE_L1D, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS) + HWCE("L1-dcache-store-misses",PERF_COUNT_HW_CACHE_L1D, PERF_COUNT_HW_CACHE_RESULT_MISS, PERF_COUNT_HW_CACHE_RESULT_ACCESS) + HWCE("L1-dcache-stores", PERF_COUNT_HW_CACHE_L1D, PERF_COUNT_HW_CACHE_OP_WRITE, PERF_COUNT_HW_CACHE_RESULT_ACCESS) + HWCE("L1-icache-load-misses", PERF_COUNT_HW_CACHE_L1I, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS) + HWCE("L1-icache-loads", PERF_COUNT_HW_CACHE_L1I, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS) +// HWCE("branch-load-misses",) +// HWCE("branch-loads",) + HWCE("dTLB-load-misses", PERF_COUNT_HW_CACHE_DTLB, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS) + HWCE("dTLB-loads", PERF_COUNT_HW_CACHE_DTLB, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS) + HWCE("iTLB-load-misses", PERF_COUNT_HW_CACHE_ITLB, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS) + HWCE("iTLB-loads", PERF_COUNT_HW_CACHE_ITLB, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS) + + NT_END +}; + +static int +core_pcbe_init (void) +{ + switch (cpuid_getvendor ()) + { + case ARM_CPU_IMP_ARM: + case ARM_CPU_IMP_BRCM: + case ARM_CPU_IMP_CAVIUM: + case ARM_CPU_IMP_APM: + case ARM_CPU_IMP_QCOM: + snprintf (core_impl_name, sizeof (core_impl_name), "%s", AARCH64_VENDORSTR_ARM); + events_table = events_fam_arm; + num_gpc = 4; // MEZ: a real implementation is needed + num_ffc = 0; + total_pmc = num_gpc + num_ffc; + return 0; + case X86_VENDOR_Intel: + break; + default: + return -1; + } + +#if defined(__i386__) || defined(__x86_64) + /* No Architectural Performance Monitoring Leaf returned by CPUID */ + if (get_cpuid_info ()->cpi_maxeax < 0xa) + return (-1); + + /* Obtain the Architectural Performance Monitoring Leaf */ + cpuid_regs_t cp; + my_cpuid (0xa, &cp); + uint32_t versionid = cp.eax & 0xFF; + + /* + * Fixed-Function Counters (FFC) + * + * All Family 6 Model 15 and Model 23 processors have fixed-function + * counters. These counters were made Architectural with + * Family 6 Model 15 Stepping 9. + */ + switch (versionid) + { + case 0: + return -1; + case 2: + num_ffc = cp.edx & 0x1F; + /* + * Some processors have an errata (AW34) where + * versionid is reported as 2 when actually 1. + * In this case, fixed-function counters are + * model-specific as in Version 1. + */ + if (num_ffc != 0) + break; + /* FALLTHROUGH */ + case 1: + num_ffc = 3; + versionid = 1; + break; + default: + num_ffc = cp.edx & 0x1F; + break; + } + if (num_ffc >= 64) + return (-1); + uint64_t known_ffc_num = sizeof (ffc_names) / sizeof (char *) - 1; /* -1 for EOT */ + if (num_ffc > known_ffc_num) + /* + * The system seems to have more fixed-function counters than + * what this PCBE is able to handle correctly. Default to the + * maximum number of fixed-function counters that this driver + * is aware of. + */ + num_ffc = known_ffc_num; + + /* + * General Purpose Counters (GPC) + */ + num_gpc = (cp.eax >> 8) & 0xFF; + if (num_gpc >= 64) + return (-1); + total_pmc = num_gpc + num_ffc; + if (total_pmc > 64) /* Too wide for the overflow bitmap */ + return (-1); + + uint_t cpuid_model = cpuid_getmodel (); + + /* GPC events for Family 6 Models 15 & 23 only */ + if ((cpuid_getfamily () == 6) && + ((cpuid_model == 15) || (cpuid_model == 23))) + (void) snprintf (core_impl_name, IMPL_NAME_LEN, "Core Microarchitecture"); + else + (void) snprintf (core_impl_name, IMPL_NAME_LEN, + "Intel Arch PerfMon v%d on Family %d Model %d", + versionid, cpuid_getfamily (), cpuid_model); + /* + * Process architectural and non-architectural events using GPC + */ + if (num_gpc > 0) + { + switch (cpuid_model) + { + case 15: /* Core 2 */ + case 23: + events_table = events_fam6_mod23; + break; + case 28: /* Atom */ + events_table = events_fam6_mod28; + break; + case 37: /* Westmere */ + case 44: + events_table = events_fam6_mod37; + break; + case 47: + events_table = events_fam6_mod47; + break; + case 26: /* Nehalem */ + case 30: + case 31: + events_table = events_fam6_mod26; + break; + case 46: + events_table = events_fam6_mod46; + break; + case 42: /* Sandy Bridge */ + events_table = events_fam6_mod42; + break; + case 45: + events_table = events_fam6_mod45; + break; + case 58: /* Ivy Bridge */ + events_table = events_fam6_mod58; + break; + case 62: + events_table = events_fam6_mod62; + break; + case 60: /* Haswell */ + case 63: + case 69: + case 70: + events_table = events_fam6_mod60; + break; + case 61: /* Broadwell */ + case 71: + case 79: + case 86: + events_table = events_fam6_mod61; + break; + case 78: /* Skylake */ + case 85: + case 94: + events_table = events_fam6_mod78; + break; + default: /* unknown */ + events_table = events_fam6_unknown; + } + } + /* + * Fixed-function Counters (FFC) are already listed individually in + * ffc_names[] + */ +#endif + return 0; +} + +static uint_t +core_pcbe_ncounters () +{ + return total_pmc; +} + +static const char * +core_pcbe_impl_name (void) +{ + return core_impl_name; +} + +static const char * +core_pcbe_cpuref (void) +{ +#if defined(__aarch64__) + return ""; +#elif defined(__i386__) || defined(__x86_64) + switch (cpuid_getmodel ()) + { + case 60: /* Haswell */ + case 63: + case 69: + case 70: + return GTXT ("See Chapter 19 of the \"Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide, Part 2\"\nOrder Number: 253669-047US, June 2013"); + case 61: /* Broadwell */ + case 71: + case 79: + case 86: + case 78: /* Skylake */ + case 85: + case 94: + return GTXT ("See Chapter 19 of the \"Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide\""); + default: + return + GTXT ("See Chapter 19 of the \"Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide, Part 2\"\nOrder Number: 253669-045US, January 2013"); + } +#endif +} + +static int +core_pcbe_get_events (hwcf_hwc_cb_t *hwc_cb) +{ + int count = 0; + const struct events_table_t *pevent; + for (pevent = events_table; pevent && pevent->name; pevent++) + for (uint_t jj = 0; jj < num_gpc; jj++) + if (C (jj) & pevent->supported_counters) + { + hwc_cb (jj, pevent->name); + count++; + } + + for (int ii = 0; ii < sizeof (ffc_names) / sizeof (*ffc_names) && ffc_names[ii]; ii++) + { + hwc_cb (ii + num_gpc, ffc_names[ii]); + count++; + } + /* add generic events here */ + return count; +} + +static int +core_pcbe_get_eventnum (const char *eventname, uint_t pmc, eventsel_t *eventnum, + eventsel_t *valid_umask, uint_t *pmc_sel) +{ + const struct events_table_t* pevent; + *valid_umask = 0x0; /* by default, don't allow user umask */ + *pmc_sel = pmc; /* by default, use the requested pmc */ + + /* search non-ffc table */ + for (pevent = events_table; pevent && pevent->name; pevent++) + { + if (strcmp (eventname, pevent->name) == 0) + { + *eventnum = pevent->eventselect; + *eventnum |= (pevent->unitmask << PERFCTR_UMASK_SHIFT); + *eventnum |= (pevent->attrs << 16); + *eventnum |= (pevent->cmask << 24); + + if (pevent->msr_offset) + { + /* + * Should also handle any pevent->msr_offset. + * Can check libcpc's usr/src/uts/intel/pcbe/snb_pcbe.h, + * function snb_gpc_configure(). + * + * Actually, we should probably error out here + * until the appropriate support has been added. + * Also, we can comment out events that require + * msr_offset so that they aren't even listed. + */ + } + if (!pevent->unitmask) + *valid_umask = 0xff; /* allow umask if nothing set */ + return 0; + } + } + + /* search ffc table */ + for (int ii = 0; ii < sizeof (ffc_names) / sizeof (*ffc_names) && ffc_names[ii]; ii++) + { + if (strcmp (eventname, ffc_names[ii]) == 0) + { + *eventnum = 0; + *pmc_sel = ii | PERFCTR_FIXED_MAGIC; + return 0; + } + } + *eventnum = (eventsel_t) - 1; + return -1; +} + +static hdrv_pcbe_api_t hdrv_pcbe_core_api = { + core_pcbe_init, + core_pcbe_ncounters, + core_pcbe_impl_name, + core_pcbe_cpuref, + core_pcbe_get_events, + core_pcbe_get_eventnum +}; diff --git a/gprofng/common/cpu_frequency.h b/gprofng/common/cpu_frequency.h new file mode 100644 index 0000000..b46b54d --- /dev/null +++ b/gprofng/common/cpu_frequency.h @@ -0,0 +1,303 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _CPU_FREQUENCY_H +#define _CPU_FREQUENCY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <alloca.h> +#include <unistd.h> /* processor_info_t */ +#include <fcntl.h> + + typedef unsigned char uint8_t; + +#define MAXSTRLEN 1024 + /* + * This file provide the api to detect Intel CPU frequency variation features + */ + +#define COL_CPUFREQ_NONE 0x0000 +#define COL_CPUFREQ_SCALING 0x0001 +#define COL_CPUFREQ_TURBO 0x0002 + +#if defined(__i386__) || defined(__x86_64) + // XXXX This is a rough table to estimate frequency increment due to intel turbo boost. + // CPU with different stepping and different core number have different turbo increment. + // It is used internally here, and is not implemented on SPARC + + // YLM: one can use cputrack to estimate max turbo frequency + // example: for a cpu-bound app that runs for > 10 seconds, count cycles for 10 seconds: + // cputrack -T 10 -v -c cpu_clk_unhalted.thread_p a.out + + static int + get_max_turbo_freq (int model) + { + switch (model) + { + // Nehalem + case 30:// Core i7-870: 2/2/4/5 + return 2 * 133333; + case 26:// Xeon L5520: 1/1/1/2 + return 2 * 133333; + case 46:// Xeon E7540: 2 + return 2 * 133333; + // Westmere + case 37:// Core i5-520M: 2/4 + return 2 * 133333; + case 44:// Xeon E5620: 1/1/2/2 + return 2 * 133333; + case 47:// Xeon E7-2820: 1/1/1/2 + return 1 * 133333; + // Sandy Bridge + case 42:// Core i5-2500: 1/2/3/4 + return 3 * 100000; + // http://ark.intel.com/products/64584/Intel-Xeon-Processor-E5-2660-20M-Cache-2_20-GHz-8_00-GTs-Intel-QPI + case 45:// Xeon E5-2660 GenuineIntel 206D7 family 6 model 45 step 7 clock 2200 MHz + return 8 * 100000; + // Ivy Bridge + case 58:// Core i7-3770: 3/4/5/5 + return 4 * 100000; + case 62:// Xeon E5-2697: 3/3/3/3/3/3/3/4/5/6/7/8 + return 7 * 100000; + // Haswell + case 60: + return 789000; // empirically we see 3189 MHz - 2400 MHz + case 63: + return 1280000; // empirically we see 3580 MHz - 2300 MHz for single-threaded + // return 500000; // empirically we see 2800 MHz - 2300 MHz for large throughput + // Broadwell + // where are these values listed? + // maybe try https://en.wikipedia.org/wiki/Broadwell_%28microarchitecture%29#Server_processors + case 61: + return 400000; + case 71: + return 400000; + case 79: + return 950000; // empirically we see (3550-2600) MHz for single-threaded on x6-2a + case 85: + return 1600000; // X7: empirically see ~3.7GHz with single thread, baseline is 2.1Ghz Return 3,700,000-2,100,000 + case 31: // Nehalem? + case 28: // Atom + case 69: // Haswell + case 70: // Haswell + case 78: // Skylake + case 94: // Skylake + default: + return 0; + } + } +#endif + + /* + * parameter: mode, pointer to a 8bit mode indicator + * return: max cpu frequency in MHz + */ + //YXXX Updating this function? Check similar cut/paste code in: + // collctrl.cc::Coll_Ctrl() + // collector.c::log_header_write() + // cpu_frequency.h::get_cpu_frequency() + + static int + get_cpu_frequency (uint8_t *mode) + { + int ret_freq = 0; + if (mode != NULL) + *mode = COL_CPUFREQ_NONE; + FILE *procf = fopen ("/proc/cpuinfo", "r"); + if (procf != NULL) + { + char temp[1024]; + int cpu = -1; +#if defined(__i386__) || defined(__x86_64) + int model = -1; + int family = -1; +#endif + while (fgets (temp, 1024, procf) != NULL) + { + if (strncmp (temp, "processor", strlen ("processor")) == 0) + { + char *val = strchr (temp, ':'); + cpu = val ? atoi (val + 1) : -1; + } +#if defined(__i386__) || defined(__x86_64) + else if (strncmp (temp, "model", strlen ("model")) == 0 + && strstr (temp, "name") == 0) + { + char *val = strchr (temp, ':'); + model = val ? atoi (val + 1) : -1; + } + else if (strncmp (temp, "cpu family", strlen ("cpu family")) == 0) + { + char *val = strchr (temp, ':'); + family = val ? atoi (val + 1) : -1; + } +#endif + else if (strncmp (temp, "cpu MHz", strlen ("cpu MHz")) == 0) + { + char *val = strchr (temp, ':'); + int mhz = val ? atoi (val + 1) : 0; /* reading it as int is fine */ + char scaling_freq_file[MAXSTRLEN + 1]; + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_driver", cpu); + int intel_pstate = 0; + int no_turbo = 0; + if (access (scaling_freq_file, R_OK) == 0) + { + FILE *cpufreqd = fopen (scaling_freq_file, "r"); + if (cpufreqd != NULL) + { + if (fgets (temp, 1024, cpufreqd) != NULL + && strncmp (temp, "intel_pstate", sizeof ("intel_pstate") - 1) == 0) + intel_pstate = 1; + fclose (cpufreqd); + } + } + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/intel_pstate/no_turbo"); + if (access (scaling_freq_file, R_OK) == 0) + { + FILE *pstatent = fopen (scaling_freq_file, "r"); + if (pstatent != NULL) + { + if (fgets (temp, 1024, pstatent) != NULL) + if (strncmp (temp, "1", sizeof ("1") - 1) == 0) + no_turbo = 1; + fclose (pstatent); + } + } + + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpu); + int frequency_scaling = 0; + int turbo_mode = 0; + if (access (scaling_freq_file, R_OK) == 0) + { + FILE *cpufreqf = fopen (scaling_freq_file, "r"); + if (cpufreqf != NULL) + { + if (fgets (temp, 1024, cpufreqf) != NULL) + { + int ondemand = 0; + if (strncmp (temp, "ondemand", sizeof ("ondemand") - 1) == 0) + ondemand = 1; + int performance = 0; + if (strncmp (temp, "performance", sizeof ("performance") - 1) == 0) + performance = 1; + int powersave = 0; + if (strncmp (temp, "powersave", sizeof ("powersave") - 1) == 0) + powersave = 1; + if (intel_pstate || ondemand || performance) + { + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); + if (access (scaling_freq_file, R_OK) == 0) + { + FILE * cpufreqf_max; + if ((cpufreqf_max = fopen (scaling_freq_file, "r")) != NULL) + { + if (fgets (temp, 1024, cpufreqf_max) != NULL) + { + int tmpmhz = atoi (temp); + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_available_frequencies", cpu); + if (intel_pstate) + { + frequency_scaling = 1; + turbo_mode = !no_turbo; + if (powersave) + // the system might have been relatively cold + // so we might do better with scaling_max_freq + mhz = (int) (((double) tmpmhz / 1000.0) + 0.5); + } + else if (access (scaling_freq_file, R_OK) == 0) + { + FILE * cpufreqf_ava; + if ((cpufreqf_ava = fopen (scaling_freq_file, "r")) != NULL) + { + if (fgets (temp, 1024, cpufreqf_ava) != NULL) + { + if (strchr (temp, ' ') != strrchr (temp, ' ') && ondemand) + frequency_scaling = 1; + if (tmpmhz > 1000) + { +#if defined(__i386__) || defined(__x86_64) + if (family == 6) + { + // test turbo mode + char non_turbo_max_freq[1024]; + snprintf (non_turbo_max_freq, sizeof (non_turbo_max_freq), + "%d", tmpmhz - 1000); + if (strstr (temp, non_turbo_max_freq)) + { + turbo_mode = 1; + tmpmhz = (tmpmhz - 1000) + get_max_turbo_freq (model); + } + } +#endif + } + } + fclose (cpufreqf_ava); + } + mhz = (int) (((double) tmpmhz / 1000.0) + 0.5); + } + } + fclose (cpufreqf_max); + } + } + } + } + fclose (cpufreqf); + } + } + if (mhz > ret_freq) + ret_freq = mhz; + if (frequency_scaling && mode != NULL) + *mode |= COL_CPUFREQ_SCALING; + if (turbo_mode && mode != NULL) + *mode |= COL_CPUFREQ_TURBO; + } + else if (strncmp (temp, "Cpu", 3) == 0 && temp[3] != '\0' && + strncmp (strchr (temp + 1, 'C') ? strchr (temp + 1, 'C') : (temp + 4), "ClkTck", 6) == 0) + { // sparc-Linux + char *val = strchr (temp, ':'); + if (val) + { + unsigned long long freq; + sscanf (val + 2, "%llx", &freq); + int mhz = (unsigned int) (((double) freq) / 1000000.0 + 0.5); + if (mhz > ret_freq) + ret_freq = mhz; + } + } + } + fclose (procf); + } + return ret_freq; + } + +#ifdef __cplusplus +} +#endif + +#endif /*_CPU_FREQUENCY_H*/ diff --git a/gprofng/common/cpuid.c b/gprofng/common/cpuid.c new file mode 100644 index 0000000..211e09a --- /dev/null +++ b/gprofng/common/cpuid.c @@ -0,0 +1,203 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#if defined(__i386__) || defined(__x86_64) +#include <cpuid.h> /* GCC-provided */ +#elif defined(__aarch64__) +#define ATTRIBUTE_UNUSED __attribute__((unused)) + +static inline uint_t __attribute_const__ +__get_cpuid (unsigned int op ATTRIBUTE_UNUSED, unsigned int *eax, + unsigned int *ebx ATTRIBUTE_UNUSED, + unsigned int *ecx ATTRIBUTE_UNUSED, unsigned int *edx ATTRIBUTE_UNUSED) +{ + // CPUID bit assignments: + // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM) + // [23:20] VARIANT indicates processor revision (0x2 = Revision 2) + // [19:16] Constant (Reads as 0xF) + // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3) + // [03:00] REVISION indicates patch release (0x0 = Patch 0) + // unsigned long v = 0; + // __asm volatile ("MRS %[result], MPIDR_EL1" : [result] "=r" (v)); + // Tprintf(DBG_LT0, "cpuid.c:%d read_cpuid_id() MPIDR_EL1=0x%016lx\n", __LINE__, v); + uint_t res = 0; + __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (*eax)); + Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1=0x%016x\n", __LINE__, *eax); + return res; +} +#endif + +/* + * Various routines to handle identification + * and classification of x86 processors. + */ + +#define IS_GLOBAL /* externally visible */ +#define X86_VENDOR_Intel 0 +#define X86_VENDORSTR_Intel "GenuineIntel" +#define X86_VENDOR_IntelClone 1 +#define X86_VENDOR_AMD 2 +#define X86_VENDORSTR_AMD "AuthenticAMD" + +#define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU)) +#define CPI_FAMILY_XTD(reg) BITX(reg, 27, 20) +#define CPI_MODEL_XTD(reg) BITX(reg, 19, 16) +#define CPI_TYPE(reg) BITX(reg, 13, 12) +#define CPI_FAMILY(reg) BITX(reg, 11, 8) +#define CPI_STEP(reg) BITX(reg, 3, 0) +#define CPI_MODEL(reg) BITX(reg, 7, 4) +#define IS_EXTENDED_MODEL_INTEL(model) ((model) == 0x6 || (model) >= 0xf) + + +typedef struct +{ + unsigned int eax; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; +} cpuid_regs_t; + +typedef struct +{ + unsigned int cpi_model; + unsigned int cpi_family; + unsigned int cpi_vendor; /* enum of cpi_vendorstr */ + unsigned int cpi_maxeax; /* fn 0: %eax */ + char cpi_vendorstr[13]; /* fn 0: %ebx:%ecx:%edx */ +} cpuid_info_t; + + +#if defined(__i386__) || defined(__x86_64) +static uint_t +cpuid_vendorstr_to_vendorcode (char *vendorstr) +{ + if (strcmp (vendorstr, X86_VENDORSTR_Intel) == 0) + return X86_VENDOR_Intel; + else if (strcmp (vendorstr, X86_VENDORSTR_AMD) == 0) + return X86_VENDOR_AMD; + else + return X86_VENDOR_IntelClone; +} + +static int +my_cpuid (unsigned int op, cpuid_regs_t *regs) +{ + regs->eax = regs->ebx = regs->ecx = regs->edx = 0; + int ret = __get_cpuid (op, ®s->eax, ®s->ebx, ®s->ecx, ®s->edx); + TprintfT (DBG_LT1, "my_cpuid: __get_cpuid(0x%x, 0x%x, 0x%x, 0x%x, 0x%x) returns %d\n", + op, regs->eax, regs->ebx, regs->ecx, regs->edx, ret); + return ret; +} +#endif + +static cpuid_info_t * +get_cpuid_info () +{ + static int cpuid_inited = 0; + static cpuid_info_t cpuid_info; + cpuid_info_t *cpi = &cpuid_info; + if (cpuid_inited) + return cpi; + cpuid_inited = 1; + +#if defined(__aarch64__) + // CPUID bit assignments: + // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM) + // [23:20] VARIANT indicates processor revision (0x2 = Revision 2) + // [19:16] Constant (Reads as 0xF) + // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3) + // [03:00] REVISION indicates patch release (0x0 = Patch 0) + uint_t reg = 0; + __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (reg)); + cpi->cpi_vendor = reg >> 24; + cpi->cpi_model = (reg >> 4) & 0xfff; + switch (cpi->cpi_vendor) + { + case ARM_CPU_IMP_APM: + case ARM_CPU_IMP_ARM: + case ARM_CPU_IMP_CAVIUM: + case ARM_CPU_IMP_BRCM: + case ARM_CPU_IMP_QCOM: + strncpy (cpi->cpi_vendorstr, AARCH64_VENDORSTR_ARM, sizeof (cpi->cpi_vendorstr)); + break; + default: + strncpy (cpi->cpi_vendorstr, "UNKNOWN ARM", sizeof (cpi->cpi_vendorstr)); + break; + } + Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1==0x%016x cpi_vendor=%d cpi_model=%d\n", + __LINE__, (unsigned int) reg, cpi->cpi_vendor, cpi->cpi_model); + +#elif defined(__i386__) || defined(__x86_64) + cpuid_regs_t regs; + my_cpuid (0, ®s); + cpi->cpi_maxeax = regs.eax; + ((uint32_t *) cpi->cpi_vendorstr)[0] = regs.ebx; + ((uint32_t *) cpi->cpi_vendorstr)[1] = regs.edx; + ((uint32_t *) cpi->cpi_vendorstr)[2] = regs.ecx; + cpi->cpi_vendorstr[12] = 0; + cpi->cpi_vendor = cpuid_vendorstr_to_vendorcode (cpi->cpi_vendorstr); + + my_cpuid (1, ®s); + cpi->cpi_model = CPI_MODEL (regs.eax); + cpi->cpi_family = CPI_FAMILY (regs.eax); + if (cpi->cpi_family == 0xf) + cpi->cpi_family += CPI_FAMILY_XTD (regs.eax); + + /* + * Beware: AMD uses "extended model" iff base *FAMILY* == 0xf. + * Intel, and presumably everyone else, uses model == 0xf, as + * one would expect (max value means possible overflow). Sigh. + */ + switch (cpi->cpi_vendor) + { + case X86_VENDOR_Intel: + if (IS_EXTENDED_MODEL_INTEL (cpi->cpi_family)) + cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4; + break; + case X86_VENDOR_AMD: + if (CPI_FAMILY (cpi->cpi_family) == 0xf) + cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4; + break; + default: + if (cpi->cpi_model == 0xf) + cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4; + break; + } +#endif + return cpi; +} + +static inline uint_t +cpuid_getvendor () +{ + return get_cpuid_info ()->cpi_vendor; +} + +static inline uint_t +cpuid_getfamily () +{ + return get_cpuid_info ()->cpi_family; +} + +static inline uint_t +cpuid_getmodel () +{ + return get_cpuid_info ()->cpi_model; +} diff --git a/gprofng/common/gp-defs.h b/gprofng/common/gp-defs.h new file mode 100644 index 0000000..440bfb1 --- /dev/null +++ b/gprofng/common/gp-defs.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _GP_DEFS_H_ +#define _GP_DEFS_H_ + +/* Define the ARCH and WSIZE predicates */ +/* + * The way we define and use predicates is similar to the + * standard #assert with one important exception: + * if an argument of a predicate is not known the result + * is 'false' and we want a compile time error to avoid + * silent results from typos like ARCH(INTEL), COMPILER(gnu), + * etc. + */ +#define ARCH(x) TOK_A_##x(ARCH) +#define TOK_A_Aarch64(x) x##_Aarch64 +#define TOK_A_SPARC(x) x##_SPARC +#define TOK_A_Intel(x) x##_Intel + +#define WSIZE(x) TOK_W_##x(WSIZE) +#define TOK_W_32(x) x##_32 +#define TOK_W_64(x) x##_64 + +#if defined(sparc) || defined(__sparcv9) +#define ARCH_SPARC 1 +#elif defined(__i386__) || defined(__x86_64) +#define ARCH_Intel 1 +#elif defined(__aarch64__) +#define ARCH_Aarch64 1 +#else +#error "Undefined platform" +#endif + +#if defined(__sparcv9) || defined(__x86_64) || defined(__aarch64__) +#define WSIZE_64 1 +#else +#define WSIZE_32 1 +#endif + +#endif diff --git a/gprofng/common/gp-experiment.h b/gprofng/common/gp-experiment.h new file mode 100644 index 0000000..040c2d1 --- /dev/null +++ b/gprofng/common/gp-experiment.h @@ -0,0 +1,186 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _EXPERIMENT_H +#define _EXPERIMENT_H + +/* version numbers define experiment format */ +#define SUNPERF_VERNUM 12 +#define SUNPERF_VERNUM_MINOR 4 + +/* backward compatibility down to: */ +#define SUNPERF_VERNUM_LEAST 12 + +#include "Emsgnum.h" /* for COL_ERROR_*, etc. symbols */ + +#define SP_REMOTE_PROTOCOL_VERSION "12.4.1" + +#define SP_GROUP_HEADER "#analyzer experiment group" + +/* Experiment name macro definitions */ + +/* for descendant experiments */ +#define DESCENDANT_EXPT_KEY ".er/_" +#define IS_DESC_EXPT(exptname) (strstr(exptname,DESCENDANT_EXPT_KEY) != NULL) +#define IS_FNDR_EXPT(exptname) (strstr(exptname,DESCENDANT_EXPT_KEY) == NULL) + +/* File name definitions */ +#define SP_ARCHIVES_DIR "archives" +#define SP_ARCHIVE_LOG_FILE "archive.log" +#define SP_LOG_FILE "log.xml" +#define SP_NOTES_FILE "notes" +#define SP_IFREQ_FILE "ifreq" +#define SP_MAP_FILE "map.xml" +#define SP_LABELS_FILE "labels.xml" +#define SP_DYNTEXT_FILE "dyntext" +#define SP_OVERVIEW_FILE "overview" +#define SP_PROFILE_FILE "profile" +#define SP_SYNCTRACE_FILE "synctrace" +#define SP_IOTRACE_FILE "iotrace" +#define SP_OMPTRACE_FILE "omptrace" +#define SP_MPVIEW_FILE "mpview.dat3" +#define SP_HWCNTR_FILE "hwcounters" +#define SP_HEAPTRACE_FILE "heaptrace" +#define SP_JCLASSES_FILE "jclasses" +#define SP_DYNAMIC_CLASSES "jdynclasses" +#define SP_RACETRACE_FILE "dataraces" +#define SP_DEADLOCK_FILE "deadlocks" +#define SP_FRINFO_FILE "frameinfo" +#define SP_WARN_FILE "warnings.xml" + +#define SP_LIBCOLLECTOR_NAME "libgp-collector.so" +#define SP_LIBAUDIT_NAME "libcollect-ng.so" + +/* XML tags */ +#define SP_TAG_COLLECTOR "collector" +#define SP_TAG_CPU "cpu" +#define SP_TAG_DATAPTR "dataptr" +#define SP_TAG_EVENT "event" +#define SP_TAG_EXPERIMENT "experiment" +#define SP_TAG_FIELD "field" +#define SP_TAG_PROCESS "process" +#define SP_TAG_PROFILE "profile" +#define SP_TAG_PROFDATA "profdata" +#define SP_TAG_PROFPCKT "profpckt" +#define SP_TAG_SETTING "setting" +#define SP_TAG_STATE "state" +#define SP_TAG_SYSTEM "system" +#define SP_TAG_POWERM "powerm" +#define SP_TAG_FREQUENCY "frequency" +#define SP_TAG_DTRACEFATAL "dtracefatal" + +/* records for log and loadobjects files */ +/* note that these are in alphabetical order */ +#define SP_JCMD_ARCH "architecture" +#define SP_JCMD_ARCHIVE "archive_run" +#define SP_JCMD_ARGLIST "arglist" +#define SP_JCMD_BLKSZ "blksz" +#define SP_JCMD_CERROR "cerror" +#define SP_JCMD_CLASS_LOAD "class_load" +#define SP_JCMD_CLASS_UNLOAD "class_unload" +#define SP_JCMD_COLLENV "collenv" +#define SP_JCMD_COMMENT "comment" +#define SP_JCMD_CPUID "cpuid" +#define SP_JCMD_CWARN "cwarn" +#define SP_JCMD_CWD "cwd" +#define SP_JCMD_CVERSION "cversion" +#define SP_JCMD_DATARACE "datarace" +#define SP_JCMD_DEADLOCK "deadlock" +#define SP_JCMD_DELAYSTART "delay_start" +#define SP_JCMD_DESC_START "desc_start" +#define SP_JCMD_DESC_STARTED "desc_started" +#define SP_JCMD_DVERSION "dversion" +#define SP_JCMD_EXEC_START "exec_start" +#define SP_JCMD_EXEC_ERROR "exec_error" +#define SP_JCMD_EXIT "exit" +#define SP_JCMD_EXPT_DURATION "exp_duration" +#define SP_JCMD_FAKETIME "faketime" +#define SP_JCMD_FN_LOAD "fn_load" +#define SP_JCMD_FN_UNLOAD "fn_unload" +#define SP_JCMD_FUN_MAP "fun_map" +#define SP_JCMD_FUN_UNMAP "fun_unmap" +#define SP_JCMD_HEAPTRACE "heaptrace" +#define SP_JCMD_HOSTNAME "hostname" +#define SP_JCMD_HWC_DEFAULT "hwc_default" +#define SP_JCMD_HW_COUNTER "hwcounter" +#define SP_JCMD_HW_SIM_CTR "hwsimctr" +#define SP_JCMD_IOTRACE "iotrace" +#define SP_JCMD_JCM_LOAD "jcm_load" +#define SP_JCMD_JCM_UNLOAD "jcm_unload" +#define SP_JCMD_JCM_MAP "jcm_map" +#define SP_JCMD_JCM_UNMAP "jcm_unmap" +#define SP_JCMD_JTHREND "jthread_end" +#define SP_JCMD_JTHRSTART "jthread_start" +#define SP_JCMD_GCEND "gc_end" +#define SP_JCMD_GCSTART "gc_start" +#define SP_JCMD_JVERSION "jversion" +//#define SP_JCMD_KPROFILE "kprofile" /* TBR */ +#define SP_JCMD_LIMIT "limit" +#define SP_JCMD_LINETRACE "linetrace" +#define SP_JCMD_LO_OPEN "lo_open" +#define SP_JCMD_LO_CLOSE "lo_close" +#define SP_JCMD_MOD_OPEN "mod_open" +#define SP_JCMD_MPIEXP "MPIexperiment" +#define SP_JCMD_MPI_NO_TRACE "MPI_no_trace" +#define SP_JCMD_MPIOMPVER "mpi_openmpi_version" +#define SP_JCMD_MPITRACEVER "mpi_trace_version" +#define SP_JCMD_MPIPP "mpipp" +#define SP_JCMD_MPIPPERR "mpipp_err" +#define SP_JCMD_MPIPPWARN "mpipp_warn" +#define SP_JCMD_MPISTATE "mpistate" +#define SP_JCMD_MPITRACE "mpitrace" /* backwards compat only */ +#define SP_JCMD_MPVIEW "mpview" +#define SP_JCMD_MSGTRACE "msgtrace" +#define SP_JCMD_NOIDLE "noidle" +#define SP_JCMD_OMPTRACE "omptrace" +#define SP_JCMD_OS "os" +#define SP_JCMD_PAGESIZE "pagesize" +#define SP_JCMD_PAUSE "pause" +#define SP_JCMD_PAUSE_SIG "pause_signal" +#define SP_JCMD_PROFILE "profile" +#define SP_JCMD_RESUME "resume" +#define SP_JCMD_RUN "run" +#define SP_JCMD_SAMPLE "sample" +#define SP_JCMD_SAMPLE_PERIOD "sample_period" +#define SP_JCMD_SAMPLE_SIG "sample_signal" +#define SP_JCMD_SEGMENT_MAP "seg_map" +#define SP_JCMD_SEGMENT_UNMAP "seg_unmap" +#define SP_JCMD_SRCHPATH "search_path" +#define SP_JCMD_STACKBASE "stackbase" +#define SP_JCMD_SUNPERF "sunperf" +#define SP_JCMD_SYNCTRACE "synctrace" +#define SP_JCMD_TERMINATE "terminate" +#define SP_JCMD_THREAD_PAUSE "thread_pause" +#define SP_JCMD_THREAD_RESUME "thread_resume" +#define SP_JCMD_USERNAME "username" +#define SP_JCMD_VERSION "version" +#define SP_JCMD_WSIZE "wsize" + +/* strings naming memory-segments */ +#define SP_MAP_ANON "Anon" +#define SP_MAP_HEAP "Heap" +#define SP_MAP_STACK "Stack" +#define SP_MAP_SHMEM "SHMid" +#define SP_MAP_UNRESOLVABLE "Unresolvable" + +#define SP_UNKNOWN_NAME "(unknown)" + +#define MAX_STACKDEPTH 2048 +#endif /* _EXPERIMENT_H */ diff --git a/gprofng/common/gp-time.h b/gprofng/common/gp-time.h new file mode 100644 index 0000000..7755370 --- /dev/null +++ b/gprofng/common/gp-time.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _GP_TIME_H_ +#define _GP_TIME_H_ + +#include <sys/time.h> + +typedef long long hrtime_t; +typedef struct timespec timestruc_t; + +#define ITIMER_REALPROF ITIMER_PROF +#define NANOSEC 1000000000 +#define MICROSEC 1000000 + +#ifdef __cplusplus +extern "C" +{ +#endif + + hrtime_t gethrtime (void); + hrtime_t gethrvtime (void); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/gprofng/common/hwc_cpus.h b/gprofng/common/hwc_cpus.h new file mode 100644 index 0000000..ff7b303 --- /dev/null +++ b/gprofng/common/hwc_cpus.h @@ -0,0 +1,198 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* Hardware counter profiling: cpu types */ + +#ifndef __HWC_CPUS_H +#define __HWC_CPUS_H + +#define MAX_PICS 20 /* Max # of HW ctrs that can be enabled simultaneously */ + + /* type for specifying CPU register number */ + typedef int regno_t; +#define REGNO_ANY ((regno_t)-1) +#define REGNO_INVALID ((regno_t)-2) + + /* --- Utilities for use with regno_t and reg_list[] --- */ +#define REG_LIST_IS_EMPTY(reg_list) (!(reg_list) || (reg_list)[0] == REGNO_ANY) +#define REG_LIST_EOL(regno) ((regno)==REGNO_ANY) +#define REG_LIST_SINGLE_VALID_ENTRY(reg_list) \ + (((reg_list) && (reg_list)[1] == REGNO_ANY && \ + (reg_list)[0] != REGNO_ANY ) ? (reg_list)[0] : REGNO_ANY) + + /* enum for specifying unknown or uninitialized CPU */ + enum + { + CPUVER_GENERIC = 0, + CPUVER_UNDEFINED = -1 + }; + + // Note: changing an values below may make older HWC experiments unreadable. + // --- Sun/Oracle SPARC --- +#define CPC_ULTRA1 1000 +#define CPC_ULTRA2 1001 +#define CPC_ULTRA3 1002 +#define CPC_ULTRA3_PLUS 1003 +#define CPC_ULTRA3_I 1004 +#define CPC_ULTRA4_PLUS 1005 /* Panther */ +#define CPC_ULTRA4 1017 /* Jaguar */ +#define CPC_ULTRA_T1 1100 /* Niagara1 */ +#define CPC_ULTRA_T2 1101 /* Niagara2 */ +#define CPC_ULTRA_T2P 1102 +#define CPC_ULTRA_T3 1103 +#define CPC_SPARC_T4 1104 +#define CPC_SPARC_T5 1110 +#define CPC_SPARC_T6 1120 +// #define CPC_SPARC_T7 1130 // use CPC_SPARC_M7 +#define CPC_SPARC_M4 1204 /* Obsolete */ +#define CPC_SPARC_M5 1210 +#define CPC_SPARC_M6 1220 +#define CPC_SPARC_M7 1230 +#define CPC_SPARC_M8 1240 + + // --- Intel --- + // Pentium +#define CPC_PENTIUM 2000 +#define CPC_PENTIUM_MMX 2001 +#define CPC_PENTIUM_PRO 2002 +#define CPC_PENTIUM_PRO_MMX 2003 +#define CPC_PENTIUM_4 2017 +#define CPC_PENTIUM_4_HT 2027 + + // Core Microarchitecture (Merom/Menryn) +#define CPC_INTEL_CORE2 2028 +#define CPC_INTEL_NEHALEM 2040 +#define CPC_INTEL_WESTMERE 2042 +#define CPC_INTEL_SANDYBRIDGE 2045 +#define CPC_INTEL_IVYBRIDGE 2047 +#define CPC_INTEL_ATOM 2050 /* Atom*/ +#define CPC_INTEL_HASWELL 2060 +#define CPC_INTEL_BROADWELL 2070 +#define CPC_INTEL_SKYLAKE 2080 +#define CPC_INTEL_UNKNOWN 2499 +#define CPC_AMD_K8C 2500 /* Opteron, Athlon... */ +#define CPC_AMD_FAM_10H 2501 /* Barcelona, Shanghai... */ +#define CPC_AMD_FAM_11H 2502 /* Griffin... */ +#define CPC_AMD_FAM_15H 2503 +#define CPC_KPROF 3003 // OBSOLETE (To support 12.3 and earlier) +#define CPC_FOX 3004 /* pseudo-chip */ + + // --- Fujitsu --- +#define CPC_SPARC64_III 3000 +#define CPC_SPARC64_V 3002 +#define CPC_SPARC64_VI 4003 /* OPL-C */ +#define CPC_SPARC64_VII 4004 /* Jupiter */ +#define CPC_SPARC64_X 4006 /* Athena */ +#define CPC_SPARC64_XII 4010 /* Athena++ */ + +// aarch64. Constants from arch/arm64/include/asm/cputype.h +enum { + ARM_CPU_IMP_ARM = 0x41, + ARM_CPU_IMP_BRCM = 0x42, + ARM_CPU_IMP_CAVIUM = 0x43, + ARM_CPU_IMP_APM = 0x50, + ARM_CPU_IMP_QCOM = 0x51 +}; + +#define AARCH64_VENDORSTR_ARM "ARM" + + /* strings below must match those returned by cpc_getcpuver() */ + typedef struct + { + int cpc2_cpuver; + const char * cpc2_cciname; + } libcpc2_cpu_lookup_t; +#define LIBCPC2_CPU_LOOKUP_LIST \ + {CPC_AMD_K8C , "AMD Opteron & Athlon64"}, \ + {CPC_AMD_FAM_10H , "AMD Family 10h"}, \ + {CPC_AMD_FAM_11H , "AMD Family 11h"}, \ + {CPC_AMD_FAM_15H , "AMD Family 15h Model 01h"}, \ + {CPC_AMD_FAM_15H , "AMD Family 15h Model 02h"},/*future*/ \ + {CPC_AMD_FAM_15H , "AMD Family 15h Model 03h"},/*future*/ \ + {CPC_PENTIUM_4_HT , "Pentium 4 with HyperThreading"}, \ + {CPC_PENTIUM_4 , "Pentium 4"}, \ + {CPC_PENTIUM_PRO_MMX , "Pentium Pro with MMX, Pentium II"}, \ + {CPC_PENTIUM_PRO , "Pentium Pro, Pentium II"}, \ + {CPC_PENTIUM_MMX , "Pentium with MMX"}, \ + {CPC_PENTIUM , "Pentium"}, \ + {CPC_INTEL_CORE2 , "Core Microarchitecture"}, \ + /* Merom: F6M15: Clovertown, Kentsfield, Conroe, Merom, Woodcrest */ \ + /* Merom: F6M22: Merom Conroe */ \ + /* Penryn: F6M23: Yorkfield, Wolfdale, Penryn, Harpertown */ \ + /* Penryn: F6M29: Dunnington */ \ + {CPC_INTEL_NEHALEM , "Intel Arch PerfMon v3 on Family 6 Model 26"},/*Bloomfield, Nehalem EP*/ \ + {CPC_INTEL_NEHALEM , "Intel Arch PerfMon v3 on Family 6 Model 30"},/*Clarksfield, Lynnfield, Jasper Forest*/ \ + {CPC_INTEL_NEHALEM , "Intel Arch PerfMon v3 on Family 6 Model 31"},/*(TBD)*/ \ + {CPC_INTEL_NEHALEM , "Intel Arch PerfMon v3 on Family 6 Model 46"},/*Nehalem EX*/ \ + {CPC_INTEL_WESTMERE , "Intel Arch PerfMon v3 on Family 6 Model 37"},/*Arrandale, Clarskdale*/ \ + {CPC_INTEL_WESTMERE , "Intel Arch PerfMon v3 on Family 6 Model 44"},/*Gulftown, Westmere EP*/ \ + {CPC_INTEL_WESTMERE , "Intel Arch PerfMon v3 on Family 6 Model 47"},/*Westmere EX*/ \ + {CPC_INTEL_SANDYBRIDGE , "Intel Arch PerfMon v3 on Family 6 Model 42"},/*Sandy Bridge*/ \ + {CPC_INTEL_SANDYBRIDGE , "Intel Arch PerfMon v3 on Family 6 Model 45"},/*Sandy Bridge E, SandyBridge-EN, SandyBridge EP*/ \ + {CPC_INTEL_IVYBRIDGE , "Intel Arch PerfMon v3 on Family 6 Model 58"},/*Ivy Bridge*/ \ + {CPC_INTEL_IVYBRIDGE , "Intel Arch PerfMon v3 on Family 6 Model 62"},/*(TBD)*/ \ + {CPC_INTEL_ATOM , "Intel Arch PerfMon v3 on Family 6 Model 28"},/*Atom*/ \ + {CPC_INTEL_HASWELL , "Intel Arch PerfMon v3 on Family 6 Model 60"},/*Haswell*/ \ + {CPC_INTEL_HASWELL , "Intel Arch PerfMon v3 on Family 6 Model 63"},/*Haswell*/ \ + {CPC_INTEL_HASWELL , "Intel Arch PerfMon v3 on Family 6 Model 69"},/*Haswell*/ \ + {CPC_INTEL_HASWELL , "Intel Arch PerfMon v3 on Family 6 Model 70"},/*Haswell*/ \ + {CPC_INTEL_BROADWELL , "Intel Arch PerfMon v3 on Family 6 Model 61"},/*Broadwell*/ \ + {CPC_INTEL_BROADWELL , "Intel Arch PerfMon v3 on Family 6 Model 71"},/*Broadwell*/ \ + {CPC_INTEL_BROADWELL , "Intel Arch PerfMon v3 on Family 6 Model 79"},/*Broadwell*/ \ + {CPC_INTEL_BROADWELL , "Intel Arch PerfMon v3 on Family 6 Model 86"},/*Broadwell*/ \ + {CPC_INTEL_SKYLAKE , "Intel Arch PerfMon v4 on Family 6 Model 78"},/*Skylake*/ \ + {CPC_INTEL_SKYLAKE , "Intel Arch PerfMon v4 on Family 6 Model 85"},/*Skylake*/ \ + {CPC_INTEL_SKYLAKE , "Intel Arch PerfMon v4 on Family 6 Model 94"},/*Skylake*/ \ + {CPC_INTEL_UNKNOWN , "Intel Arch PerfMon"},/*Not yet in table*/ \ + {CPC_SPARC64_III , "SPARC64 III"/*?*/}, \ + {CPC_SPARC64_V , "SPARC64 V"/*?*/}, \ + {CPC_SPARC64_VI , "SPARC64 VI"}, \ + {CPC_SPARC64_VII , "SPARC64 VI & VII"}, \ + {CPC_SPARC64_X , "SPARC64 X"}, \ + {CPC_SPARC64_XII , "SPARC64 XII"}, \ + {CPC_ULTRA_T1 , "UltraSPARC T1"}, \ + {CPC_ULTRA_T2 , "UltraSPARC T2"}, \ + {CPC_ULTRA_T2P , "UltraSPARC T2+"}, \ + {CPC_ULTRA_T3 , "SPARC T3"}, \ + {CPC_SPARC_T4 , "SPARC T4"}, \ + {CPC_SPARC_M4 , "SPARC M4"}, \ + {CPC_SPARC_T5 , "SPARC T5"}, \ + {CPC_SPARC_M5 , "SPARC M5"}, \ + {CPC_SPARC_T6 , "SPARC T6"}, \ + {CPC_SPARC_M6 , "SPARC M6"}, \ + {CPC_SPARC_M7 , "SPARC T7"}, \ + {CPC_SPARC_M7 , "SPARC 3e40"}, \ + {CPC_SPARC_M7 , "SPARC M7"}, \ + {CPC_SPARC_M8 , "SPARC 3e50"}, \ + {CPC_ULTRA4_PLUS , "UltraSPARC IV+"}, \ + {CPC_ULTRA4 , "UltraSPARC IV"}, \ + {CPC_ULTRA3_I , "UltraSPARC IIIi"}, \ + {CPC_ULTRA3_I , "UltraSPARC IIIi & IIIi+"}, \ + {CPC_ULTRA3_PLUS , "UltraSPARC III+"}, \ + {CPC_ULTRA3_PLUS , "UltraSPARC III+ & IV"}, \ + {CPC_ULTRA3 , "UltraSPARC III"}, \ + {CPC_ULTRA2 , "UltraSPARC I&II"}, \ + {CPC_ULTRA1 , "UltraSPARC I&II"}, \ + {ARM_CPU_IMP_APM , AARCH64_VENDORSTR_ARM}, \ + {0, NULL} + /* init like this: + static libcpc2_cpu_lookup_t cpu_table[]={LIBCPC2_CPU_LOOKUP_LIST}; + */ +#endif diff --git a/gprofng/common/hwcdrv.c b/gprofng/common/hwcdrv.c new file mode 100644 index 0000000..caab983 --- /dev/null +++ b/gprofng/common/hwcdrv.c @@ -0,0 +1,1454 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include <sys/syscall.h> +#include <linux/perf_event.h> + +#include "hwcdrv.h" + +/*---------------------------------------------------------------------------*/ +/* macros */ +#define IS_GLOBAL /* Mark global symbols */ + +#include "cpuid.c" /* ftns for identifying a chip */ + +static hdrv_pcbe_api_t hdrv_pcbe_core_api; +static hdrv_pcbe_api_t hdrv_pcbe_opteron_api; +static hdrv_pcbe_api_t *hdrv_pcbe_drivers[] = { + &hdrv_pcbe_core_api, + &hdrv_pcbe_opteron_api, + NULL +}; +#include "opteron_pcbe.c" /* CPU-specific code */ +#include "core_pcbe.c" /* CPU-specific code */ + +extern hwcdrv_api_t hwcdrv_pcl_api; +IS_GLOBAL hwcdrv_api_t *hwcdrv_drivers[] = { + &hwcdrv_pcl_api, + NULL +}; + +/*---------------------------------------------------------------------------*/ + +/* utils for drivers */ +IS_GLOBAL int +hwcdrv_assign_all_regnos (Hwcentry* entries[], unsigned numctrs) +{ + unsigned int pmc_assigned[MAX_PICS]; + unsigned idx; + for (int ii = 0; ii < MAX_PICS; ii++) + pmc_assigned[ii] = 0; + + /* assign the HWCs that we already know about */ + for (idx = 0; idx < numctrs; idx++) + { + regno_t regno = entries[idx]->reg_num; + if (regno == REGNO_ANY) + { + /* check to see if list of possible registers only contains one entry */ + regno = REG_LIST_SINGLE_VALID_ENTRY (entries[idx]->reg_list); + } + if (regno != REGNO_ANY) + { + if (regno < 0 || regno >= MAX_PICS || !regno_is_valid (entries[idx], regno)) + { + logerr (GTXT ("For counter #%d, register %d is out of range\n"), idx + 1, regno); /*!*/ + return HWCFUNCS_ERROR_HWCARGS; + } + TprintfT (DBG_LT2, "hwcfuncs_assign_regnos(): preselected: idx=%d, regno=%d\n", idx, regno); + entries[idx]->reg_num = regno; /* assigning back to entries */ + pmc_assigned[regno] = 1; + } + } + + /* assign HWCs that are currently REGNO_ANY */ + for (idx = 0; idx < numctrs; idx++) + { + if (entries[idx]->reg_num == REGNO_ANY) + { + int assigned = 0; + regno_t *reg_list = entries[idx]->reg_list; + for (; reg_list && *reg_list != REGNO_ANY; reg_list++) + { + regno_t regno = *reg_list; + if (regno < 0 || regno >= MAX_PICS) + { + logerr (GTXT ("For counter #%d, register %d is out of range\n"), idx + 1, regno); /*!*/ + return HWCFUNCS_ERROR_HWCARGS; + } + if (pmc_assigned[regno] == 0) + { + TprintfT (DBG_LT2, "hwcfuncs_assign_regnos(): assigned: idx=%d, regno=%d\n", idx, regno); + entries[idx]->reg_num = regno; /* assigning back to entries */ + pmc_assigned[regno] = 1; + assigned = 1; + break; + } + } + if (!assigned) + { + logerr (GTXT ("Counter '%s' could not be bound to a register\n"), + entries[idx]->name ? entries[idx]->name : "<NULL>"); + return HWCFUNCS_ERROR_HWCARGS; + } + } + } + return 0; +} + +IS_GLOBAL int +hwcdrv_lookup_cpuver (const char * cpcN_cciname) +{ + libcpc2_cpu_lookup_t *plookup; + static libcpc2_cpu_lookup_t cpu_table[] = { + LIBCPC2_CPU_LOOKUP_LIST + }; + if (cpcN_cciname == NULL) + return CPUVER_UNDEFINED; + + /* search table for name */ + for (plookup = cpu_table; plookup->cpc2_cciname; plookup++) + { + int n = strlen (plookup->cpc2_cciname); + if (!strncmp (plookup->cpc2_cciname, cpcN_cciname, n)) + return plookup->cpc2_cpuver; + } + /* unknown, but does have a descriptive string */ + TprintfT (DBG_LT0, "hwcfuncs: CPC2: WARNING: Id of processor '%s' " + "could not be determined\n", + cpcN_cciname); + return CPUVER_GENERIC; +} + +/*---------------------------------------------------------------------------*/ +/* utils to generate x86 register definitions on Linux */ + +/* + * This code is structured as though we're going to initialize the + * HWC by writing the Intel MSR register directly. That is, we + * assume the lowest 16 bits of the event number will have the event + * and that higher bits will set attributes. + * + * While SPARC is different, we can nonetheless use basically the + * same "x86"-named functions: + * + * - The event code will still be 16 bits. It will still + * be in the lowest 16 bits of the event number. Though + * perf_event_code() on SPARC will expect those bits to + * shifted, hwcdrv_pcl.c can easily perform that shift. + * + * - On SPARC we support only two attributes, "user" and "system", + * which hwcdrv_pcl.c already converts to the "exclude_user" + * and "exclude_kernel" fields expected by perf_event_open(). + * "user" and "system" are stored in event bits 16 and 17. + * For M8, a 4-bit mask of supported PICs is stored in bits [23:20]. + */ + +IS_GLOBAL hwcdrv_get_eventnum_fn_t *hwcdrv_get_x86_eventnum = 0; + +static const attr_info_t perfctr_sparc_attrs[] = { + {NTXT ("user"), 0, 0x01, 16}, //usr + {NTXT ("system"), 0, 0x01, 17}, //os + {NULL, 0, 0x00, 0}, +}; +static const attr_info_t perfctr_x64_attrs[] = {/* ok for Core2 & later */ + {NTXT ("umask"), 0, 0xff, 8}, + {NTXT ("user"), 0, 0x01, 16}, //usr + //{NTXT("nouser"), 1, 0x01, 16}, //usr (inverted) + {NTXT ("system"), 0, 0x01, 17}, //os + {NTXT ("edge"), 0, 0x01, 18}, + {NTXT ("pc"), 0, 0x01, 19}, + {NTXT ("inv"), 0, 0x01, 23}, + {NTXT ("cmask"), 0, 0xff, 24}, + {NULL, 0, 0x00, 0}, +}; +const attr_info_t *perfctr_attrs_table = perfctr_x64_attrs; + +static const eventsel_t perfctr_evntsel_enable_bits = (0x01 << 16) | /* usr */ + // (0xff << 0) | /* event*/ + // (0xff << 8) | /* umask */ + // (0x01 << 17) | /* os */ + // (0x01 << 18) | /* edge */ + // (0x01 << 19) | /* pc */ + (0x01 << 20) | /* int */ + // (0x01 << 21) | /* reserved */ + (0x01 << 22) | /* enable */ + // (0x01 << 23) | /* inv */ + // (0xff << 24) | /* cmask */ + 0; + +static int +myperfctr_get_x86_eventnum (const char *eventname, uint_t pmc, + eventsel_t *eventsel, eventsel_t *valid_umask, + uint_t *pmc_sel) +{ + if (hwcdrv_get_x86_eventnum && + !hwcdrv_get_x86_eventnum (eventname, pmc, eventsel, valid_umask, pmc_sel)) + return 0; + + /* check for numerically-specified counters */ + char * endptr; + uint64_t num = strtoull (eventname, &endptr, 0); + if (*eventname && !*endptr) + { + *eventsel = EXTENDED_EVNUM_2_EVSEL (num); + *valid_umask = 0xff; /* allow any umask (unused for SPARC?) */ + *pmc_sel = pmc; + return 0; + } + + /* name does not specify a numeric value */ + *eventsel = (eventsel_t) - 1; + *valid_umask = 0x0; + *pmc_sel = pmc; + return -1; +} + +static int +mask_shift_set (eventsel_t *presult, eventsel_t invalue, + eventsel_t mask, eventsel_t shift) +{ + if (invalue & ~mask) + return -1; /* invalue attempts to set bits outside of mask */ + *presult &= ~(mask << shift); /* clear all the mask bits */ + *presult |= (invalue << shift); /* set bits according to invalue */ + return 0; +} + +static int +set_x86_attr_bits (eventsel_t *result_mask, eventsel_t evnt_valid_umask, + hwcfuncs_attr_t attrs[], int nattrs, const char*nameOnly) +{ + eventsel_t evntsel = *result_mask; + for (int ii = 0; ii < (int) nattrs; ii++) + { + const char *attrname = attrs[ii].ca_name; + eventsel_t attrval = (eventsel_t) attrs[ii].ca_val; + const char *tmpname; + int attr_found = 0; + for (int jj = 0; (tmpname = perfctr_attrs_table[jj].attrname); jj++) + { + if (strcmp (attrname, tmpname) == 0) + { + if (strcmp (attrname, "umask") == 0) + { + if (attrval & ~evnt_valid_umask) + { + logerr (GTXT ("for `%s', allowable umask bits are: 0x%llx\n"), + nameOnly, (long long) evnt_valid_umask); + return -1; + } + } + if (mask_shift_set (&evntsel, + perfctr_attrs_table[jj].is_inverted ? (attrval^1) : attrval, + perfctr_attrs_table[jj].mask, + perfctr_attrs_table[jj].shift)) + { + logerr (GTXT ("`%s' attribute `%s' could not be set to 0x%llx\n"), + nameOnly, attrname, (long long) attrval); + return -1; + } + TprintfT (DBG_LT2, "hwcfuncs: Counter %s, attribute %s set to 0x%llx\n", + nameOnly, attrname, (long long) attrval); + attr_found = 1; + break; + } + } + if (!attr_found) + { + logerr (GTXT ("attribute `%s' is invalid\n"), attrname); + return -1; + } + } + *result_mask = evntsel; + return 0; +} + +IS_GLOBAL int +hwcfuncs_get_x86_eventsel (unsigned int regno, const char *int_name, + eventsel_t *return_event, uint_t *return_pmc_sel) +{ + hwcfuncs_attr_t attrs[HWCFUNCS_MAX_ATTRS + 1]; + unsigned nattrs = 0; + char *nameOnly = NULL; + eventsel_t evntsel = 0; // event number + eventsel_t evnt_valid_umask = 0; + uint_t pmc_sel = 0; + int rc = -1; + *return_event = 0; + *return_pmc_sel = 0; + void *attr_mem = hwcfuncs_parse_attrs (int_name, attrs, HWCFUNCS_MAX_ATTRS, + &nattrs, NULL); + if (!attr_mem) + { + logerr (GTXT ("out of memory, could not parse attributes\n")); + return -1; + } + hwcfuncs_parse_ctr (int_name, NULL, &nameOnly, NULL, NULL, NULL); + if (regno == REGNO_ANY) + { + logerr (GTXT ("reg# could not be determined for `%s'\n"), nameOnly); + goto attr_wrapup; + } + + /* look up evntsel */ + if (myperfctr_get_x86_eventnum (nameOnly, regno, + &evntsel, &evnt_valid_umask, &pmc_sel)) + { + logerr (GTXT ("counter `%s' is not valid\n"), nameOnly); + goto attr_wrapup; + } + TprintfT (DBG_LT1, "hwcfuncs: event=0x%llx pmc=0x%x '%s' nattrs = %u\n", + (long long) evntsel, pmc_sel, nameOnly, nattrs); + + /* determine event attributes */ + eventsel_t evnt_attrs = perfctr_evntsel_enable_bits; + if (set_x86_attr_bits (&evnt_attrs, evnt_valid_umask, attrs, nattrs, nameOnly)) + goto attr_wrapup; + if (evntsel & evnt_attrs) + TprintfT (DBG_LT0, "hwcfuncs: ERROR - evntsel & enable bits overlap: 0x%llx 0x%llx 0x%llx\n", + (long long) evntsel, (long long) evnt_attrs, + (long long) (evntsel & evnt_attrs)); + *return_event = evntsel | evnt_attrs; + *return_pmc_sel = pmc_sel; + rc = 0; + +attr_wrapup: + free (attr_mem); + free (nameOnly); + return rc; +} + +#ifdef __x86_64__ +#define syscall_instr "syscall" +#define syscall_clobber "rcx", "r11", "memory" +#endif +#ifdef __i386__ +#define syscall_instr "int $0x80" +#define syscall_clobber "memory" +#endif + +static inline int +perf_event_open (struct perf_event_attr *hw_event_uptr, pid_t pid, + int cpu, int group_fd, unsigned long flags) +{ + /* It seems that perf_event_open() sometimes fails spuriously, + * even while an immediate retry succeeds. + * So, let's try a few retries if the call fails just to be sure. + */ + int rc; + for (int retry = 0; retry < 5; retry++) + { + rc = syscall (__NR_perf_event_open, hw_event_uptr, pid, cpu, group_fd, flags); + if (rc != -1) + return rc; + } + return rc; +} + +/*---------------------------------------------------------------------------*/ +/* macros & fwd prototypes */ + +#define HWCDRV_API static /* Mark functions used by hwcdrv API */ + +HWCDRV_API int hwcdrv_start (void); +HWCDRV_API int hwcdrv_free_counters (); + +static pid_t +hwcdrv_gettid (void) +{ +#ifndef LIBCOLLECTOR_SRC + return syscall (__NR_gettid); +#elif defined(intel) + pid_t r; + __asm__ __volatile__(syscall_instr + : "=a" (r) : "0" (__NR_gettid) + : syscall_clobber); + return r; +#else + return syscall (__NR_gettid); // FIXUP_XXX_SPARC_LINUX // write gettid in asm +#endif +} + +/*---------------------------------------------------------------------------*/ +/* types */ + +#define NPAGES_PER_BUF 1 // number of pages to be used for perf_event samples +// must be a power of 2 + +/*---------------------------------------------------------------------------*/ + +/* typedefs */ + +typedef struct +{ // event (hwc) definition + unsigned int reg_num; // PMC assignment, potentially for detecting conflicts + eventsel_t eventsel; // raw event bits (Intel/AMD) + uint64_t counter_preload; // number of HWC events before signal + struct perf_event_attr hw; // perf_event definition + hrtime_t min_time; // minimum time we're targeting between events + char *name; +} perf_event_def_t; + +typedef struct +{ // runtime state of perf_event buffer + void *buf; // pointer to mmapped buffer + size_t pagesz; // size of pages +} buffer_state_t; + +typedef struct +{ // runtime state of counter values + uint64_t prev_ena_ts; // previous perf_event "enabled" time + uint64_t prev_run_ts; // previous perf_event "running" time + uint64_t prev_value; // previous HWC value +} counter_value_state_t; + +typedef struct +{ // per-counter information + perf_event_def_t *ev_def; // global HWC definition for one counter + int fd; // perf_event fd + buffer_state_t buf_state; // perf_event buffer's state + counter_value_state_t value_state; // counter state + int needs_restart; // workaround for dbx failure to preserve si_fd + uint64_t last_overflow_period; + hrtime_t last_overflow_time; +} counter_state_t; + +typedef struct +{ // per-thread context + counter_state_t *ctr_list; + int signal_fd; // fd that caused the most recent signal + pthread_t tid; // for debugging signal delivery problems +} hdrv_pcl_ctx_t; + +/*---------------------------------------------------------------------------*/ + +/* static variables */ +static struct +{ + int library_ok; + int internal_open_called; + hwcfuncs_tsd_get_fn_t find_vpc_ctx; + unsigned hwcdef_cnt; /* number of *active* hardware counters */ + hwcdrv_get_events_fn_t *get_events; +} hdrv_pcl_state; + +static hwcdrv_about_t hdrv_pcl_about = {.cpcN_cpuver = CPUVER_UNDEFINED}; +static perf_event_def_t global_perf_event_def[MAX_PICS]; + +#define COUNTERS_ENABLED() (hdrv_pcl_state.hwcdef_cnt) + + +/* perf_event buffer formatting and handling */ +static void +reset_buf (buffer_state_t *bufstate) +{ + TprintfT (0, "hwcdrv: ERROR: perf_event reset_buf() called!\n"); + struct perf_event_mmap_page *metadata = bufstate->buf; + if (metadata) + metadata->data_tail = metadata->data_head; +} + +static int +skip_buf (buffer_state_t *bufstate, size_t sz) +{ + TprintfT (DBG_LT1, "hwcdrv: WARNING: perf_event skip_buf called!\n"); + struct perf_event_mmap_page *metadata = bufstate->buf; + if (metadata == NULL) + return -1; + size_t pgsz = bufstate->pagesz; + size_t bufsz = NPAGES_PER_BUF*pgsz; + uint64_t d_tail = metadata->data_tail; + uint64_t d_head = metadata->data_head; + + // validate request size + if (sz > d_head - d_tail || sz >= bufsz) + { + reset_buf (bufstate); + return -1; + } + metadata->data_tail = d_tail + sz; // advance tail + return 0; +} + +static int +read_buf (buffer_state_t *bufstate, void *buf, size_t sz) +{ + struct perf_event_mmap_page *metadata = bufstate->buf; + if (metadata == NULL) + return -1; + size_t pgsz = bufstate->pagesz; + size_t bufsz = NPAGES_PER_BUF*pgsz; + uint64_t d_tail = metadata->data_tail; + uint64_t d_head = metadata->data_head; + + // validate request size + if (sz > d_head - d_tail || sz >= bufsz) + { + reset_buf (bufstate); + return -1; + } + char *buf_base = ((char *) metadata) + pgsz; // start of data buffer + uint64_t start_pos = d_tail & (bufsz - 1); // char offset into data buffer + size_t nbytes = sz; + if (start_pos + sz > bufsz) + { + // will wrap past end of buffer + nbytes = bufsz - start_pos; + memcpy (buf, buf_base + start_pos, nbytes); + start_pos = 0; // wrap to start + buf = (void *) (((char *) buf) + nbytes); + nbytes = sz - nbytes; + } + memcpy (buf, buf_base + start_pos, nbytes); + metadata->data_tail += sz; + return 0; +} + +static int +read_u64 (buffer_state_t *bufstate, uint64_t *value) +{ + return read_buf (bufstate, value, sizeof (uint64_t)); +} + +static int +read_sample (counter_state_t *ctr_state, int msgsz, uint64_t *rvalue, + uint64_t *rlost) +{ + // returns count of bytes read + buffer_state_t *bufstate = &ctr_state->buf_state; + counter_value_state_t *cntstate = &ctr_state->value_state; + int readsz = 0; + + // PERF_SAMPLE_IP + uint64_t ipc = 0; + int rc = read_u64 (bufstate, &ipc); + if (rc) + return -1; + readsz += sizeof (uint64_t); + + // PERF_SAMPLE_READ: value + uint64_t value = 0; + rc = read_u64 (bufstate, &value); + if (rc) + return -2; + readsz += sizeof (uint64_t); + + /* Bug 20806896 + * Old Linux kernels (e.g. 2.6.32) on certain systems return enabled and + * running times in the sample data that correspond to the metadata times + * metadata->time_enabled + * metadata->time_running + * from the PREVIOUS (not current) sample. Probably just ignore this bug + * since it's on old kernels and we only use the enabled and running times + * to construct loss_estimate. + */ + // PERF_SAMPLE_READ: PERF_FORMAT_ENABLED + uint64_t enabled_time = 0; + rc = read_u64 (bufstate, &enabled_time); + if (rc) + return -3; + readsz += sizeof (uint64_t); + + // PERF_SAMPLE_READ: PERF_FORMAT_RUNNING + uint64_t running_time = 0; + rc = read_u64 (bufstate, &running_time); + if (rc) + return -4; + readsz += sizeof (uint64_t); + + uint64_t value_delta = value - cntstate->prev_value; + uint64_t enabled_delta = enabled_time - cntstate->prev_ena_ts; + uint64_t running_delta = running_time - cntstate->prev_run_ts; + cntstate->prev_value = value; + cntstate->prev_ena_ts = enabled_time; + cntstate->prev_run_ts = running_time; + + // 24830461 need workaround for Linux anomalous HWC skid overrun + int set_error_flag = 0; + if (value_delta > 2 * ctr_state->last_overflow_period + 2000 /* HWC_SKID_TOLERANCE */) + set_error_flag = 1; + + uint64_t loss_estimate = 0; // estimate loss of events caused by multiplexing + if (running_delta == enabled_delta) + { + // counter was running 100% of time, no multiplexing + } + else if (running_delta == 0) + loss_estimate = 1; // token amount to aid in debugging perfctr oddities + else if ((running_delta > enabled_delta) || (enabled_delta & 0x1000000000000000ll)) + { + // running should be smaller than enabled, can't estimate + /* + * 21418391 HWC can have a negative count + * + * We've also seen enabled not only be smaller than running + * but in fact go negative. Guard against this. + */ + loss_estimate = 2; // token amount to aid in debugging perfctr oddities + } + else + { + // counter was running less than 100% of time + // Example: ena=7772268 run=6775669 raw_value=316004 scaled_value=362483 loss_est=46479 + uint64_t scaled_delta = (double) value_delta * enabled_delta / running_delta; + value_delta = scaled_delta; +#if 0 + // We should perhaps warn the user that multiplexing is going on, + // but hwcdrv_pcl.c doesn't know about the collector_interface, SP_JCMD_COMMENT, or COL_COMMENT_* values. + // For now we simply don't report. + // Perhaps we should address the issue not here but in the caller collector_sigemt_handler(), + // but at that level "lost" has a meaning that's considerably broader than just multiplexing. + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s %d -> %d</event>\n", + SP_JCMD_COMMENT, COL_COMMENT_HWCADJ, global_perf_event_def[idx].name, + ctr_list[idx].last_overflow_period, new_period); +#endif + } + TprintfT ((loss_estimate || set_error_flag) ? DBG_LT1 : DBG_LT3, + "hwcdrv: '%s' ipc=0x%llx ena=%llu run=%llu " + "value_delta=%lld(0x%llx) loss_est=%llu %s error_flag='%s'\n", + ctr_state->ev_def->name, (long long) ipc, + (long long) enabled_delta, (long long) running_delta, + (long long) value_delta, (long long) value_delta, + (unsigned long long) loss_estimate, + loss_estimate ? ", WARNING - SCALED" : "", + set_error_flag ? ", ERRORFLAG" : ""); + if (set_error_flag == 1) + value_delta |= (1ULL << 63) /* HWCVAL_ERR_FLAG */; + *rvalue = value_delta; + *rlost = loss_estimate; + if (readsz != msgsz) + { + TprintfT (0, "hwcdrv: ERROR: perf_event sample not fully parsed\n"); + return -5; + } + return 0; +} + +static void +dump_perf_event_attr (struct perf_event_attr *at) +{ + TprintfT (DBG_LT2, "dump_perf_event_attr: size=%d type=%d sample_period=%lld\n" + " config=0x%llx config1=0x%llx config2=0x%llx wakeup_events=%lld __reserved_1=%lld\n", + (int) at->size, (int) at->type, (unsigned long long) at->sample_period, + (unsigned long long) at->config, (unsigned long long) at->config1, + (unsigned long long) at->config2, (unsigned long long) at->wakeup_events, + (unsigned long long) at->__reserved_1); +#define DUMP_F(fld) if (at->fld) TprintfT(DBG_LT2, " %-10s : %lld\n", #fld, (long long) at->fld) + DUMP_F (disabled); + DUMP_F (inherit); + DUMP_F (pinned); + DUMP_F (exclusive); + DUMP_F (exclude_user); + DUMP_F (exclude_kernel); + DUMP_F (exclude_hv); + DUMP_F (exclude_idle); + // DUMP_F(xmmap); + DUMP_F (comm); + DUMP_F (freq); + DUMP_F (inherit_stat); + DUMP_F (enable_on_exec); + DUMP_F (task); + DUMP_F (watermark); +} + +static void +init_perf_event (struct perf_event_attr *hw, uint64_t event, uint64_t period) +{ + memset (hw, 0, sizeof (struct perf_event_attr)); + hw->size = sizeof (struct perf_event_attr); // fwd/bwd compat + +#if defined(__i386__) || defined(__x86_64) + //note: Nehalem/Westmere OFFCORE_RESPONSE in upper 32 bits + hw->config = event; + hw->type = PERF_TYPE_RAW; // hw/sw/trace/raw... +#elif defined(__aarch64__) + hw->type = (event >> 24) & 7; + hw->config = event & 0xff; +#elif defined(sparc) + //SPARC needs to be shifted up 16 bits + hw->config = (event & 0xFFFF) << 16; // uint64_t event + uint64_t regs = (event >> 20) & 0xf; // see sparc_pcbe.c + hw->config |= regs << 4; // for M8, supported PICs need to be placed at bits [7:4] + hw->type = PERF_TYPE_RAW; // hw/sw/trace/raw... +#endif + + hw->sample_period = period; + hw->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_READ | + // PERF_SAMPLE_TID | + // PERF_SAMPLE_TIME | // possibly interesting + // PERF_SAMPLE_ADDR | + PERF_SAMPLE_READ | // HWC value + // PERF_SAMPLE_CALLCHAIN | // interesting + // PERF_SAMPLE_ID | + // PERF_SAMPLE_CPU | // possibly interesting + // PERF_SAMPLE_PERIOD | + // PERF_SAMPLE_STREAM_ID | + // PERF_SAMPLE_RAW | + 0; + hw->read_format = + PERF_FORMAT_TOTAL_TIME_ENABLED | // detect when hwc not scheduled + PERF_FORMAT_TOTAL_TIME_RUNNING | // detect when hwc not scheduled + // PERF_FORMAT_ID | + // PERF_FORMAT_GROUP | + 0; + hw->disabled = 1; /* off by default */ + + // Note: the following override config.priv bits! + hw->exclude_user = (event & (1 << 16)) == 0; /* don't count user */ + hw->exclude_kernel = (event & (1 << 17)) == 0; /* ditto kernel */ + hw->exclude_hv = 1; /* ditto hypervisor */ + hw->wakeup_events = 1; /* wakeup every n events */ + dump_perf_event_attr (hw); +} + +static int +start_one_ctr (int ii, size_t pgsz, hdrv_pcl_ctx_t * pctx, char *error_string) +{ + // pe_attr should have been initialized in hwcdrv_create_counters() + struct perf_event_attr pe_attr; + memcpy (&pe_attr, &global_perf_event_def[ii].hw, sizeof (pe_attr)); + + // but we adjust the period, so make sure that pctx->ctr_list[ii].last_overflow_period has been set + pe_attr.sample_period = pctx->ctr_list[ii].last_overflow_period; + + int hwc_fd = perf_event_open (&pe_attr, pctx->tid, -1, -1, 0); + if (hwc_fd == -1) + { + TprintfT (DBG_LT1, "%s idx=%d perf_event_open failed, errno=%d\n", + error_string, ii, errno); + return 1; + } + + size_t buffer_area_sz = (NPAGES_PER_BUF + 1) * pgsz; // add a page for metadata + void * buf = mmap (NULL, buffer_area_sz, //YXXX is this a safe call? + PROT_READ | PROT_WRITE, MAP_SHARED, hwc_fd, 0); + if (buf == MAP_FAILED) + { + TprintfT (0, "sz = %ld, pgsz = %ld\n err=%s idx=%d mmap failed: %s\n", + (long) buffer_area_sz, (long) pgsz, error_string, ii, strerror (errno)); + return 1; + } + pctx->ctr_list[ii].ev_def = &global_perf_event_def[ii]; // why do we set ev_def? we never seem to use it + pctx->ctr_list[ii].fd = hwc_fd; + pctx->ctr_list[ii].buf_state.buf = buf; + pctx->ctr_list[ii].buf_state.pagesz = pgsz; + pctx->ctr_list[ii].value_state.prev_ena_ts = 0; + pctx->ctr_list[ii].value_state.prev_run_ts = 0; + pctx->ctr_list[ii].value_state.prev_value = 0; + pctx->ctr_list[ii].last_overflow_time = gethrtime (); + + /* set async mode */ + long flags = fcntl (hwc_fd, F_GETFL, 0) | O_ASYNC; + int rc = fcntl (hwc_fd, F_SETFL, flags); + if (rc == -1) + { + TprintfT (0, "%s idx=%d O_ASYNC failed\n", error_string, ii); + return 1; + } + + /* + * set lwp ownership of the fd + * See BUGS section of "man perf_event_open": + * The F_SETOWN_EX option to fcntl(2) is needed to properly get + * overflow signals in threads. This was introduced in Linux 2.6.32. + * Legacy references: + * see http://lkml.org/lkml/2009/8/4/128 + * google man fcntl F_SETOWN_EX -conflict + * "From Linux 2.6.32 onward, use F_SETOWN_EX to target + * SIGIO and SIGURG signals at a particular thread." + * http://icl.cs.utk.edu/papi/docs/da/d2a/examples__v2_8x_2self__smpl__multi_8c.html + * See 2010 CSCADS presentation by Eranian + */ + struct f_owner_ex fowner_ex; + fowner_ex.type = F_OWNER_TID; + fowner_ex.pid = pctx->tid; + rc = fcntl (hwc_fd, F_SETOWN_EX, (unsigned long) &fowner_ex); + if (rc == -1) + { + TprintfT (0, "%s idx=%d F_SETOWN failed\n", error_string, ii); + return 1; + } + + /* Use sigio so handler can determine FD via siginfo->si_fd. */ + rc = fcntl (hwc_fd, F_SETSIG, SIGIO); + if (rc == -1) + { + TprintfT (0, "%s idx=%d F_SETSIG failed\n", error_string, ii); + return 1; + } + return 0; +} + +static int +stop_one_ctr (int ii, counter_state_t *ctr_list) +{ + int hwc_rc = 0; + if (-1 == ioctl (ctr_list[ii].fd, PERF_EVENT_IOC_DISABLE, 1)) + { + TprintfT (0, "hwcdrv: ERROR: PERF_EVENT_IOC_DISABLE #%d failed: errno=%d\n", ii, errno); + hwc_rc = HWCFUNCS_ERROR_GENERIC; + } + void *buf = ctr_list[ii].buf_state.buf; + if (buf) + { + size_t bufsz = (NPAGES_PER_BUF + 1) * ctr_list[ii].buf_state.pagesz; + ctr_list[ii].buf_state.buf = NULL; + int tmprc = munmap (buf, bufsz); + if (tmprc) + { + TprintfT (0, "hwcdrv: ERROR: munmap() #%d failed: errno=%d\n", ii, errno); + hwc_rc = HWCFUNCS_ERROR_GENERIC; + } + } + if (-1 == close (ctr_list[ii].fd)) + { + TprintfT (0, "hwcdrv: ERROR: close(fd) #%d failed: errno=%d\n", ii, errno); + hwc_rc = HWCFUNCS_ERROR_GENERIC; + } + return hwc_rc; +} + +/* HWCDRV_API for thread-specific actions */ +HWCDRV_API int +hwcdrv_lwp_init (void) +{ + return hwcdrv_start (); +} + +HWCDRV_API void +hwcdrv_lwp_fini (void) +{ + hwcdrv_free_counters (); /* also sets pctx->ctr_list=NULL; */ +} + +/* open */ +static int +hdrv_pcl_internal_open () +{ + if (hdrv_pcl_state.internal_open_called) + { + TprintfT (0, "hwcdrv: WARNING: hdrv_pcl_internal_open: already called\n"); + return HWCFUNCS_ERROR_ALREADY_CALLED; + } + + // determine if PCL is available + perf_event_def_t tmp_event_def; + memset (&tmp_event_def, 0, sizeof (tmp_event_def)); + struct perf_event_attr *pe_attr = &tmp_event_def.hw; + init_perf_event (pe_attr, 0, 0); + pe_attr->type = PERF_TYPE_HARDWARE; // specify abstracted HW event + pe_attr->config = PERF_COUNT_HW_INSTRUCTIONS; // specify abstracted insts + int hwc_fd = perf_event_open (pe_attr, + 0, // pid/tid, 0 is self + -1, // cpu, -1 is per-thread mode + -1, // group_fd, -1 is root + 0); // flags + if (hwc_fd == -1) + { + TprintfT (DBG_LT1, "hwcdrv: WARNING: hdrv_pcl_internal_open:" + " perf_event_open() failed, errno=%d\n", errno); + goto internal_open_error; + } + + /* see if the PCL is new enough to know about F_SETOWN_EX */ + struct f_owner_ex fowner_ex; + fowner_ex.type = F_OWNER_TID; + fowner_ex.pid = hwcdrv_gettid (); // "pid=tid" is correct w/F_OWNER_TID + if (fcntl (hwc_fd, F_SETOWN_EX, (unsigned long) &fowner_ex) == -1) + { + TprintfT (DBG_LT1, "hwcdrv: WARNING: hdrv_pcl_internal_open: " + "F_SETOWN failed, errno=%d\n", errno); + close (hwc_fd); + goto internal_open_error; + } + close (hwc_fd); + + hdrv_pcl_state.internal_open_called = 1; + hdrv_pcl_state.library_ok = 1; // set to non-zero to show it's initted + hdrv_pcl_about.cpcN_cpuver = CPUVER_UNDEFINED; + TprintfT (DBG_LT2, "hwcdrv: hdrv_pcl_internal_open()\n"); + for (int ii = 0; hdrv_pcbe_drivers[ii]; ii++) + { + hdrv_pcbe_api_t *ppcbe = hdrv_pcbe_drivers[ii]; + if (!ppcbe->hdrv_pcbe_init ()) + { + hdrv_pcl_about.cpcN_cciname = ppcbe->hdrv_pcbe_impl_name (); + hdrv_pcl_about.cpcN_cpuver = hwcdrv_lookup_cpuver (hdrv_pcl_about.cpcN_cciname); + if (hdrv_pcl_about.cpcN_cpuver == CPUVER_UNDEFINED) + goto internal_open_error; + hdrv_pcl_about.cpcN_npics = ppcbe->hdrv_pcbe_ncounters (); + hdrv_pcl_about.cpcN_docref = ppcbe->hdrv_pcbe_cpuref (); + hdrv_pcl_state.get_events = ppcbe->hdrv_pcbe_get_events; + hwcdrv_get_x86_eventnum = ppcbe->hdrv_pcbe_get_eventnum; + break; + } + } + if (hdrv_pcl_about.cpcN_npics > MAX_PICS) + { + TprintfT (0, "hwcdrv: WARNING: hdrv_pcl_internal_open:" + " reducing number of HWCs from %u to %u on processor '%s'\n", + hdrv_pcl_about.cpcN_npics, MAX_PICS, hdrv_pcl_about.cpcN_cciname); + hdrv_pcl_about.cpcN_npics = MAX_PICS; + } + TprintfT (DBG_LT1, "hwcdrv: hdrv_pcl_internal_open:" + " perf_event cpuver=%d, name='%s'\n", + hdrv_pcl_about.cpcN_cpuver, hdrv_pcl_about.cpcN_cciname); + return 0; + +internal_open_error: + hdrv_pcl_about.cpcN_cpuver = CPUVER_UNDEFINED; + hdrv_pcl_about.cpcN_npics = 0; + hdrv_pcl_about.cpcN_docref = NULL; + hdrv_pcl_about.cpcN_cciname = NULL; + return HWCFUNCS_ERROR_NOT_SUPPORTED; +} + +static void * +single_thread_tsd_ftn () +{ + static hdrv_pcl_ctx_t tsd_context; + return &tsd_context; +} + +/* HWCDRV_API */ +HWCDRV_API int +hwcdrv_init (hwcfuncs_abort_fn_t abort_ftn, int *tsd_sz) +{ + hdrv_pcl_state.find_vpc_ctx = single_thread_tsd_ftn; + if (tsd_sz) + *tsd_sz = sizeof (hdrv_pcl_ctx_t); + + if (hdrv_pcl_state.internal_open_called) + return HWCFUNCS_ERROR_ALREADY_CALLED; + return hdrv_pcl_internal_open (); +} + +HWCDRV_API void +hwcdrv_get_info (int *cpuver, const char **cciname, uint_t *npics, + const char **docref, uint64_t *support) +{ + if (cpuver) + *cpuver = hdrv_pcl_about.cpcN_cpuver; + if (cciname) + *cciname = hdrv_pcl_about.cpcN_cciname; + if (npics) + *npics = hdrv_pcl_about.cpcN_npics; + if (docref) + *docref = hdrv_pcl_about.cpcN_docref; + if (support) + *support = HWCFUNCS_SUPPORT_OVERFLOW_PROFILING | HWCFUNCS_SUPPORT_OVERFLOW_CTR_ID; +} + +HWCDRV_API int +hwcdrv_enable_mt (hwcfuncs_tsd_get_fn_t tsd_ftn) +{ + if (tsd_ftn) + hdrv_pcl_state.find_vpc_ctx = tsd_ftn; + else + { + TprintfT (0, "hwcdrv: ERROR: enable_mt(): tsd_ftn==NULL\n"); + return HWCFUNCS_ERROR_UNAVAIL; + } + return 0; +} + +HWCDRV_API int +hwcdrv_get_descriptions (hwcf_hwc_cb_t *hwc_cb, hwcf_attr_cb_t *attr_cb) +{ + int count = 0; + if (hwc_cb && hdrv_pcl_state.get_events) + count = hdrv_pcl_state.get_events (hwc_cb); + if (attr_cb) + for (int ii = 0; perfctr_attrs_table && perfctr_attrs_table[ii].attrname; ii++) + attr_cb (perfctr_attrs_table[ii].attrname); + if (!count) + return -1; + return 0; +} + +HWCDRV_API int +hwcdrv_assign_regnos (Hwcentry* entries[], unsigned numctrs) +{ + return hwcdrv_assign_all_regnos (entries, numctrs); +} + +static int +internal_hwc_start (int fd) +{ + int rc = ioctl (fd, PERF_EVENT_IOC_REFRESH, 1); + if (rc == -1) + { + TprintfT (DBG_LT0, "hwcdrv: ERROR: internal_hwc_start:" + " PERF_EVENT_IOC_REFRESH(fd=%d) failed: errno=%d\n", fd, errno); + return HWCFUNCS_ERROR_UNAVAIL; + } + TprintfT (DBG_LT3, "hwcdrv: internal_hwc_start(fd=%d)\n", fd); + return 0; +} + +HWCDRV_API int +hwcdrv_overflow (siginfo_t *si, hwc_event_t *eventp, hwc_event_t *lost_events) +{ + /* set expired counters to overflow value and all others to 0 */ + /* return 0: OK, counters should be restarted */ + /* return non-zero: eventp not set, counters should not be restarted */ + /* clear return values */ + int ii; + for (ii = 0; ii < hdrv_pcl_state.hwcdef_cnt; ii++) + { + eventp->ce_pic[ii] = 0; + lost_events->ce_pic[ii] = 0; + } + hrtime_t sig_ts = gethrtime (); //YXXX get this from HWC event? + eventp->ce_hrt = sig_ts; + lost_events->ce_hrt = sig_ts; + + /* determine source signal */ + int signal_fd = -1; + switch (si->si_code) + { + case POLL_HUP: /* expected value from pcl */ + /* According to Stephane Eranian: + * "expect POLL_HUP instead of POLL_IN because we are + * in one-shot mode (IOC_REFRESH)" + */ + signal_fd = si->si_fd; + break; + case SI_TKILL: /* event forwarded by tkill */ + /* DBX can only forward SI_TKILL when it detects POLL_HUP + * unfortunately, this means that si->si_fd has been lost... + * We need to process the buffers, but we don't know the fd! + */ + TprintfT (DBG_LT0, "hwcdrv: sig_ts=%llu: WARNING: hwcdrv_overflow:" + " SI_TKILL detected\n", sig_ts); + break; + default: + // "sometimes we see a POLL_IN (1) with very high event rates," + // according to eranian(?) + TprintfT (DBG_LT0, "hwcdrv: sig_ts=%llu: ERROR: hwcdrv_overflow:" + " unexpected si_code 0x%x\n", sig_ts, si->si_code); + return HWCFUNCS_ERROR_GENERIC; + } + + hdrv_pcl_ctx_t * pctx = hdrv_pcl_state.find_vpc_ctx (); + if (!pctx) + { + TprintfT (DBG_LT0, "hwcdrv: sig_ts=%llu: ERROR: hwcdrv_overflow:" + " tsd context is NULL\n", sig_ts); + return HWCFUNCS_ERROR_UNEXPECTED; + } + counter_state_t * ctr_list = (counter_state_t *) pctx->ctr_list; + if (!ctr_list) + { + TprintfT (DBG_LT0, "hwcdrv: sig_ts=%llu: WARNING: hwcdrv_overflow:" + " ctr_list is NULL\n", sig_ts); + return HWCFUNCS_ERROR_UNEXPECTED; + } + + /* clear needs_restart flag */ + for (ii = 0; ii < hdrv_pcl_state.hwcdef_cnt; ii++) + ctr_list[ii].needs_restart = 0; + + /* attempt to identify the counter to read */ + int signal_idx = -1; + pctx->signal_fd = signal_fd; // save the signal provided by siginfo_t + if (signal_fd != -1) + { + for (ii = 0; ii < hdrv_pcl_state.hwcdef_cnt; ii++) + { + if (ctr_list[ii].fd == signal_fd) + { + signal_idx = ii; + break; + } + } + } + + if (signal_idx < 0) + { + TprintfT (DBG_LT0, "hwcdrv: sig_ts=%llu: ERROR: hwcdrv_overflow:" + " pmc not determined!\n", sig_ts); + lost_events->ce_pic[0] = 1; /* record a bogus value into experiment */ + // note: bogus value may get overwritten in loop below + } + + /* capture sample(s). In addition to signal_idx, check other counters. */ + struct perf_event_header sheader; + int idx; + for (idx = 0; idx < hdrv_pcl_state.hwcdef_cnt; idx++) + { + int num_recs = 0; + while (1) + { + /* check for samples */ + struct perf_event_mmap_page *metadata = ctr_list[idx].buf_state.buf; + if (metadata == NULL) + break; // empty + if (metadata->data_tail == metadata->data_head) + break; // empty + + /* read header */ + if (read_buf (&ctr_list[idx].buf_state, &sheader, sizeof (sheader))) + break; + num_recs++; + + /* check for PERF_RECORD_SAMPLE */ + size_t datasz = sheader.size - sizeof (struct perf_event_header); + if (sheader.type != PERF_RECORD_SAMPLE) + { + TprintfT (DBG_LT2, "hwcdrv: sig_ts=%llu: WARNING: hwcdrv_overflow:" + " unexpected recd type=%d\n", + sig_ts, sheader.type); + if (skip_buf (&ctr_list[idx].buf_state, datasz)) + { + TprintfT (DBG_LT0, "hwcdrv: sig_ts=%llu: ERROR: hwcdrv_overflow:" + " skip recd type=%d failed\n", sig_ts, sheader.type); + lost_events->ce_pic[idx] = 4; /* record a bogus value */ + break; // failed to skip buffer?? + } + lost_events->ce_pic[idx] = 2; /* record a bogus value */ + continue; // advance to next record + } + + /* type is PERF_RECORD_SAMPLE */ + uint64_t value, lostv; + if (read_sample (&ctr_list[idx], datasz, &value, &lostv)) + { + TprintfT (DBG_LT0, "hwcdrv: sig_ts=%llu: ERROR: hwcdrv_overflow:" + " read_sample() failed\n", sig_ts); + lost_events->ce_pic[idx] = 3; // record a bogus value + break; // failed to read sample data?? + } + TprintfT (DBG_LT3, "hwcdrv: sig_ts=%llu: hwcdrv_overflow:" + " idx=%d value=%llu lost=%llu\n", (unsigned long long) sig_ts, + idx, (unsigned long long) value, (unsigned long long) lostv); + if (eventp->ce_pic[idx]) + { + TprintfT (DBG_LT2, "hwcdrv: sig_ts=%llu: WARNING: hwcdrv_overflow:" + " idx=%d previous sample recorded as lost_event\n", sig_ts, idx); + lost_events->ce_pic[idx] += eventp->ce_pic[idx]; + } + eventp->ce_pic[idx] = value; + lost_events->ce_pic[idx] += lostv; + } + + /* debug output for unexpected (but common) cases */ + if (idx == signal_idx) + { + if (num_recs != 1) + TprintfT (DBG_LT2, "hwcdrv: sig_ts=%llu: WARNING: hwcdrv_overflow:" + " %d records for signal_idx=%d\n", sig_ts, num_recs, signal_idx); + } + else if (num_recs) + TprintfT (DBG_LT2, "hwcdrv: sig_ts=%llu: WARNING: hwcdrv_overflow:" + " %d unexpected record(s) for idx=%d (signal_idx=%d)\n", + sig_ts, num_recs, idx, signal_idx); + + /* trigger counter restart whenever records were found */ + if (num_recs) + { + /* check whether to adapt the overflow interval */ + /* This is the Linux version. + * The Solaris version is in hwprofile.c collector_update_overflow_counters(). + */ + hrtime_t min_time = global_perf_event_def[idx].min_time; + if (min_time > 0 // overflow interval is adaptive + && sig_ts - ctr_list[idx].last_overflow_time < min_time) // last interval below min + { + /* pick a new overflow interval */ + /* roughly doubled, but add funny numbers */ + /* hopefully the result is prime or not a multiple of some # of ops/loop */ + uint64_t new_period = 2 * ctr_list[idx].last_overflow_period + 37; +#if 0 + // On Solaris, we report the adjustment to the log file. + // On Linux it's hard for us to do so since hwcdrv_pcl.c doesn't know about collector_interface, SP_JCMD_COMMENT, or COL_COMMENT_HWCADJ. + // For now we simply don't report. + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s %d -> %d</event>\n", + SP_JCMD_COMMENT, COL_COMMENT_HWCADJ, global_perf_event_def[idx].name, + ctr_list[idx].last_overflow_period, new_period); +#endif + /* There are a variety of ways of resetting the period on Linux. + * The most elegant is + * ioctl(fd,PERF_EVENT_IOC_PERIOD,&period) + * but check the perf_event_open man page for PERF_EVENT_IOC_PERIOD: + * > Prior to Linux 2.6.36 this ioctl always failed due to a bug in the kernel. + * > Prior to Linux 3.14 (or 3.7 on ARM), the new period did not take effect + * until after the next overflow. + * So we're kind of stuck shutting the fd down and restarting it with the new period. + */ + if (stop_one_ctr (idx, ctr_list)) + { + // EUGENE figure out what to do on error + } + ctr_list[idx].last_overflow_period = new_period; + if (start_one_ctr (idx, ctr_list[idx].buf_state.pagesz, pctx, "hwcdrv: ERROR: hwcdrv_overflow (readjust overflow):")) + { + // EUGENE figure out what to do on error + } + } + ctr_list[idx].last_overflow_time = sig_ts; +#if 0 + ctr_list[idx].needs_restart = 1; +#else // seems to be more reliable to restart here instead of hwcdrv_sighlr_restart() + internal_hwc_start (ctr_list[idx].fd); +#endif + } + } + return 0; // OK to restart counters +} + +HWCDRV_API int +hwcdrv_sighlr_restart (const hwc_event_t *pp) +{ +#if 0 // restarting here doesn't seem to work as well as restarting in hwcdrv_overflow() + hdrv_pcl_ctx_t * pctx = hdrv_pcl_state.find_vpc_ctx (); + if (!pctx) + { + TprintfT (DBG_LT0, "hwcdrv: ERROR: hwcdrv_sighlr_restart: find_vpc_ctx()==NULL\n"); + return -1; + } + counter_state_t * ctr_list = (counter_state_t *) pctx->ctr_list; + if (!ctr_list) + { + TprintfT (DBG_LT0, "hwcdrv: WARNING: hwcdrv_sighlr_restart: ctr_list is NULL\n"); + return -1; + } + int errors = 0; + for (int ii = 0; ii < hdrv_pcl_state.hwcdef_cnt; ii++) + { + if (ctr_list[ii].needs_restart) + errors |= internal_hwc_start (ctr_list[ii].fd); + ctr_list[ii].needs_restart = 0; + } + return errors; +#else + return 0; +#endif +} + +/* create counters based on hwcdef[] */ +HWCDRV_API int +hwcdrv_create_counters (unsigned hwcdef_cnt, Hwcentry *hwcdef) +{ + if (hwcdef_cnt > hdrv_pcl_about.cpcN_npics) + { + logerr (GTXT ("More than %d counters were specified\n"), hdrv_pcl_about.cpcN_npics); /*!*/ + return HWCFUNCS_ERROR_HWCARGS; + } + if (hdrv_pcl_about.cpcN_cpuver == CPUVER_UNDEFINED) + { + logerr (GTXT ("Processor not supported\n")); + return HWCFUNCS_ERROR_HWCARGS; + } + + /* add counters */ + for (unsigned idx = 0; idx < hwcdef_cnt; idx++) + { + perf_event_def_t *glb_event_def = &global_perf_event_def[idx]; + memset (glb_event_def, 0, sizeof (perf_event_def_t)); + unsigned int pmc_sel; + eventsel_t evntsel; + if (hwcfuncs_get_x86_eventsel (hwcdef[idx].reg_num, + hwcdef[idx].int_name, &evntsel, &pmc_sel)) + { + TprintfT (0, "hwcdrv: ERROR: hwcfuncs_get_x86_eventsel() failed\n"); + return HWCFUNCS_ERROR_HWCARGS; + } + glb_event_def->reg_num = pmc_sel; + glb_event_def->eventsel = evntsel; + glb_event_def->counter_preload = hwcdef[idx].val; + glb_event_def->min_time = hwcdef[idx].min_time; + glb_event_def->name = strdup (hwcdef[idx].name); // memory leak??? very minor + init_perf_event (&glb_event_def->hw, glb_event_def->eventsel, + glb_event_def->counter_preload); + TprintfT (DBG_LT1, "hwcdrv: create_counters: pic=%u name='%s' interval=%lld" + "(min_time=%lld): reg_num=0x%x eventsel=0x%llx ireset=%lld usr=%lld sys=%lld\n", + idx, hwcdef[idx].int_name, (long long) glb_event_def->counter_preload, + (long long) glb_event_def->min_time, (int) glb_event_def->reg_num, + (long long) glb_event_def->eventsel, + (long long) HW_INTERVAL_PRESET (hwcdef[idx].val), + (long long) glb_event_def->hw.exclude_user, + (long long) glb_event_def->hw.exclude_kernel); + } + + hdrv_pcl_state.hwcdef_cnt = hwcdef_cnt; + return 0; +} + +HWCDRV_API int +hwcdrv_free_counters () // note: only performs shutdown for this thread +{ + hdrv_pcl_ctx_t * pctx; + if (!COUNTERS_ENABLED ()) + return 0; + pctx = hdrv_pcl_state.find_vpc_ctx (); + if (!pctx) + { + TprintfT (0, "hwcdrv: WARNING: hwcdrv_free_counters: tsd context is NULL\n"); + return HWCFUNCS_ERROR_GENERIC; + } + counter_state_t *ctr_list = pctx->ctr_list; + if (!ctr_list) + { + // fork child: prolog suspends hwcs, then epilog frees them + TprintfT (DBG_LT1, "hwcdrv: WARNING: hwcdrv_free_counters: ctr_list is already NULL\n"); + return 0; + } + int hwc_rc = 0; + for (int ii = 0; ii < hdrv_pcl_state.hwcdef_cnt; ii++) + if (stop_one_ctr (ii, ctr_list)) + hwc_rc = HWCFUNCS_ERROR_GENERIC; + TprintfT (DBG_LT1, "hwcdrv: hwcdrv_free_counters(tid=0x%lx).\n", pctx->tid); + pctx->ctr_list = NULL; + return hwc_rc; +} + +HWCDRV_API int +hwcdrv_start (void) /* must be called from each thread ? */ +{ + hdrv_pcl_ctx_t *pctx = NULL; + if (!COUNTERS_ENABLED ()) + { + TprintfT (DBG_LT1, "hwcdrv: WARNING: hwcdrv_start: no counters to start \n"); + return 0; + } + if (!hdrv_pcl_state.library_ok) + { + TprintfT (0, "hwcdrv: ERROR: hwcdrv_start: library is not open\n"); + return HWCFUNCS_ERROR_NOT_SUPPORTED; + } + + /* + * set up per-thread context + */ + pctx = hdrv_pcl_state.find_vpc_ctx (); + if (!pctx) + { + TprintfT (0, "hwcdrv: ERROR: hwcdrv_start: tsd context is NULL\n"); + return HWCFUNCS_ERROR_UNEXPECTED; + } + pctx->tid = hwcdrv_gettid (); + TprintfT (DBG_LT1, "hwcdrv: hwcdrv_start(tid=0x%lx)\n", pctx->tid); + + /* + * create per-thread counter list + */ + counter_state_t *ctr_list = (counter_state_t *) calloc (hdrv_pcl_state.hwcdef_cnt, + sizeof (counter_state_t)); + if (!ctr_list) + { + TprintfT (0, "hwcdrv: ERROR: hwcdrv_start: calloc(ctr_list) failed\n"); + return HWCFUNCS_ERROR_MEMORY; + } + int ii; + for (ii = 0; ii < hdrv_pcl_state.hwcdef_cnt; ii++) + ctr_list[ii].fd = -1; // invalidate fds in case we have to close prematurely + pctx->ctr_list = ctr_list; + + /* + * bind the counters + */ + size_t pgsz = sysconf (_SC_PAGESIZE); + for (ii = 0; ii < hdrv_pcl_state.hwcdef_cnt; ii++) + { + ctr_list[ii].last_overflow_period = global_perf_event_def[ii].hw.sample_period; + if (start_one_ctr (ii, pgsz, pctx, "hwcdrv: ERROR: hwcdrv_start:")) goto hwcdrv_start_cleanup; + } + + /* + * start the counters + */ + for (ii = 0; ii < hdrv_pcl_state.hwcdef_cnt; ii++) + { + int rc = internal_hwc_start (ctr_list[ii].fd); + if (rc < 0) + goto hwcdrv_start_cleanup; + } + return 0; + +hwcdrv_start_cleanup: + hwcdrv_free_counters (); // PERF_EVENT_IOC_DISABLE and close() for all fds + return HWCFUNCS_ERROR_UNAVAIL; +} + +HWCDRV_API int +hwcdrv_lwp_suspend (void) /* must be called from each thread */ +{ + if (!COUNTERS_ENABLED ()) + { + TprintfT (DBG_LT1, "hwcdrv: WARNING: hwcdrv_lwp_suspend: no counters\n"); + return 0; + } + TprintfT (DBG_LT1, "hwcdrv: hwcdrv_lwp_suspend()\n"); + return hwcdrv_free_counters (); +} + +HWCDRV_API int +hwcdrv_lwp_resume (void) /* must be called from each thread */ +{ + if (!COUNTERS_ENABLED ()) + { + TprintfT (DBG_LT1, "hwcdrv: WARNING: hwcdrv_lwp_resume: no counters\n"); + return 0; + } + TprintfT (DBG_LT1, "hwcdrv: hwcdrv_lwp_resume()\n"); + return hwcdrv_start (); +} + +HWCDRV_API int +hwcdrv_read_events (hwc_event_t *overflow_data, hwc_event_samples_t *sampled_data) +{ + overflow_data->ce_hrt = 0; + for (int i = 0; i < MAX_PICS; i++) + { + overflow_data->ce_pic[i] = 0; + if (sampled_data) + HWCFUNCS_SAMPLE_RESET (&sampled_data->sample[i]); + } + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* HWCDRV_API */ + +hwcdrv_api_t hwcdrv_pcl_api = { + hwcdrv_init, + hwcdrv_get_info, + hwcdrv_enable_mt, + hwcdrv_get_descriptions, + hwcdrv_assign_regnos, + hwcdrv_create_counters, + hwcdrv_start, + hwcdrv_overflow, + hwcdrv_read_events, + hwcdrv_sighlr_restart, + hwcdrv_lwp_suspend, + hwcdrv_lwp_resume, + hwcdrv_free_counters, + hwcdrv_lwp_init, + hwcdrv_lwp_fini, + -1 // hwcdrv_init_status +}; diff --git a/gprofng/common/hwcdrv.h b/gprofng/common/hwcdrv.h new file mode 100644 index 0000000..14c55cf --- /dev/null +++ b/gprofng/common/hwcdrv.h @@ -0,0 +1,330 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* Hardware counter profiling driver's header */ + +#ifndef __HWCDRV_H +#define __HWCDRV_H + +#include "hwcfuncs.h" + +#ifdef linux +#define HWCFUNCS_SIGNAL SIGIO +#define HWCFUNCS_SIGNAL_STRING "SIGIO" +#else +#define HWCFUNCS_SIGNAL SIGEMT +#define HWCFUNCS_SIGNAL_STRING "SIGEMT" +#endif + +#ifndef LIBCOLLECTOR_SRC /* not running in libcollector */ +#include <string.h> + +#else /* running in libcollector */ +#include "collector_module.h" +#include "libcol_util.h" + +#define get_hwcdrv __collector_get_hwcdrv +#define hwcdrv_drivers __collector_hwcdrv_drivers +#define hwcdrv_cpc1_api __collector_hwcdrv_cpc1_api +#define hwcdrv_cpc2_api __collector_hwcdrv_cpc2_api +#define hwcdrv_default __collector_hwcdrv_default +#define hwcdrv_driver __collector_hwcdrv_driver +#define hwcdrv_init __collector_hwcdrv_init +#define hwcdrv_get_info __collector_hwcdrv_get_info +#define hwcdrv_enable_mt __collector_hwcdrv_enable_mt +#define hwcdrv_get_descriptions __collector_hwcdrv_get_descriptions +#define hwcdrv_assign_regnos __collector_hwcdrv_assign_regnos +#define hwcdrv_create_counters __collector_hwcdrv_create_counters +#define hwcdrv_start __collector_hwcdrv_start +#define hwcdrv_overflow __collector_hwcdrv_overflow +#define hwcdrv_read_events __collector_hwcdrv_read_events +#define hwcdrv_sighlr_restart __collector_hwcdrv_sighlr_restart +#define hwcdrv_lwp_suspend __collector_hwcdrv_lwp_suspend +#define hwcdrv_lwp_resume __collector_hwcdrv_lwp_resume +#define hwcdrv_free_counters __collector_hwcdrv_free_counters +#define hwcdrv_lwp_init __collector_hwcdrv_lwp_init +#define hwcdrv_lwp_fini __collector_hwcdrv_lwp_fini +#define hwcdrv_assign_all_regnos __collector_hwcdrv_assign_all_regnos +#define hwcdrv_lookup_cpuver __collector_hwcdrv_lookup_cpuver +#define hwcfuncs_int_capture_errmsg __collector_hwcfuncs_int_capture_errmsg + +#define GTXT(x) x + +/* Implemented by libcollector */ +#define calloc __collector_calloc +#define close CALL_UTIL(close) +#define fcntl CALL_UTIL(fcntl) +#define fprintf CALL_UTIL(fprintf) +//#define free __collector_free +#define free(...) +#define gethrtime __collector_gethrtime +#define ioctl CALL_UTIL(ioctl) +#define malloc __collector_malloc +#define memcpy __collector_memcpy +#define memset CALL_UTIL(memset) +#define mmap CALL_UTIL(mmap) +#define snprintf CALL_UTIL(snprintf) +#define strchr CALL_UTIL(strchr) +#define strcmp CALL_UTIL(strcmp) +#define strncmp CALL_UTIL(strncmp) +#define strcpy CALL_UTIL(strcpy) +#define strdup __collector_strdup +#define strncpy CALL_UTIL(strncpy) +#define strerror CALL_UTIL(strerror) +#define strlen CALL_UTIL(strlen) +#define strstr CALL_UTIL(strstr) +#define strtol CALL_UTIL(strtol) +#define strtoll CALL_UTIL(strtoll) +#define strtoul CALL_UTIL(strtoul) +#define strtoull CALL_UTIL(strtoull) +#define syscall CALL_UTIL(syscall) +#define sysconf CALL_UTIL(sysconf) +#define vsnprintf CALL_UTIL(vsnprintf) + +#endif /* --- LIBCOLLECTOR_SRC --- */ + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 +#define DBG_LT4 4 + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* hwcdrv api */ + typedef struct + { + int (*hwcdrv_init)(hwcfuncs_abort_fn_t abort_ftn, int * tsd_sz); + /* Initialize hwc counter library (do not call again after fork) + Must be called before other functions. + Input: + <abort_ftn>: NULL or callback function to be used for fatal errors + <tsd_sz>: If not NULL, returns size in bytes required for thread-specific storage + Return: 0 if successful + */ + + void (*hwcdrv_get_info)(int *cpuver, const char **cciname, uint_t *npics, + const char **docref, uint64_t *support); + /* get info about session + Input: + <cpuver>: if not NULL, returns value of CPC cpu version + <cciname>: if not NULL, returns name of CPU + <npics>: if not NULL, returns maximum # of HWCs + <docref>: if not NULL, returns documentation reference + <support>: if not NULL, returns bitmask (see hwcfuncs.h) of hwc support + Return: 0 if successful, nonzero otherwise + */ + + int (*hwcdrv_enable_mt)(hwcfuncs_tsd_get_fn_t tsd_ftn); + /* Enables multi-threaded mode (do not need to call again after fork) + Input: + <tsd_ftn>: If <tsd_sz>==0, this parameter is ignored. + Otherwise: + tsd_ftn() must be able to return a pointer to thread-specific + memory of <tsd_sz> bytes. + For a given thread, tsd_ftn() must + always return the same pointer. + Return: none + */ + + int (*hwcdrv_get_descriptions)(hwcf_hwc_cb_t *hwc_find_action, + hwcf_attr_cb_t *attr_find_action); + /* Initiate callbacks with all available HWC names and and HWC attributes. + Input: + <hwc_find_action>: if not NULL, will be called once for each HWC + <attr_find_action>: if not NULL, will be called once for each attribute + Return: 0 if successful + or a cpc return code upon error + */ + + int (*hwcdrv_assign_regnos)(Hwcentry* entries[], unsigned numctrs); + /* Assign entries[]->reg_num values as needed by platform + Input: + <entries>: array of counters + <numctrs>: number of items in <entries> + Return: 0 if successful + HWCFUNCS_ERROR_HWCINIT if resources unavailable + HWCFUNCS_ERROR_HWCARGS if counters were not specified correctly + */ + + int (*hwcdrv_create_counters)(unsigned hwcdef_cnt, Hwcentry *hwcdef); + /* Create the counters, but don't start them. + call this once in main thread to create counters. + Input: + <defcnt>: number of counter definitions. + <hwcdef>: counter definitions. + Return: 0 if successful + or a cpc return code upon error + */ + + int (*hwcdrv_start)(void); + /* Start the counters. + call this once in main thread to start counters. + Return: 0 if successful + or a cpc return code upon error + */ + + int (*hwcdrv_overflow)(siginfo_t *si, hwc_event_t *sample, + hwc_event_t *lost_samples); + /* Linux only. Capture current counter values. + This is intended to be called from SIGEMT handler; + Input: + <si>: signal handler context information + <sample>: returns non-zero values for counters that overflowed + <lost_samples>: returns non-zero values for counters that "lost" counts + Return: 0 if successful + or a cpc return code upon error. + */ + + int (*hwcdrv_read_events)(hwc_event_t *overflow_data, + hwc_event_samples_t *sampled_data); + /* Read current counter values and samples. Read of samples is destructive. + Note: hwcdrv_read_events is not supported on Linux. + <overflow_data>: returns snapshot of counter values + <sampled_data>: returns sampled data + Return: 0 if successful + HWCFUNCS_ERROR_UNAVAIL if resource unavailable(e.g. called before initted) + (other values may be possible) + */ + + int (*hwcdrv_sighlr_restart)(const hwc_event_t* startVals); + /* Restarts the counters at the given value. + This is intended to be called from SIGEMT handler; + Input: + <startVals>: Solaris: new start values. + Linux: pointer may be NULL; startVals is ignored. + Return: 0 if successful + or a cpc return code upon error. + */ + + int (*hwcdrv_lwp_suspend)(void); + /* Attempt to stop counters on this lwp only. + hwcdrv_lwp_resume() should be used to restart counters. + Return: 0 if successful + or a cpc return code upon error. + */ + + int (*hwcdrv_lwp_resume)(void); + /* Attempt to restart counters on this lwp when counters were + stopped with hwcdrv_lwp_suspend(). + Return: 0 if successful + or a cpc return code upon error. + */ + + int (*hwcdrv_free_counters)(void); + /* Stops counters on this lwp only and frees resources. + This will fail w/ unpredictable results if other lwps's are + still running. After this call returns, + hwcdrv_create_counters() may be called with new values. + Return: 0 if successful + or a cpc return code upon error. + */ + + int (*hwcdrv_lwp_init)(void); + /* per-thread counter init. + Solaris: nop. + Linux: just after thread creation call this from inside thread + to create context and start counters. + Return: 0 if successful + or a perfctr return code upon error + */ + + void (*hwcdrv_lwp_fini)(void); + /* per-thread counter cleanup. + Solaris: nop. + Linux: call in each thread upon thread destruction. + */ + + int hwcdrv_init_status; + } hwcdrv_api_t; + + extern hwcdrv_api_t *get_hwcdrv (); + extern hwcdrv_api_t *__collector_get_hwcdrv (); + extern int __collector_hwcfuncs_bind_descriptor (const char *defstring); + extern Hwcentry **__collector_hwcfuncs_get_ctrs (unsigned *defcnt); + extern hwcdrv_api_t *hwcdrv_drivers[]; // array of available drivers + + /* prototypes for internal use by hwcdrv drivers */ + typedef struct + { // see hwcdrv_get_info() for field definitions + int cpcN_cpuver; + uint_t cpcN_npics; + const char *cpcN_docref; + const char *cpcN_cciname; + } hwcdrv_about_t; + + extern int hwcdrv_assign_all_regnos (Hwcentry* entries[], unsigned numctrs); + /* assign user's counters to specific CPU registers */ + + extern int hwcdrv_lookup_cpuver (const char * cpcN_cciname); + /* returns hwc_cpus.h ID for a given string. */ + + extern void hwcfuncs_int_capture_errmsg (const char *fn, int subcode, + const char *fmt, va_list ap); +#define logerr hwcfuncs_int_logerr + + /*---------------------------------------------------------------------------*/ + /* prototypes for internal use by linux hwcdrv drivers */ +#define PERFCTR_FIXED_MAGIC 0x40000000 /* tells perfctr to use intel fixed pmcs */ +#define PERFCTR_UMASK_SHIFT 8 +#define EXTENDED_EVNUM_2_EVSEL(evnum) \ + ( (((eventsel_t)(evnum) & 0x0f00ULL) << 24) | ((eventsel_t)(evnum) & ~0x0f00ULL) ) + + typedef uint64_t eventsel_t; + extern int hwcfuncs_get_x86_eventsel (unsigned int regno, const char *int_name, + eventsel_t *return_event, uint_t *return_pmc_sel); + + typedef int (hwcdrv_get_events_fn_t) (hwcf_hwc_cb_t *hwc_cb); + typedef int (hwcdrv_get_eventnum_fn_t) (const char *eventname, uint_t pmc, + eventsel_t *eventnum, + eventsel_t *valid_umask, uint_t *pmc_sel); + extern hwcdrv_get_eventnum_fn_t *hwcdrv_get_x86_eventnum; + + typedef struct + { + const char * attrname; // user-visible name of attribute + int is_inverted; // nonzero means boolean attribute is inverted + eventsel_t mask; // which attribute bits can be set? + eventsel_t shift; // how far to shift bits for use in x86 register + } attr_info_t; + extern const attr_info_t *perfctr_attrs_table; + + /* hdrv_pcbe api: cpu-specific drivers for Linux */ + typedef struct + { + int (*hdrv_pcbe_init)(void); + uint_t (*hdrv_pcbe_ncounters)(void); + const char *(*hdrv_pcbe_impl_name)(void); + const char *(*hdrv_pcbe_cpuref)(void); + int (*hdrv_pcbe_get_events)(hwcf_hwc_cb_t *hwc_cb); + int (*hdrv_pcbe_get_eventnum)(const char * eventname, uint_t pmc, + eventsel_t *eventnum, eventsel_t *valid_umask, + uint_t *pmc_sel); + } hdrv_pcbe_api_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gprofng/common/hwcentry.h b/gprofng/common/hwcentry.h new file mode 100644 index 0000000..8611ab7 --- /dev/null +++ b/gprofng/common/hwcentry.h @@ -0,0 +1,417 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _HWCENTRY_H +#define _HWCENTRY_H + +#ifndef LIBCOLLECTOR_SRC /* not running in libcollector */ +#include <stdio.h> /* FILE */ +#endif /* --- LIBCOLLECTOR_SRC --- */ +#include <stdlib.h> /* size_t */ +#include "hwc_cpus.h" +#include "gp-time.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* ABS backtrack types */ + typedef enum + { + /* !! Lowest 2 bits are used to indicate load and store, respectively !! */ + /* Example: On SPARC, backtrack.c did this: if (ABS_memop & inst_type) ... */ + ABST_NONE = 0x0, + ABST_LOAD = 0x1, + ABST_STORE = 0x2, + ABST_LDST = 0x3, + ABST_COUNT = 0x4, + ABST_US_DTLBM = 0xF, + ABST_NOPC = 0x100, + ABST_CLKDS = 0x103, // Obsolete + ABST_EXACT = 0x203, + ABST_LDST_SPARC64 = 0x303, + ABST_EXACT_PEBS_PLUS1 = 0x403 + /* full description below... */ + } ABST_type; + +#define ABST_PLUS_BY_DEFAULT(n) ((n)==ABST_EXACT || (n)==ABST_EXACT_PEBS_PLUS1) +#define ABST_BACKTRACK_ENABLED(n) ((n)!=ABST_NONE && (n)!=ABST_NOPC) +#define ABST_MEMSPACE_ENABLED(n) ((n)!=ABST_NONE && (n)!=ABST_NOPC && (n)!=ABST_COUNT) + + /* ABS determines the type of backtracking available for a particular metric. + * Backtracking is enabled with the "+" in "-h +<countername>...". + * + * When Backtracking is not possible: + * + * ABST_NONE=0: Either the user did not specify "+", or backtracking + * is not applicable to the metric, for example: + * clk cycles, + * instruct counts (dispatch + branch + prefetch), + * i$, + * FP ops + * ABST_NOPC=0x100 Used for non-program-related external events, for example: + * system interface events, + * memory controller counters + * Of all ABST_type options, only ABST_NOPC prevents hwprofile.c + * from recording PC/stack information. + * + * When backtracking is allowed: + * + * ABST_LOAD=1: data read events, used with metrics like: + * D$, E$, P$ read misses and hits. + * [DC+EC+PC]_rd*, Re_*_miss*, + * EC_snoop_cb(?) + * ABST_STORE=2: data write events, used with metrics like: + * D$ writes and write related misses + * DC_wr/wr-miss, EC_wb, WC=writecache, Rstall_storeQ + * [EC+PC=pcache]_snoop_inv(?), WC_snoop_cb(?), + * ABST_LDST=3: data reads/writes, used with metrics like: + * E$ references, misses. + * ABST_COUNT=4: dedicated assembly instruction: '%hi(0xfc000)' + * See SW_count_n metric on sparc. + * ABST_US_DTLBM=0xF: for load-store on Sparc -- seems to be used only + * for "unskidded DTLB_miss" with DTLB_miss metric. + * Checks two adjacent instructions for Data access. + * ABST_CLKDS=0x103: data reads/writes, used with Clock-based Dataspace + * profiling. Ultrasparc T2 and earlier. + * ABST_EXACT=0x203: data reads/writes, precise trap with no skid + * ABST_LDST_SPARC64=0x303: Fujitsu SPARC64 load/store + * ABST_EXACT_PEBS_PLUS1=0x403: data reads/writes, precise sampling with 1 instr. skid + */ + + /* Hwcentry - structure for defining a counter. + * Some fields have different usage when returned from + * hwc_lookup(), hwc_post_lookup(), or hwc_scan_*(). + * Each function will describe its return values in more detail. + */ + typedef struct + { + char *name; /* user HWC specification */ + char *int_name; /* internal HWC specification */ + regno_t reg_num; /* register in CPU, aka picnum, or REGNO_ANY */ + char *metric; /* descriptive name, for well-known counters only */ + volatile int val; /* default or actual overflow value */ + int timecvt; /* multiplier to convert metric to time, 0 if N/A */ + ABST_type memop; /* type of backtracking allowed */ + char *short_desc; /* optional one-liner description, or NULL */ + int type; /* Type of perf_event_attr */ + long long config; /* perf_event_type -specific configuration */ + /* the fields above this line are expected, in order, by the tables in hwctable.c */ + /* ================================================== */ + /* the fields below this line are more flexible */ + int sort_order; /* "tag" to associate experiment record with HWC def */ + regno_t *reg_list; /* if not NULL, legal values for <reg_num> field above */ + /* Note: reg_list will be terminated by REGNO_ANY */ + /* Max size of array is MAX_PICS */ + hrtime_t min_time; /* target minimum time between overflow events. 0 is off. See HWCTIME_* macros */ + hrtime_t min_time_default; /* if min_time==HWCTIME_AUTO, use this value instead. 0 is off. */ + int ref_val; /* if min_time==HWCTIME_AUTO, use this time. 0 is off. */ + int lval, hval; /* temporary to allow DBX to build until dbx glue.cc fixed */ + } Hwcentry; + + // Hwcentry.min_time canned values +#define HWCTIME_TBD ((hrtime_t)( -1LL)) /* self-adjusting enabled but nsecs not yet selected */ +#define HWCTIME_HI ( 1 * 1000 * 1000LL ) /* 1 msec represented in nsecs */ +#define HWCTIME_ON ( 10 * 1000 * 1000LL ) /* 10 msec represented in nsecs */ +#define HWCTIME_LO ( 100 * 1000 * 1000LL ) /* 100 msec represented in nsecs */ + +#define HWC_VAL_HI(refVal) (((refVal)/10) + 1) +#define HWC_VAL_ON(refVal) (refVal) +#define HWC_VAL_LO(refVal) (((refVal)*10)/100*100 + 1) // zero's out lower digits, add 1 +#define HWC_VAL_CUSTOM(refVal, targetNanoSec) ((double)(refVal)*(targetNanoSec)/HWCTIME_ON) + +#define HWCENTRY_USES_SAMPLING(h) ((h)->memop==ABST_EXACT_PEBS_PLUS1) + + extern int hwc_lookup (int forKernel, hrtime_t min_time_default, + const char *uname, Hwcentry *list[], unsigned listsz, + char **emsg, char **wmsg); + /* Parses counter cmdline string. Returns counter definitions. + * Input: + * <forKernel> lookup using which table: 0-collect or 1-er_kernel + * <min_time_default> minimum nseconds between events if Hwcentry.min_time == HWCTIME_TBD. 0 to disable. + * <uname> command line HWC definition of format: + * <ctr_def>...[{','|(whitespace)}<ctr_n_def>] where + * <ctr_def> == [+]<ctr>[/<reg#>][,<interval>] + * <list> array of pointers to store counter definitions + * <listsz> number of elements in <list> + * Returns: + * Success: + * Returns number of valid counters in <list> and <list>'s elements + * will be initialized as follows: + * + * <list[]->name>: + * Copy of the <uname> with the following modification: + * if backtracking is not supported, the + will be removed. + * <list[]->int_name>: + * For well-known and convenience ctrs, the internal HWC specification, + * e.g. BSQ_cache_reference~emask=0x0100. + * For raw ctrs, this will be a copy of <name>. + * <list[]->reg_num>: + * Register number if specified by user or table, REGNO_ANY otherwise. + * <list[]->metric>: + * For well-known counters, descriptive name, e.g. "D$ Read Misses". + * NULL otherwise. + * <list[]->val>: + * Overflow value selected by user, default value otherwise. + * <list[]->timecvt>: + * Value from tables. + * <list[]->memop>: + * If + is selected and backtracking is allowed, value from table. + * ABST_NONE or ABST_NOPC otherwise. + * + * It is the responsibility of the caller to free 'name' and 'int_name'. + * 'metric' is a static string and shouldn't be freed. + * 'emsg' will point to NULL + * + * Failure: + * Frees all allocated elements. + * emsg will point to a string with an error message to print + * returns -1 + */ + + extern char *hwc_validate_ctrs (int forKernel, Hwcentry *list[], unsigned listsz); + /* Validates that the vector of specified HW counters can be loaded (more-or-less) + * Some invalid combinations, especially on Linux will not be detected + */ + + extern int hwc_get_cpc_cpuver (); + /* Return the cpc_cpuver for this system. Other possible values: + * CPUVER_GENERIC=0, CPU could not be determined, but HWCs are ok. + * CPUVER_UNDEFINED=-1, HWCs are not available. + */ + + extern char *hwc_get_docref (char *buf, size_t buflen); + /* Return a CPU HWC document reference, or NULL. */ + + // TBR + extern char *hwc_get_default_cntrs (); + /* Return a default HW counter string; may be NULL, or zero-length */ + /* NULL means none is defined in the table; or zero-length means string defined could not be loaded */ + + extern char *hwc_get_default_cntrs2 (int forKernel, int style); + /* like hwc_get_default_cntrs() for style==1 */ + /* but allows other styles of formatting as well */ + /* deprecate and eventually remove hwc_get_default_cntrs() */ + + extern char *hwc_get_orig_default_cntrs (); + /* Get the default HW counter string as set in the table */ + /* NULL means none is defined in the table */ + + extern void hwc_update_val (Hwcentry *ctr); + /* Check time-based intervals and update Hwcentry.val as needed */ + + extern char *hwc_get_cpuname (char *buf, size_t buflen); + /* Return the cpc cpu name for this system, or NULL. */ + + extern unsigned hwc_get_max_regs (); + /* Return number of counters registers for this system. */ + + extern unsigned hwc_get_max_concurrent (int forKernel); + /* Return the max number of simultaneous counters for this system. */ + + extern char **hwc_get_attrs (int forKernel); + /* Return: + * Array of attributes (strings) supported by this system. + * Last element in array is null. + * Array and its elements should NOT be freed by the caller. + */ + + extern unsigned hwc_scan_attrs (void (*action)(const char *attr, + const char *desc)); + /* Scan the HW counter attributes, and call function for each attribute. + * Input: + * <action>: + * If NULL, no action is performed, but count is still returned. + * Otherwise called for each type of attributes, or if none exist, + * called once with NULL parameter. + * Return: count of times <action> would have been called w/ non-NULL data. + */ + + extern Hwcentry *hwc_post_lookup (Hwcentry * pret_ctr, char *uname, + char * int_name, int cpc_cpuver); + /* When post-processing a run, look up a Hwcentry for given type of system. + * Input: + * <pret_ctr>: storage for counter definition + * <uname>: well-known name, convenience name, or complete HWC defintion. + * <int_name>: Hwcentry->int_name or NULL for don't care + * <cpc_cpuver>: version of cpu used for experiment. + * Return: + * <pret_ctr>'s elements set as follows: + * + * <pret_ctr->name>: + * Copy of <uname> with the following modifications: + * 1) + and /<regnum> will be stripped off + * 2) attributes will be sorted and values will shown in hex. + * <pret_ctr->int_name>: + * For well-known/convenience counters, the internal HWC specification + * from the table, e.g. BSQ_cache_reference~emask=0x0100. + * Otherwise, a copy of <uname>. + * <pret_ctr->reg_num>: + * Register number if specified by user or table, + * REGNO_ANY othewise. + * <pret_ctr->metric>: + * For well-known counters, descriptive name, e.g. "D$ Read Misses". + * NULL otherwise. + * <pret_ctr->timecvt>: + * For well-known/convenience/hidden counters, value from table. + * 0 otherwise. + * <pret_ctr->memop>: + * For well-known/convenience/hidden counters, value from table. + * ABST_NONE otherwise. + * <pret_ctr->sort_order>: + * Set to 0. + * + * It is the responsibility of the caller to free 'name' and 'int_name'. + * 'metric' is a static string and shouldn't be freed. + */ + + extern Hwcentry **hwc_get_std_ctrs (int forKernel); + /* Return: + * Array of well-known counters supported by this system. + * Last element in array will be NULL. + * Array and its elements should NOT be freed by the caller. + */ + + extern unsigned hwc_scan_std_ctrs (void (*action)(const Hwcentry *)); + /* Call <action> for each well-known counter. + * Input: + * <action>: + * If NULL, no action is performed, but count is still returned. + * Otherwise called for each type of attributes, or if none exist, + * called once with NULL parameter. + * Return: + * Count of times <action> would have been called w/ non-NULL data. + * If <action> is not NULL, Hwcentry fields will be set as follows: + * <ctr->name>: + * HWC alias name, e.g. dcrm. + * <ctr->int_name>: + * The internal HWC specification, e.g. BSQ_cache_reference~emask=0x0100. + * <ctr->reg_num>: + * Register number if specified by the table, REGNO_ANY otherwise. + * <ctr->metric>: + * Descriptive name, e.g. "D$ Read Misses". + * <ctr->lval>: + * Low-resolution overflow value. + * <ctr->val>: + * Default overflow value. + * <ctr->hval>: + * High-resolution overflow value. + * <ctr->timecvt>: + * multiplier to convert metric to time, 0 otherwise. + * <ctr->memop>: + * ABST_* type for this counter. + * <ctr->reg_list>: + * Array of legal <reg_num> values. Terminated by REGNO_ANY. + * + * Note: All fields point to static data, none should be freed. + */ + + extern Hwcentry **hwc_get_raw_ctrs (int forKernel); + /* Return: + * Table of raw (not well-known) counters supported by this system. + * Last element in array will be NULL. + * Table and its elements should NOT be freed by the caller. + */ + + extern unsigned hwc_scan_raw_ctrs (void (*action)(const Hwcentry *)); + /* Call <action> for each raw counter. + * Input: + * <action>: + * If NULL, no action is performed, but count is still returned. + * Otherwise called for each type of attributes, or if none exist, + * called once with NULL parameter. + * Return: + * Count of times <action> would have been called w/ non-NULL data. + * If <action> is not NULL, Hwcentry fields will be set as follows: + * <ctr->name>: + * HWC raw name without attributes, e.g. BSQ_cache_reference. + * <ctr->int_name>: + * NULL. + * <ctr->metric>: + * NULL. + * The remainder of the fields are the same as for + * hwc_scan_std_ctrs(). + * + * Note: All fields point to static data, none should be freed. + */ + + extern void + hwc_usage (int forKernel, const char *cmd, const char *dataspace_msg); + /* Print an i18n'd description of "-h" usage, used by collect and er_kernel. + */ + + extern void hwc_usage_f (int forKernel, FILE *f, const char *cmd, + const char *dataspace_msg, int show_syntax, + int show_short_desc); + /* Print an i18n'd description of "-h" usage to a FILE. Used by GUI. */ + + extern char *hwc_rate_string (const Hwcentry *pctr, int force_numeric_format); + /* Returns {"on"|"hi"|"lo"|""|<value>}. Return value must be freed by caller. */ + + extern char *hwc_i18n_metric (const Hwcentry *ctr); + /* Get a basic lable for a counter, properly i18n'd. + * Note: NOT MT SAFE. + * Examples: + * CPU Cycles + * DC_rd Events + * Pseudocode: + * if(ctr->metric != NULL) { + * sprintf(metricbuf, PTXT(ctr->metric) ); + * } else if (ctr->name != NULL) { + * sprintf(metricbuf, GTXT("%s Events"), ctr->name ); + * } else if (ctr->int_name != NULL) { + * sprintf(metricbuf, GTXT("%s Events"), ctr->int_name ); + * } + * Return: pointer to a buffer containing the above description. + */ + + extern char *hwc_hwcentry_string (char *buf, size_t buflen, const Hwcentry *ctr); + /* Get a i18n'd description of a HW counter's options. + * Examples of well-known counters: + * cycles[/{0|1}],9999991 ('CPU Cycles', alias for Cycle_cnt; CPU-cycles) + * dcr[/0],1000003 ('D$ Read Refs', alias for DC_rd; load events) + * Examples of raw counters: + * Cycle_cnt[/{0|1}],1000003 (CPU-cycles) + * DC_rd[/0],1000003 (load events) + * Return: <buf>, filled in. + */ + + extern char *hwc_hwcentry_specd_string (char *buf, size_t buflen, const Hwcentry *ctr); + /* Get a i18n'd description of a HW counter's specific configuration. + * Examples of well-known counters: + * cycles,9999991 ('CPU Cycles') + * +dcr/0,1000003 ('D$ Read Refs') + * Examples of raw counters: + * Cycle_cnt,1000003 + * +DC_rd/0,1000003 + * Return: <buf>, filled in. + */ + + extern const char *hwc_memop_string (ABST_type memop); + /* Get a i18n'd description of a variable of type ABST_type. + * Return: pointer to static string. + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gprofng/common/hwcfuncs.c b/gprofng/common/hwcfuncs.c new file mode 100644 index 0000000..2f9764d --- /dev/null +++ b/gprofng/common/hwcfuncs.c @@ -0,0 +1,704 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* Hardware counter profiling */ +#include "hwcdrv.h" +#include "hwcfuncs.h" + +/*---------------------------------------------------------------------------*/ +/* macros */ + +#define IS_GLOBAL /* Mark global symbols */ +#define HWCDRV_API static /* Mark functions used by hwcdrv API */ + +/*---------------------------------------------------------------------------*/ +/* static variables */ +static uint_t cpcN_npics; +static char hwcfuncs_errmsg_buf[1024]; +static int hwcfuncs_errmsg_enabled = 1; +static int hwcfuncs_errmsg_valid; + +/* --- user counter selections and options */ +static unsigned hwcdef_cnt; /* number of *active* hardware counters */ +static Hwcentry hwcdef[MAX_PICS]; /* HWC definitions */ +static Hwcentry *hwctable[MAX_PICS]; /* HWC definitions */ + +/* --- drivers --- */ + +// default driver + +HWCDRV_API int +hwcdrv_init (hwcfuncs_abort_fn_t abort_ftn, int* tsd_sz) +{ + return -1; +} + +HWCDRV_API void +hwcdrv_get_info ( + int * cpuver, const char ** cciname, + uint_t * npics, const char ** docref, uint64_t* support) { } + +HWCDRV_API int +hwcdrv_enable_mt (hwcfuncs_tsd_get_fn_t tsd_ftn) +{ + return -1; +} + +HWCDRV_API int +hwcdrv_get_descriptions (hwcf_hwc_cb_t *hwc_find_action, + hwcf_attr_cb_t *attr_find_action) +{ + return 0; +} + +HWCDRV_API int +hwcdrv_assign_regnos (Hwcentry *entries[], unsigned numctrs) +{ + return -1; +} + +HWCDRV_API int +hwcdrv_create_counters (unsigned hwcdef_cnt, Hwcentry *hwcdef) +{ + return -1; +} + +HWCDRV_API int +hwcdrv_read_events (hwc_event_t *events, hwc_event_samples_t*samples) +{ + return -1; +} + +HWCDRV_API int +hwcdrv_start (void) +{ + return -1; +} + +HWCDRV_API int +hwcdrv_overflow (siginfo_t *si, hwc_event_t *s, hwc_event_t *t) +{ + return 0; +} + +HWCDRV_API int +hwcdrv_sighlr_restart (const hwc_event_t *sample) +{ + return -1; +} + +HWCDRV_API int +hwcdrv_lwp_suspend (void) +{ + return -1; +} + +HWCDRV_API int +hwcdrv_lwp_resume (void) +{ + return -1; +} + +HWCDRV_API int +hwcdrv_free_counters (void) +{ + return 0; +} + +HWCDRV_API int +hwcdrv_lwp_init (void) +{ + return 0; +} + +HWCDRV_API void +hwcdrv_lwp_fini (void) { } + +static hwcdrv_api_t hwcdrv_default = { + hwcdrv_init, + hwcdrv_get_info, + hwcdrv_enable_mt, + hwcdrv_get_descriptions, + hwcdrv_assign_regnos, + hwcdrv_create_counters, + hwcdrv_start, + hwcdrv_overflow, + hwcdrv_read_events, + hwcdrv_sighlr_restart, + hwcdrv_lwp_suspend, + hwcdrv_lwp_resume, + hwcdrv_free_counters, + hwcdrv_lwp_init, + hwcdrv_lwp_fini, + -1 // hwcdrv_init_status +}; + +static hwcdrv_api_t *hwcdrv_driver = &hwcdrv_default; + + +/*---------------------------------------------------------------------------*/ +/* misc */ + +/* print a counter definition (for debugging) */ +static void +ctrdefprint (int dbg_lvl, const char * hdr, Hwcentry*phwcdef) +{ + TprintfT (dbg_lvl, "%s: name='%s', int_name='%s'," + " reg_num=%d, timecvt=%d, memop=%d, " + "interval=%d, tag=%u, reg_list=%p\n", + hdr, phwcdef->name, phwcdef->int_name, phwcdef->reg_num, + phwcdef->timecvt, phwcdef->memop, phwcdef->val, + phwcdef->sort_order, phwcdef->reg_list); +} + +/*---------------------------------------------------------------------------*/ +/* errmsg buffering */ + +/* errmsg buffering is needed only because the most descriptive error + messages from CPC are delivered using a callback mechanism. + hwcfuncs_errmsg_get() should only be used during initialization, and + ideally, only to provide feedback to an end user when his counters can't + be bound to HW. + */ +IS_GLOBAL char * +hwcfuncs_errmsg_get (char *buf, size_t bufsize, int enable) +{ + hwcfuncs_errmsg_enabled = 0; + if (buf && bufsize) + { + if (hwcfuncs_errmsg_valid) + { + strncpy (buf, hwcfuncs_errmsg_buf, bufsize); + buf[bufsize - 1] = 0; + } + else + *buf = 0; + } + hwcfuncs_errmsg_buf[0] = 0; + hwcfuncs_errmsg_valid = 0; + hwcfuncs_errmsg_enabled = enable; + return buf; +} + +/* used by cpc to log an error */ +IS_GLOBAL void +hwcfuncs_int_capture_errmsg (const char *fn, int subcode, + const char *fmt, va_list ap) +{ + if (hwcfuncs_errmsg_enabled && + !hwcfuncs_errmsg_valid) + { + vsnprintf (hwcfuncs_errmsg_buf, sizeof (hwcfuncs_errmsg_buf), fmt, ap); + TprintfT (DBG_LT0, "hwcfuncs: cpcN_capture_errmsg(): %s\n", + hwcfuncs_errmsg_buf); + hwcfuncs_errmsg_valid = 1; + } + return; +} + +/* Log an internal error to the CPC error buffer. + * Note: only call this during init functions. + * Note: when most cpc calls fail, they will call cpcN_capture_errmsg() + * directly, so only call logerr() when a non-cpc function fails. + */ +IS_GLOBAL void +hwcfuncs_int_logerr (const char *format, ...) +{ + va_list va; + va_start (va, format); + hwcfuncs_int_capture_errmsg ("logerr", 0, format, va); + va_end (va); +} + +/* utils to parse counter strings */ +static void +clear_hwcdefs () +{ + for (unsigned idx = 0; idx < MAX_PICS; idx++) + { + static Hwcentry empty; + hwcdef[idx] = empty; // leaks strings and reg_list array + hwcdef[idx].reg_num = REGNO_ANY; + hwcdef[idx].val = -1; + hwcdef[idx].sort_order = -1; + } +} + +/* initialize hwcdef[] based on user's counter definitions */ +static int +process_data_descriptor (const char *defstring) +{ + /* + * <defstring> format should be of format + * :%s:%s:0x%x:%d:%lld:%d:%d:0x%x[,%s...repeat for each ctr] + * where the counter fields are: + * :<userName>:<internalCtr>:<register>:<timeoutVal>[:m<min_time>]:<tag>:<timecvt>:<memop> + * See Coll_Ctrl::build_data_desc(). + */ + int err = 0; + char *ds = NULL; + char *dsp = NULL; + unsigned idx; + + clear_hwcdefs (); + if (!defstring || !strlen (defstring)) + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + ds = strdup (defstring); + if (!ds) + { + err = HWCFUNCS_ERROR_HWCINIT; + goto ext_hw_install_end; + } + dsp = ds; + + for (idx = 0; idx < MAX_PICS && *dsp; idx++) + { + char *name = NULL; + char *int_name = NULL; + regno_t reg = REGNO_ANY; + ABST_type memop = ABST_NONE; + int interval = 0; + int timecvt = 0; + unsigned sort_order = (unsigned) - 1; + + /* name */ + name = dsp; + dsp = strchr (dsp, ':'); + if (dsp == NULL) + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + *dsp++ = (char) 0; + + /* int_name */ + int_name = dsp; + dsp = strchr (dsp, ':'); + if (dsp == NULL) + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + *dsp++ = (char) 0; + + /* reg_num */ + reg = (int) strtol (dsp, &dsp, 0); + if (*dsp++ != ':') + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + if (reg < 0 && reg != -1) + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + if (reg >= 0) + hwcdef[idx].reg_num = reg; + + /* val */ + interval = (int) strtol (dsp, &dsp, 0); + if (*dsp++ != ':') + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + if (interval < 0) + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + hwcdef[idx].val = interval; + + /* min_time */ + /* + * This is a new field. + * An old launcher (dbx, etc.) would not include it. + * Detect the presence of the field by the char 'm'. + */ + if (*dsp == 'm') + { + long long tmp_ll = 0; + dsp++; + tmp_ll = strtoll (dsp, &dsp, 0); + if (*dsp++ != ':') + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + if (tmp_ll < 0) + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + hwcdef[idx].min_time = tmp_ll; + } + else + hwcdef[idx].min_time = 0; + + /* sort_order */ + sort_order = (int) strtoul (dsp, &dsp, 0); + if (*dsp++ != ':') + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + hwcdef[idx].sort_order = sort_order; + + /* timecvt */ + timecvt = (int) strtol (dsp, &dsp, 0); + if (*dsp++ != ':') + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + hwcdef[idx].timecvt = timecvt; + + /* memop */ + memop = (ABST_type) strtol (dsp, &dsp, 0); + if (*dsp != 0 && *dsp++ != ',') + { + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + hwcdef[idx].memop = memop; + if (*name) + hwcdef[idx].name = strdup (name); + else + hwcdef[idx].name = strdup (int_name); + if (*int_name) + hwcdef[idx].int_name = strdup (int_name); + else + hwcdef[idx].int_name = strdup (name); + ctrdefprint (DBG_LT1, "hwcfuncs: process_data_descriptor", &hwcdef[idx]); + } + + if (*dsp) + { + TprintfT (DBG_LT0, "hwcfuncs: ERROR: process_data_descriptor(): " + "ctr string had some trailing garbage:" + " '%s'\n", dsp); + err = HWCFUNCS_ERROR_HWCARGS; + goto ext_hw_install_end; + } + free (ds); + hwcdef_cnt = idx; + return 0; + +ext_hw_install_end: + if (dsp && *dsp) + { + TprintfT (DBG_LT0, "hwcfuncs: ERROR: process_data_descriptor(): " + " syntax error just before:" + " '%s;\n", dsp); + logerr (GTXT ("Data descriptor syntax error near `%s'\n"), dsp); + } + else + logerr (GTXT ("Data descriptor syntax error\n")); + free (ds); + return err; +} + +/* initialize hwcdef[] based on user's counter definitions */ +static int +process_hwcentrylist (const Hwcentry* entries[], unsigned numctrs) +{ + int err = 0; + clear_hwcdefs (); + if (numctrs > cpcN_npics) + { + logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics); /*!*/ + return HWCFUNCS_ERROR_HWCARGS; + } + for (unsigned idx = 0; idx < numctrs; idx++) + { + Hwcentry *phwcdef = &hwcdef[idx]; + *phwcdef = *entries[idx]; + if (phwcdef->name) + phwcdef->name = strdup (phwcdef->name); + else + phwcdef->name = "NULL"; + if (phwcdef->int_name) + phwcdef->int_name = strdup (phwcdef->int_name); + else + phwcdef->int_name = "NULL"; + if (phwcdef->val < 0) + { + logerr (GTXT ("Negative interval specified for HW counter `%s'\n"), /*!*/ + phwcdef->name); + err = HWCFUNCS_ERROR_HWCARGS; + break; + } + ctrdefprint (DBG_LT1, "hwcfuncs: process_hwcentrylist", phwcdef); + } + if (!err) + hwcdef_cnt = numctrs; + return err; +} + +/* see hwcfuncs.h */ +IS_GLOBAL void * +hwcfuncs_parse_attrs (const char *countername, hwcfuncs_attr_t attrs[], + unsigned max_attrs, uint_t *pnum_attrs, char**errstring) +{ + char *head = NULL; + char *tail = NULL; + uint_t nattrs = 0; + char *counter_copy; + int success = 0; + char errbuf[512]; + errbuf[0] = 0; + counter_copy = strdup (countername); + + /* advance pointer to first attribute */ + tail = strchr (counter_copy, HWCFUNCS_PARSE_ATTR); + if (tail) + *tail = 0; + + /* remove regno and value, if supplied */ + { + char *tmp = strchr (counter_copy, HWCFUNCS_PARSE_REGNUM); + if (tmp) + *tmp = 0; + tmp = strchr (counter_copy, HWCFUNCS_PARSE_VALUE); + if (tmp) + *tmp = 0; + } + + while (tail) + { + char *pch; + if (nattrs >= max_attrs) + { + snprintf (errbuf, sizeof (errbuf), + GTXT ("Too many attributes defined in `%s'"), + countername); + goto mycpc2_parse_attrs_end; + } + /* get attribute name */ + head = tail + 1; + tail = strchr (head, HWCFUNCS_PARSE_EQUAL); + if (!tail) + { + snprintf (errbuf, sizeof (errbuf), + GTXT ("Missing value for attribute `%s' in `%s'"), + head, countername); + goto mycpc2_parse_attrs_end; + } + *tail = 0; /* null terminate current component */ + attrs[nattrs].ca_name = head; + + /* get attribute value */ + head = tail + 1; + tail = strchr (head, HWCFUNCS_PARSE_ATTR); + if (tail) + *tail = 0; /* null terminate current component */ + attrs[nattrs].ca_val = strtoull (head, &pch, 0); + if (pch == head) + { + snprintf (errbuf, sizeof (errbuf), + GTXT ("Illegal value for attribute `%s' in `%s'"), + attrs[nattrs].ca_name, countername); + goto mycpc2_parse_attrs_end; + } + TprintfT (DBG_LT0, "hwcfuncs: pic_: '%s', attribute[%u]" + " '%s' = 0x%llx\n", + counter_copy, nattrs, attrs[nattrs].ca_name, + (long long unsigned int) attrs[nattrs].ca_val); + + nattrs++; + } + success = 1; + +mycpc2_parse_attrs_end: + *pnum_attrs = nattrs; + if (success) + { + if (errstring) + *errstring = NULL; + } + else + { + if (errstring) + *errstring = strdup (errbuf); + free (counter_copy); + counter_copy = NULL; + } + return counter_copy; +} + +IS_GLOBAL void +hwcfuncs_parse_ctr (const char *counter_def, int *pplus, char **pnameOnly, + char **pattrs, char **pregstr, regno_t *pregno) +{ + char *nameptr, *copy, *slash, *attr_delim; + int plus; + regno_t regno; + nameptr = copy = strdup (counter_def); + + /* plus */ + plus = 0; + if (nameptr[0] == HWCFUNCS_PARSE_BACKTRACK) + { + plus = 1; + nameptr++; + } + else if (nameptr[0] == HWCFUNCS_PARSE_BACKTRACK_OFF) + { + plus = -1; + nameptr++; + } + if (pplus) + *pplus = plus; + + /* regno */ + regno = REGNO_ANY; + if (pregstr) + *pregstr = NULL; + slash = strchr (nameptr, HWCFUNCS_PARSE_REGNUM); + if (slash != NULL) + { + /* the remaining string should be a number > 0 */ + if (pregstr) + *pregstr = strdup (slash); + char *endchar = NULL; + regno = (regno_t) strtol (slash + 1, &endchar, 0); + if (*endchar != 0) + regno = -2; + if (*(slash + 1) == '-') + regno = -2; + /* terminate previous element up to slash */ + *slash = 0; + } + if (pregno) + *pregno = regno; + + /* attrs */ + if (pattrs) + *pattrs = NULL; + attr_delim = strchr (nameptr, HWCFUNCS_PARSE_ATTR); + if (attr_delim != NULL) + { + if (pattrs) + *pattrs = strdup (attr_delim); + /* terminate previous element up to attr_delim */ + *attr_delim++ = 0; + } + if (pnameOnly) + *pnameOnly = strdup (nameptr); + free (copy); +} + +/* create counters */ +IS_GLOBAL int +hwcfuncs_bind_descriptor (const char *defstring) +{ + int err = process_data_descriptor (defstring); + if (err) + { + TprintfT (DBG_LT0, "hwcfuncs: ERROR: hwcfuncs_bind_descriptor failed\n"); + return err; + } + err = hwcdrv_driver->hwcdrv_create_counters (hwcdef_cnt, hwcdef); + return err; +} + +/* see hwcfuncs.h */ +IS_GLOBAL int +hwcfuncs_bind_hwcentry (const Hwcentry* entries[], unsigned numctrs) +{ + int err = -1; + err = process_hwcentrylist (entries, numctrs); + if (err) + { + TprintfT (DBG_LT0, "hwcfuncs: ERROR: hwcfuncs_bind_hwcentry\n"); + return err; + } + err = hwcdrv_driver->hwcdrv_create_counters (hwcdef_cnt, hwcdef); + return err; +} + +/* see hwcfuncs.h */ +IS_GLOBAL Hwcentry ** +hwcfuncs_get_ctrs (unsigned *defcnt) +{ + if (defcnt) + *defcnt = hwcdef_cnt; + return hwctable; +} + +/* return 1 if <regno> is in Hwcentry's list */ +IS_GLOBAL int +regno_is_valid (const Hwcentry * pctr, regno_t regno) +{ + regno_t *reg_list = pctr->reg_list; + if (REG_LIST_IS_EMPTY (reg_list)) + return 0; + if (regno == REGNO_ANY) /* wildcard */ + return 1; + for (int ii = 0; ii < MAX_PICS; ii++) + { + regno_t tmp = reg_list[ii]; + if (REG_LIST_EOL (tmp)) /* end of list */ + break; + if (tmp == regno) /* is in list */ + return 1; + } + return 0; +} + +/* supplied by hwcdrv_api drivers */ +IS_GLOBAL int +hwcfuncs_assign_regnos (Hwcentry* entries[], + unsigned numctrs) +{ + if (numctrs > cpcN_npics) + { + logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics); /*!*/ + return HWCFUNCS_ERROR_HWCARGS; + } + return hwcdrv_driver->hwcdrv_assign_regnos (entries, numctrs); +} + +extern hwcdrv_api_t hwcdrv_pcl_api; +static int hwcdrv_driver_inited = 0; + +hwcdrv_api_t * +get_hwcdrv () +{ + if (hwcdrv_driver_inited) + return hwcdrv_driver; + hwcdrv_driver_inited = 1; + cpcN_npics = 0; + for (int i = 0; i < MAX_PICS; i++) + hwctable[i] = &hwcdef[i]; + hwcdrv_driver = &hwcdrv_pcl_api; + hwcdrv_driver->hwcdrv_init_status = hwcdrv_driver->hwcdrv_init (NULL, NULL); + if (hwcdrv_driver->hwcdrv_init_status == 0) + { + hwcdrv_driver->hwcdrv_get_info (NULL, NULL, &cpcN_npics, NULL, NULL); + return hwcdrv_driver; + } + hwcdrv_driver = &hwcdrv_default; + return hwcdrv_driver; +} diff --git a/gprofng/common/hwcfuncs.h b/gprofng/common/hwcfuncs.h new file mode 100644 index 0000000..ef0360b --- /dev/null +++ b/gprofng/common/hwcfuncs.h @@ -0,0 +1,269 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* Hardware counter profiling */ + +#ifndef __HWCFUNCS_H +#define __HWCFUNCS_H + +#ifdef LIBCOLLECTOR_SRC /* running in libcollector */ +#define hwcfuncs_int_logerr __collector_hwcfuncs_int_logerr +#define hwcfuncs_parse_ctr __collector_hwcfuncs_parse_ctr +#define hwcfuncs_parse_attrs __collector_hwcfuncs_parse_attrs +#define hwcfuncs_bind_descriptor __collector_hwcfuncs_bind_descriptor +#define hwcfuncs_bind_hwcentry __collector_hwcfuncs_bind_hwcentry +#define hwcfuncs_assign_regnos __collector_hwcfuncs_assign_regnos +#define regno_is_valid __collector_regno_is_valid +#define hwcfuncs_get_ctrs __collector_hwcfuncs_get_ctrs +#define hwcfuncs_errmsg_get __collector_hwcfuncs_errmsg_get +#endif /* --- LIBCOLLECTOR_SRC --- */ + +#include <signal.h> /* siginfo_t */ +#include <limits.h> /* UINT64_t */ +#include <sys/types.h> +#include <stdint.h> + +#include "hwcentry.h" /* for Hwcentry type */ +#include "gp-time.h" + +typedef unsigned int uint_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/*---------------------------------------------------------------------------*/ +/* compile options */ + +#define HWC_DEBUG 0 /* 0/1 to enable extra HWC debug */ + +/*---------------------------------------------------------------------------*/ +/* typedefs */ +/* generic hw event */ + typedef struct _hwc_event_t + { /* generalized counter event */ + hrtime_t ce_hrt; /* gethrtime() */ + uint64_t ce_pic[MAX_PICS]; /* counter samples or start values */ + } hwc_event_t; + + /* supplementary data that accompanies some hw events */ + typedef struct + { /* supplementary data fields */ + uint64_t smpl_pc; /* pc related to event */ + uint64_t smpl_data_source; /* chip-specific data source encoding */ + uint64_t smpl_latency; /* latency related to event */ + uint64_t smpl_mem_addr; /* memory address related to event */ + } hwc_sample_t; +#define HWCFUNCS_INVALID_U64 0xFEEDBEEFDEADBEEFllu /* identifies fields as unused */ + +typedef struct { /* supplementary data fields */ + hwc_sample_t sample[MAX_PICS]; /* counter samples or start values */ +} hwc_event_samples_t; + +#define HWCFUNCS_SAMPLE_RESET(sample) \ + do { \ + (sample)->smpl_pc =HWCFUNCS_INVALID_U64; \ + (sample)->smpl_data_source =HWCFUNCS_INVALID_U64; \ + (sample)->smpl_latency =HWCFUNCS_INVALID_U64; \ + (sample)->smpl_mem_addr =HWCFUNCS_INVALID_U64; \ + } while(0) + +#define HWCFUNCS_SAMPLE_IS_RESET(sample) \ + ( \ + (sample)->smpl_pc ==HWCFUNCS_INVALID_U64 && \ + (sample)->smpl_data_source==HWCFUNCS_INVALID_U64 && \ + (sample)->smpl_latency ==HWCFUNCS_INVALID_U64 && \ + (sample)->smpl_mem_addr ==HWCFUNCS_INVALID_U64 \ + ) + +/*---------------------------------------------------------------------------*/ +/* macros */ + +#define HW_INTERVAL_MAX UINT64_MAX +#define HW_INTERVAL_PRESET(x) (HW_INTERVAL_MAX - ((uint64_t)(x) - 1)) +#define HW_INTERVAL_TYPE(x) ((uint64_t) (x) + +/* parsing */ +#define HWCFUNCS_MAX_ATTRS 20 +#define HWCFUNCS_PARSE_ATTR '~' +#define HWCFUNCS_PARSE_EQUAL '=' +#define HWCFUNCS_PARSE_BACKTRACK '+' +#define HWCFUNCS_PARSE_BACKTRACK_OFF '-' +#define HWCFUNCS_PARSE_REGNUM '/' +#define HWCFUNCS_PARSE_VALUE ',' + +/* error codes */ +#define HWCFUNCS_ERROR_GENERIC (-1) +#define HWCFUNCS_ERROR_NOT_SUPPORTED (-2) +#define HWCFUNCS_ERROR_ALREADY_CALLED (-3) +#define HWCFUNCS_ERROR_HWCINIT (-4) +#define HWCFUNCS_ERROR_HWCARGS (-5) +#define HWCFUNCS_ERROR_MEMORY (-6) +#define HWCFUNCS_ERROR_UNAVAIL (-7) +#define HWCFUNCS_ERROR_ERRNO_ZERO (-8) +#define HWCFUNCS_ERROR_UNEXPECTED (-99) + +/*---------------------------------------------------------------------------*/ +/* prototypes */ + +typedef void (*hwcfuncs_abort_fn_t) (int errnum, const char *msg); + +extern void hwcfuncs_int_logerr(const char *format,...); +/* Log an error to the internal error buffer. See hwcfuncs_errmsg_get(). + Note: Not MT-safe; don't even enable logging in an MT environment. + Recommend using this call only during init. + Note: when a libcpc call fails, it may automatically call + cpcN_capture_errmsg() to log the error message in the same internal buffer. + Recommend using this call only for non-cpc failures. + */ + +#define HWCFUNCS_SUPPORT_OVERFLOW_PROFILING 0x01llu +#define HWCFUNCS_SUPPORT_PEBS_SAMPLING 0x02llu +#define HWCFUNCS_SUPPORT_OVERFLOW_CTR_ID 0x04llu // OS identifies which counter overflowed + /* get info about session + Input: + <cpuver>: if not NULL, returns value of CPC cpu version + <cciname>: if not NULL, returns name of CPU + <npics>: if not NULL, returns maximum # of HWCs + <docref>: if not NULL, returns documentation reference + <support>: if not NULL, returns bitmask (see above) of hwc support + Return: none + */ + + typedef void* (*hwcfuncs_tsd_get_fn_t) (void); + typedef void (hwcf_hwc_cb_t) (uint_t cpcregno, const char *name); + typedef void (hwcf_attr_cb_t) (const char *attr); + + extern void + hwcfuncs_parse_ctr (const char *counter_def, int *pplus, char **pnameOnly, + char **pattrs, char **pregstr, regno_t *pregno); +/* Parse a counter definition string (value must already be stripped off). + Input: + <counter_def>: input whose format is + [+|-]<countername>[~attrs...][/<regno>] + pointers to return values: Any can be NULL. + Return: + <plus>: 1 if [+] is found, -1 if [-] is found, 0 otherwise + <pnameonly>: strdup(<countername>) + <pattrs>: strdup([~attrs...]) if specified, NULL otherwise. + <pregstr>: strdup(/<regno>) if specified, NULL otherwise. + <pregno>: <regno> if readable, REGNO_ANY if not specd, or -2 otherwise. + */ + + typedef struct + { + char *ca_name; + uint64_t ca_val; + } hwcfuncs_attr_t; /* matches cpc_attr_t */ + + void * hwcfuncs_parse_attrs (const char *countername, + hwcfuncs_attr_t attrs[], unsigned max_attrs, + uint_t *pnum_attrs, char **errstring); + /* Extract the attribute fields from <countername>. + Input: + <countername>: string whose format is + [+]<ctrname>[~attributes...][/<regno>][,...] + <attrs>: array of attributes to be returned + <max_attrs>: number of elements in <attrs> + <pnum_attrs>: if not NULL, will return how many attrs were found. + <errstring>: pointer to a buffer for storing error info, or NULL. + Return: upon success, a pointer to an allocated copy of <countername>, or + NULL if there's a failure. (A copy is made in order to provide storage + for the ca_name fields in the <attrs> array.) + + The pointer should be freed when <attrs> is no longer in use. + <attrs> will be filled in data from countername. + <pnum_attrs> will have the number of elements in <attrs>. May be + non-zero even if return value indicates an error. + <errstring> NULL if no error, otherwise, a malloc'd GTXT string. + */ + + extern int hwcfuncs_bind_descriptor (const char *defstring); + /* Bind counters to resources. + Input: + <defstring>: string whose format is + :%s:%s:0x%x:%d:%d,0x%x[:%s...repeat for each ctr] + where the fields are: + :<userName>:<internalCtr>:<register>:<timeoutVal>:<tag>:<memop> + Return: 0 if successful + HWCFUNCS_ERROR_HWCINIT if resources unavailable + HWCFUNCS_ERROR_HWCARGS if counters were not specified correctly + */ + + extern int hwcfuncs_bind_hwcentry (const Hwcentry *entries[], + unsigned numctrs); + /* Bind counters to resources. + Input: + <entries>: array of counters + <numctrs>: number of items in <entries> + Return: 0 if successful + HWCFUNCS_ERROR_HWCINIT if resources unavailable + HWCFUNCS_ERROR_HWCARGS if counters were not specified correctly + */ + + extern int hwcfuncs_assign_regnos (Hwcentry *entries[], unsigned numctrs); + /* Assign entries[]->reg_num values as needed by platform + Note: modifies <entries> by supplying a regno to each counter + Input: + <entries>: array of counters + <numctrs>: number of items in <entries> + Output: + <entries>: array of counters is modified + Return: 0 if successful + HWCFUNCS_ERROR_HWCINIT if resources unavailable + HWCFUNCS_ERROR_HWCARGS if counters were not specified correctly + */ + + extern int regno_is_valid (const Hwcentry *pctr, regno_t regno); + /* return 1 if <regno> is in Hwcentry's list + Input: + <pctr>: counter definition, reg_list[] should be initialized + <regno>: register to check + Return: 1 if <regno> is in Hwcentry's list, 0 otherwise + */ + + extern Hwcentry **hwcfuncs_get_ctrs (unsigned *defcnt); + /* Get descriptions of the currently bound counters. + Input: + <defcnt>: if not NULL, returns number of counter definitions. + Return: + table of counter definition pointers + */ + + extern char *hwcfuncs_errmsg_get (char * buf, size_t bufsize, + int enable_capture); + /* Gets a recent HWC error message. + To clear previous error messages and insure error message is enabled, + call hwcfuncs_errmsg_get(NULL,0,1). + Once enabled, one error is stored in an internal buffer. A call to this + function will clear the buffer and allow a new message to be captured. + Note: Not MT-safe - don't enable this feature in an MT environment. + Input: + <buf>: pointer to buffer or NULL. + <bufsize>: size of <buf> + <enable_capture>: 0 - disable buffering, 1 - enable buffering. + Return: error string or an empty string. + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ! __HWCFUNCS_H */ diff --git a/gprofng/common/hwctable.c b/gprofng/common/hwctable.c new file mode 100644 index 0000000..bc441e1 --- /dev/null +++ b/gprofng/common/hwctable.c @@ -0,0 +1,5410 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <limits.h> + +#include "hwcdrv.h" +#include "hwcfuncs.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +/*---------------------------------------------------------------------------*/ +/* compile options */ + +#define DISALLOW_USI_USII_6357446 +/* Solaris 9/libcpc1 allows cpc_bind() to work on US-IIe processors, even + though this processor cannot generate profiling interrupts. */ + +#define DISALLOW_PENTIUM_PRO_MMX_7007575 +/* Solaris/libcpc2 defaults to "Pentium Pro with MMX, Pentium II" + when it doesn't recognize an Intel processor. As a result, + when collect attempts to start Pentium Pro counters on a + new machine (e.g. Westmere as of 1/2011), the OS may hang. */ + +/* Register 0 counter doesn't work on Niagara T1 version (?) */ +#define WORKAROUND_6231196_NIAGARA1_NO_CTR_0 + +/*---------------------------------------------------------------------------*/ +/* consts, macros */ + +/* 10^N rates */ +#define PRELOADS_9 1001000001 +#define PRELOADS_85 320100001 +#define PRELOADS_8 100100001 +#define PRELOADS_75 32010001 +#define PRELOADS_7 10010001 +#define PRELOADS_65 3201001 +#define PRELOADS_6 1001001 +#define PRELOADS_55 320101 +#define PRELOADS_5 100101 +#define PRELOADS_45 32001 +#define PRELOADS_4 10001 +#define PRELOADS_35 3201 +#define PRELOADS_3 1001 +#define PRELOADS_25 301 + +#define ABST_TBD ABST_NONE /* to be determined */ + +/*---------------------------------------------------------------------------*/ +/* prototypes */ +static void hwc_cb (uint_t cpc_regno, const char *name); +static void attrs_cb (const char *attr); +static int attr_is_valid (int forKernel, const char *attr); + +/*---------------------------------------------------------------------------*/ +/* HWC definition tables */ + +/* + comments on hwcentry tables + --------------------------- + name: this field should not contain '~'. + int_name: actual name of register, may contain ~ attribute specifications. + regnum: assigned register. + metric: if non-NULL, is a 'standard' counter that will show up in help. + timecvt: >0: can convert to time, 'timecvt' CPU cycles per event + =0: counts events + <0: can convert to time, count reference-clock cycles at '-timecvt' MHz + memop: see description for ABST_type enum + */ + +// PRELOAD(): generates an interval based on the cycles/event and CPU GHZ. +// Note: the macro tweaks the interval so that it ends in decimal 001. +#define CYC_PER_SAMPLE (1000ULL*1000*1000/100) // cycles per signal at 1ghz, 100 samples/second +#define PRELOAD(min_cycles_per_event,ghz) (((ghz)*CYC_PER_SAMPLE/(min_cycles_per_event))/100*100+1) + +// PRELOAD_DEF: initial value for uncalibrated events. +// This value should be based on a rate that will work for the slowest changing +// HWCs, HWCs where there are many CPU cycles between events. +// +// The interval needs to target the slowest HWCs so that +// automatic adjustment of HWC overflow intervals can adapt. +#define PRELOAD_DEF PRELOAD(1000,3) // default interval targets 1000 cycles/event at 3ghz +// For er_kernel, which HWC intervals cannot be adjusted automatically for ON/HI/LO, +// The interval should target some safe interval for fast events +#define PRELOAD_DEF_ERKERNEL PRELOAD(4,4) // default interval targets 4 cycles/event at 4ghz + +static const Hwcentry empty_ctr = {NULL, NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, 0}; + + +// --- use cycles counter to expose "system_time" on Linux --- +#define SYSTIME_REGNOS REGNO_ANY // Linux: make sys_time/usr_time available for data collection +// Note: For x86, Linux and Solaris use different ref-clock names +#define USE_INTEL_REF_CYCLES(MHZ) \ + {"usr_time","unhalted-reference-cycles", SYSTIME_REGNOS, STXT("User CPU"), PRELOAD(900,MHZ), -(MHZ), ABST_NONE}, \ + {"usr_time","cpu_clk_unhalted.ref_p", SYSTIME_REGNOS, STXT("User CPU"), PRELOAD(900,MHZ), -(MHZ), ABST_NONE}, \ + {"sys_time","unhalted-reference-cycles~system=1~user=0", SYSTIME_REGNOS, STXT("System CPU"), PRELOAD(900,MHZ), -(MHZ), ABST_NONE}, \ + {"sys_time","cpu_clk_unhalted.ref_p~system=1~user=0", SYSTIME_REGNOS, STXT("System CPU"), PRELOAD( 900,MHZ), -(MHZ), ABST_NONE}, \ + {"cycles0", "unhalted-reference-cycles", 0, NULL, PRELOAD( 900,MHZ), -(MHZ), ABST_NONE}, /*hidden*/ \ + {"cycles0", "cpu_clk_unhalted.ref_p", 0, NULL, PRELOAD( 900,MHZ), -(MHZ), ABST_NONE}, /*hidden*/ \ + {"cycles1", "unhalted-reference-cycles", 1, NULL, PRELOAD( 910,MHZ), -(MHZ), ABST_NONE}, /*hidden*/ \ + {"cycles1", "cpu_clk_unhalted.ref_p", 1, NULL, PRELOAD( 910,MHZ), -(MHZ), ABST_NONE}, /*hidden*/ \ + /* end of list */ + +#define SPARC_CYCLES \ + {"usr_time","Cycles_user", SYSTIME_REGNOS, STXT("User CPU"), PRELOADS_75,1, ABST_NONE}, \ + {"sys_time","Cycles_user~system=1~user=0", SYSTIME_REGNOS, STXT("System CPU"), PRELOADS_75,1, ABST_NONE}, \ + /* end of list */ + + +/* --- PERF_EVENTS "software" definitions --- */ +#define PERF_EVENTS_SW_EVENT_ALIASES \ +// none supported for now +#if 0 + {"usr", "PERF_COUNT_SW_TASK_CLOCK", REGNO_ANY, STXT("User CPU"), PRELOADS_7, -(1000), ABST_NONE}, \ + {"sys", "PERF_COUNT_SW_TASK_CLOCK~system=1~user=0", REGNO_ANY, STXT("System CPU"), PRELOADS_7, -(1000), ABST_NONE}, \ + /* end of list */ +#endif + +#define PERF_EVENTS_SW_EVENT_DEFS \ +// none supported for now +#if 0 + {"PERF_COUNT_SW_TASK_CLOCK", NULL, REGNO_ANY, NULL, PRELOADS_7, -(1000),ABST_NONE}, \ + /* end of list */ +#endif + +/* + * The PAPI descriptive strings used to be wrapped with STXT(), + * a macro defined in perfan/include/i18n.h. For the time being, + * we want to demote the PAPI counters by omitting the + * descriptions. So we use a new macro PAPITXT() for this purpose. + */ +#define PAPITXT(x) NULL + +/* Solaris "Generic" Counters */ +static Hwcentry papi_generic_list[] = { + {"PAPI_l1_dcm", NULL, REGNO_ANY, PAPITXT ("L1 D-cache misses"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l1_icm", NULL, REGNO_ANY, PAPITXT ("L1 I-cache misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l2_dcm", NULL, REGNO_ANY, PAPITXT ("L2 D-cache misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l2_icm", NULL, REGNO_ANY, PAPITXT ("L2 I-cache misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l3_dcm", NULL, REGNO_ANY, PAPITXT ("L3 D-cache misses"), PRELOADS_5, 0, ABST_NONE}, + {"PAPI_l3_icm", NULL, REGNO_ANY, PAPITXT ("L3 I-cache misses"), PRELOADS_5, 0, ABST_NONE}, + {"PAPI_l1_tcm", NULL, REGNO_ANY, PAPITXT ("L1 misses"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l2_tcm", NULL, REGNO_ANY, PAPITXT ("L2 misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l3_tcm", NULL, REGNO_ANY, PAPITXT ("L3 misses"), PRELOADS_5, 0, ABST_NONE}, + {"PAPI_ca_snp", NULL, REGNO_ANY, PAPITXT ("Requests for a snoop"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_ca_shr", NULL, REGNO_ANY, PAPITXT ("Requests for exclusive access to shared cache line"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_ca_cln", NULL, REGNO_ANY, PAPITXT ("Requests for exclusive access to clean cache line"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_ca_inv", NULL, REGNO_ANY, PAPITXT ("Requests for cache line invalidation"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_ca_itv", NULL, REGNO_ANY, PAPITXT ("Requests for cache line intervention"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l3_ldm", NULL, REGNO_ANY, PAPITXT ("L3 load misses"), PRELOADS_5, 0, ABST_NONE}, + {"PAPI_l3_stm", NULL, REGNO_ANY, PAPITXT ("L3 store misses"), PRELOADS_5, 0, ABST_NONE}, + {"PAPI_bru_idl", NULL, REGNO_ANY, PAPITXT ("Cycles branch units are idle"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_fxu_idl", NULL, REGNO_ANY, PAPITXT ("Cycles integer units are idle"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_fpu_idl", NULL, REGNO_ANY, PAPITXT ("Cycles FP units are idle"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_lsu_idl", NULL, REGNO_ANY, PAPITXT ("Cycles load/store units are idle"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_tlb_dm", NULL, REGNO_ANY, PAPITXT ("DTLB misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_tlb_im", NULL, REGNO_ANY, PAPITXT ("ITLB misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_tlb_tl", NULL, REGNO_ANY, PAPITXT ("Total TLB misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_tlb_tm", NULL, REGNO_ANY, PAPITXT ("Total TLB misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_ldm", NULL, REGNO_ANY, PAPITXT ("L1 load misses"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l1_stm", NULL, REGNO_ANY, PAPITXT ("L1 store misses"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l2_ldm", NULL, REGNO_ANY, PAPITXT ("L2 load misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l2_stm", NULL, REGNO_ANY, PAPITXT ("L2 store misses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_btac_m", NULL, REGNO_ANY, PAPITXT ("Branch target address cache misses"), PRELOADS_5, 0, ABST_NONE}, + {"PAPI_prf_dm", NULL, REGNO_ANY, PAPITXT ("Data prefetch cache misses"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_dch", NULL, REGNO_ANY, PAPITXT ("L3 D-cache hits"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_tlb_sd", NULL, REGNO_ANY, PAPITXT ("TLB shootdowns"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_csr_fal", NULL, REGNO_ANY, PAPITXT ("Failed store conditional instructions"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_csr_suc", NULL, REGNO_ANY, PAPITXT ("Successful store conditional instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_csr_tot", NULL, REGNO_ANY, PAPITXT ("Total store conditional instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_mem_scy", NULL, REGNO_ANY, PAPITXT ("Cycles Stalled Waiting for memory accesses"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_mem_rcy", NULL, REGNO_ANY, PAPITXT ("Cycles Stalled Waiting for memory reads"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_mem_wcy", NULL, REGNO_ANY, PAPITXT ("Cycles Stalled Waiting for memory writes"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_stl_icy", NULL, REGNO_ANY, PAPITXT ("Cycles with no instruction issue"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_ful_icy", NULL, REGNO_ANY, PAPITXT ("Cycles with maximum instruction issue"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_stl_ccy", NULL, REGNO_ANY, PAPITXT ("Cycles with no instructions completed"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_ful_ccy", NULL, REGNO_ANY, PAPITXT ("Cycles with maximum instructions completed"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_hw_int", NULL, REGNO_ANY, PAPITXT ("Hardware interrupts"), PRELOADS_5, 0, ABST_NONE}, + {"PAPI_br_ucn", NULL, REGNO_ANY, PAPITXT ("Unconditional branch instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_br_cn", NULL, REGNO_ANY, PAPITXT ("Cond. branch instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_br_tkn", NULL, REGNO_ANY, PAPITXT ("Cond. branch instructions taken"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_br_ntk", NULL, REGNO_ANY, PAPITXT ("Cond. branch instructions not taken"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_br_msp", NULL, REGNO_ANY, PAPITXT ("Cond. branch instructions mispredicted"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_br_prc", NULL, REGNO_ANY, PAPITXT ("Cond. branch instructions correctly predicted"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_fma_ins", NULL, REGNO_ANY, PAPITXT ("FMA instructions completed"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_tot_iis", NULL, REGNO_ANY, PAPITXT ("Instructions issued"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_tot_ins", NULL, REGNO_ANY, PAPITXT ("Instructions completed"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_int_ins", NULL, REGNO_ANY, PAPITXT ("Integer instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_fp_ins", NULL, REGNO_ANY, PAPITXT ("Floating-point instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_ld_ins", NULL, REGNO_ANY, PAPITXT ("Load instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_sr_ins", NULL, REGNO_ANY, PAPITXT ("Store instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_br_ins", NULL, REGNO_ANY, PAPITXT ("Branch instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_vec_ins", NULL, REGNO_ANY, PAPITXT ("Vector/SIMD instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_res_stl", NULL, REGNO_ANY, PAPITXT ("Cycles stalled on any resource"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_fp_stal", NULL, REGNO_ANY, PAPITXT ("Cycles the FP unit(s) are stalled"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_tot_cyc", NULL, REGNO_ANY, PAPITXT ("Total cycles"), PRELOADS_7, 1, ABST_NONE}, + {"PAPI_lst_ins", NULL, REGNO_ANY, PAPITXT ("Load/store instructions completed"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_syc_ins", NULL, REGNO_ANY, PAPITXT ("Sync instructions completed"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l1_dch", NULL, REGNO_ANY, PAPITXT ("L1 D-cache hits"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_dch", NULL, REGNO_ANY, PAPITXT ("L2 D-cache hits"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l1_dca", NULL, REGNO_ANY, PAPITXT ("L1 D-cache accesses"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_dca", NULL, REGNO_ANY, PAPITXT ("L2 D-cache accesses"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_dca", NULL, REGNO_ANY, PAPITXT ("L3 D-cache accesses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_dcr", NULL, REGNO_ANY, PAPITXT ("L1 D-cache reads"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_dcr", NULL, REGNO_ANY, PAPITXT ("L2 D-cache reads"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_dcr", NULL, REGNO_ANY, PAPITXT ("L3 D-cache reads"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_dcw", NULL, REGNO_ANY, PAPITXT ("L1 D-cache writes"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_dcw", NULL, REGNO_ANY, PAPITXT ("L2 D-cache writes"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_dcw", NULL, REGNO_ANY, PAPITXT ("L3 D-cache writes"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_ich", NULL, REGNO_ANY, PAPITXT ("L1 I-cache hits"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_ich", NULL, REGNO_ANY, PAPITXT ("L2 I-cache hits"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_ich", NULL, REGNO_ANY, PAPITXT ("L3 I-cache hits"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_ica", NULL, REGNO_ANY, PAPITXT ("L1 I-cache accesses"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_ica", NULL, REGNO_ANY, PAPITXT ("L2 I-cache accesses"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_ica", NULL, REGNO_ANY, PAPITXT ("L3 I-cache accesses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_icr", NULL, REGNO_ANY, PAPITXT ("L1 I-cache reads"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_icr", NULL, REGNO_ANY, PAPITXT ("L2 I-cache reads"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_icr", NULL, REGNO_ANY, PAPITXT ("L3 I-cache reads"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_icw", NULL, REGNO_ANY, PAPITXT ("L1 I-cache writes"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_icw", NULL, REGNO_ANY, PAPITXT ("L2 I-cache writes"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_icw", NULL, REGNO_ANY, PAPITXT ("L3 I-cache writes"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_tch", NULL, REGNO_ANY, PAPITXT ("L1 total hits"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_tch", NULL, REGNO_ANY, PAPITXT ("L2 total hits"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_tch", NULL, REGNO_ANY, PAPITXT ("L3 total hits"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_tca", NULL, REGNO_ANY, PAPITXT ("L1 total accesses"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_tca", NULL, REGNO_ANY, PAPITXT ("L2 total accesses"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_tca", NULL, REGNO_ANY, PAPITXT ("L3 total accesses"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_tcr", NULL, REGNO_ANY, PAPITXT ("L1 total reads"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_tcr", NULL, REGNO_ANY, PAPITXT ("L2 total reads"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_tcr", NULL, REGNO_ANY, PAPITXT ("L3 total reads"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_l1_tcw", NULL, REGNO_ANY, PAPITXT ("L1 total writes"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_l2_tcw", NULL, REGNO_ANY, PAPITXT ("L2 total writes"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_l3_tcw", NULL, REGNO_ANY, PAPITXT ("L3 total writes"), PRELOADS_6, 0, ABST_NONE}, + {"PAPI_fml_ins", NULL, REGNO_ANY, PAPITXT ("FP multiply instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_fad_ins", NULL, REGNO_ANY, PAPITXT ("FP add instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_fdv_ins", NULL, REGNO_ANY, PAPITXT ("FP divide instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_fsq_ins", NULL, REGNO_ANY, PAPITXT ("FP square root instructions"), PRELOADS_65, 0, ABST_NONE}, + {"PAPI_fnv_ins", NULL, REGNO_ANY, PAPITXT ("FP inverse instructions"), PRELOADS_7, 0, ABST_NONE}, + {"PAPI_fp_ops", NULL, REGNO_ANY, PAPITXT ("FP operations"), PRELOADS_7, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry usIlist[] = { + {"cycles", "Cycle_cnt", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"insts", "Instr_cnt", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry usIIIlist[] = /* III, IIIi, IIIp. Note that some counters are processor-specific */{ + {"cycles", "Cycle_cnt", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"insts", "Instr_cnt", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, + {"icm", "IC_miss", REGNO_ANY, STXT ("I$ Misses"), PRELOADS_5, 0, ABST_NONE}, + {"dcrm", "DC_rd_miss", REGNO_ANY, STXT ("D$ Read Misses"), PRELOADS_5, 0, ABST_LOAD}, + {"dcwm", "DC_wr_miss", REGNO_ANY, STXT ("D$ Write Misses"), PRELOADS_5, 0, ABST_STORE}, + {"dcr", "DC_rd", REGNO_ANY, STXT ("D$ Read Refs"), PRELOADS_6, 0, ABST_LOAD}, + {"dcw", "DC_wr", REGNO_ANY, STXT ("D$ Write Refs"), PRELOADS_6, 0, ABST_STORE}, + {"ecref", "EC_ref", REGNO_ANY, STXT ("E$ Refs"), PRELOADS_6, 0, ABST_LDST}, + {"itlbm", "ITLB_miss", REGNO_ANY, STXT ("ITLB Misses"), PRELOADS_5, 0, ABST_NONE}, + {"dtlbm", "DTLB_miss", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_5, 0, ABST_US_DTLBM}, + {"ecm", "EC_misses", REGNO_ANY, STXT ("E$ Misses"), PRELOADS_5, 0, ABST_LDST}, + {"ecrm", "EC_rd_miss", REGNO_ANY, STXT ("E$ Read Misses"), PRELOADS_5, 0, ABST_LOAD}, + {"ecml", "EC_miss_local", REGNO_ANY, STXT ("E$ Local Misses"), PRELOADS_5, 0, ABST_LDST}, + {"ecmr", "EC_miss_remote", REGNO_ANY, STXT ("E$ Remote Misses"), PRELOADS_5, 0, ABST_LDST}, + {"ecim", "EC_ic_miss", REGNO_ANY, STXT ("E$ Instr. Misses"), PRELOADS_5, 0, ABST_NONE}, + {"icstall", "Dispatch0_IC_miss", REGNO_ANY, STXT ("I$ Stall Cycles"), PRELOADS_6, 1, ABST_NONE}, + {"dcstall", "Re_DC_miss", REGNO_ANY, STXT ("D$ and E$ Stall Cycles"), PRELOADS_6, 1, ABST_LOAD}, + {"ecstall", "Re_EC_miss", REGNO_ANY, STXT ("E$ Stall Cycles"), PRELOADS_6, 1, ABST_LOAD}, + {"sqstall", "Rstall_storeQ", REGNO_ANY, STXT ("StoreQ Stall Cycles"), PRELOADS_6, 1, ABST_STORE}, + {"rawstall", "Re_RAW_miss", REGNO_ANY, STXT ("RAW Stall Cycles"), PRELOADS_6, 1, ABST_LOAD}, + {"dcmissov", "Re_DC_missovhd", REGNO_ANY, STXT ("DC Miss Ovhd"), PRELOADS_6, 1, ABST_LOAD}, + {"fpustall", "Re_FPU_bypass", REGNO_ANY, STXT ("FPU Stall Cycles"), PRELOADS_6, 1, ABST_NONE}, + {"fpusestall", "Rstall_FP_use", REGNO_ANY, STXT ("FPU Use Stall Cycles"), PRELOADS_6, 1, ABST_NONE}, + {"iustall", "Rstall_IU_use", REGNO_ANY, STXT ("IU Stall Cycles"), PRELOADS_6, 1, ABST_NONE}, + {"fpadd", "FA_pipe_completion", REGNO_ANY, STXT ("FP Adds"), PRELOADS_6, 0, ABST_NONE}, + {"fpmul", "FM_pipe_completion", REGNO_ANY, STXT ("FP Muls"), PRELOADS_6, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"Cycle_cnt", NULL, REGNO_ANY, NULL, PRELOADS_7, 1, ABST_NONE}, + {"EC_miss_mtag_remote", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"DC_rd_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"DC_wr_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"DC_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"DC_wr", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"EC_ref", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"EC_snoop_inv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC /*?*/}, + {"EC_wb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"EC_wb_remote", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"DTLB_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_US_DTLBM}, + {"EC_misses", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"EC_rd_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"PC_port0_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"EC_miss_local", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"EC_miss_remote", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"EC_snoop_cb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC /*?*/}, + {"WC_snoop_cb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC /*?*/}, + {"WC_scrubbed", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"WC_wb_wo_read", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"PC_MS_misses", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"PC_soft_hit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"PC_hard_hit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"PC_port1_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"PC_snoop_inv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE /*?*/}, + {"SW_count_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_COUNT}, + {"SW_count_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_COUNT}, + {"Dispatch0_IC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Dispatch0_mispred", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Dispatch0_br_target", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Dispatch0_2nd_br", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Dispatch_rs_mispred", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Rstall_storeQ", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_STORE}, + {"Rstall_FP_use", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Rstall_IU_use", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"EC_write_hit_RTO", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"Re_RAW_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Re_DC_missovhd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Re_endian_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Re_FPU_bypass", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Re_DC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Re_EC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Re_PC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"SI_snoop", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"SI_ciq_flow", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"SI_owned", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_msl_busy_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_mdb_overflow_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_page_close_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_reads_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_reads_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_reads_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_reads_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_writes_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_writes_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_writes_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_writes_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_stalls_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_stalls_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_stalls_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_stalls_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + + /* additional (hidden) aliases, for convenience */ + {"cycles0", "Cycle_cnt", 0, NULL, PRELOADS_75, 1, ABST_NONE}, + {"cycles1", "Cycle_cnt", 1, NULL, PRELOADS_75, 1, ABST_NONE}, + {"insts0", "Instr_cnt", 0, NULL, PRELOADS_75, 0, ABST_NONE}, + {"insts1", "Instr_cnt", 1, NULL, PRELOADS_75, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry usIVplist[] = { + {"cycles", "Cycle_cnt", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"insts", "Instr_cnt", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, + {"icm", "IC_fill", REGNO_ANY, STXT ("I$ Misses"), PRELOADS_5, 0, ABST_NONE}, + {"dcrm", "DC_rd_miss", REGNO_ANY, STXT ("D$ Read Misses"), PRELOADS_5, 0, ABST_LOAD}, + {"dcwm", "DC_wr_miss", REGNO_ANY, STXT ("D$ Write Misses"), PRELOADS_5, 0, ABST_STORE}, + {"dcr", "DC_rd", REGNO_ANY, STXT ("D$ Read Refs"), PRELOADS_6, 0, ABST_LOAD}, + {"dcw", "DC_wr", REGNO_ANY, STXT ("D$ Write Refs"), PRELOADS_6, 0, ABST_STORE}, + {"itlbm", "ITLB_miss", REGNO_ANY, STXT ("ITLB Misses"), PRELOADS_5, 0, ABST_NONE}, + {"dtlbm", "DTLB_miss", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_5, 0, ABST_US_DTLBM}, + {"l2ref", "L2_ref", REGNO_ANY, STXT ("L2$ Refs"), PRELOADS_5, 0, ABST_LDST}, + {"l2m", "L2_miss", REGNO_ANY, STXT ("L2$ Misses"), PRELOADS_5, 0, ABST_LDST}, + {"l2rm", "L2_rd_miss", REGNO_ANY, STXT ("L2$ Read Misses"), PRELOADS_5, 0, ABST_LOAD}, + {"l2im", "L2_IC_miss", REGNO_ANY, STXT ("L2$ Instr. Misses"), PRELOADS_5, 0, ABST_NONE}, + {"ecm", "L3_miss", REGNO_ANY, STXT ("E$ Misses"), PRELOADS_5, 0, ABST_LDST}, + {"ecrm", "L3_rd_miss", REGNO_ANY, STXT ("E$ Read Misses"), PRELOADS_5, 0, ABST_LOAD}, + {"ecml", "SSM_L3_miss_local", REGNO_ANY, STXT ("E$ Local Misses"), PRELOADS_5, 0, ABST_LDST}, + {"ecmr", "SSM_L3_miss_remote", REGNO_ANY, STXT ("E$ Remote Misses"), PRELOADS_5, 0, ABST_LDST}, + {"ecim", "L3_IC_miss", REGNO_ANY, STXT ("E$ Instr. Misses"), PRELOADS_5, 0, ABST_NONE}, + {"icstall", "Dispatch0_IC_miss", REGNO_ANY, STXT ("I$ Stall Cycles"), PRELOADS_6, 1, ABST_NONE}, + {"dcstall", "Re_DC_miss", REGNO_ANY, STXT ("D$ and E$ Stall Cycles"), PRELOADS_6, 1, ABST_LOAD}, + {"ecstall", "Re_L3_miss", REGNO_ANY, STXT ("E$ Stall Cycles"), PRELOADS_6, 1, ABST_LOAD}, + {"sqstall", "Rstall_storeQ", REGNO_ANY, STXT ("StoreQ Stall Cycles"), PRELOADS_6, 1, ABST_STORE}, + {"rawstall", "Re_RAW_miss", REGNO_ANY, STXT ("RAW Stall Cycles"), PRELOADS_6, 1, ABST_LOAD}, + {"dcmissov", "Re_DC_missovhd", REGNO_ANY, STXT ("DC Miss Ovhd"), PRELOADS_6, 1, ABST_LOAD}, + {"fpustall", "Re_FPU_bypass", REGNO_ANY, STXT ("FPU Stall Cycles"), PRELOADS_6, 1, ABST_NONE}, + {"fpusestall", "Rstall_FP_use", REGNO_ANY, STXT ("FPU Use Stall Cycles"), PRELOADS_6, 1, ABST_NONE}, + {"iustall", "Rstall_IU_use", REGNO_ANY, STXT ("IU Stall Cycles"), PRELOADS_6, 1, ABST_NONE}, + {"fpadd", "FA_pipe_completion", REGNO_ANY, STXT ("FP Adds"), PRELOADS_6, 0, ABST_NONE}, + {"fpmul", "FM_pipe_completion", REGNO_ANY, STXT ("FP Muls"), PRELOADS_6, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"Cycle_cnt", NULL, REGNO_ANY, NULL, PRELOADS_7, 1, ABST_NONE}, + {"DC_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"DC_rd_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"DC_wr", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"DC_wr_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"DTLB_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_US_DTLBM}, + {"Dispatch0_2nd_br", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Dispatch0_IC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Dispatch0_other", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2L3_snoop_cb_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC /*?*/}, + {"L2L3_snoop_inv_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC /*?*/}, + {"L2_hit_I_state_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST /*?*/}, + {"L2_hit_other_half", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"L2_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"L2_rd_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"L2_ref", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"L2_snoop_cb_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC /*?*/}, + {"L2_snoop_inv_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC /*?*/}, + {"L2_wb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"L2_wb_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"L2_write_hit_RTO", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"L2_write_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"L3_hit_I_state_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"L3_hit_other_half", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"L3_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"L3_rd_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"L3_wb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"L3_wb_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"L3_write_hit_RTO", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"L3_write_miss_RTO", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE}, + {"MC_reads_0_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_reads_1_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_reads_2_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_reads_3_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_stalls_0_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_stalls_1_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_stalls_2_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_stalls_3_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"MC_writes_0_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_writes_1_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_writes_2_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"MC_writes_3_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + /*? {"PC_MS_misses", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, */ + {"PC_hard_hit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"PC_inv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE /*?*/}, + {"PC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"PC_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"PC_soft_hit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LOAD}, + {"Re_DC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Re_DC_missovhd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Re_FPU_bypass", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Re_L2_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Re_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Re_PFQ_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Re_RAW_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_LOAD}, + {"Rstall_FP_use", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Rstall_IU_use", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Rstall_storeQ", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_STORE}, + {"SI_RTO_src_data", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"SI_RTS_src_data", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"SI_ciq_flow_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NOPC}, + {"SI_owned_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"SI_snoop_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NOPC}, + {"ecml", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"ecmr", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST}, + {"SSM_L3_miss_local", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST /*?*/}, + {"SSM_L3_miss_mtag_remote", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST /*?*/}, + {"SSM_L3_miss_remote", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_LDST /*?*/}, + {"SSM_L3_wb_remote", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_STORE /*?*/}, + {"SSM_new_transaction_sh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_TBD /*?*/}, + + /* additional (hidden) aliases, for convenience */ + {"cycles0", "Cycle_cnt", 0, NULL, PRELOADS_75, 1, ABST_NONE}, + {"cycles1", "Cycle_cnt", 1, NULL, PRELOADS_75, 1, ABST_NONE}, + {"insts0", "Instr_cnt", 0, NULL, PRELOADS_75, 0, ABST_NONE}, + {"insts1", "Instr_cnt", 1, NULL, PRELOADS_75, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry niagara1[] = + /* CPC_ULTRA_T1 , "UltraSPARC T1" */{ + {"insts", "Instr_cnt", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, +#ifndef WORKAROUND_6231196_NIAGARA1_NO_CTR_0 /* since register 0 counter don't work XXX */ + {"icm", "IC_miss", REGNO_ANY, STXT ("I$ Misses"), PRELOADS_5, 0, ABST_NONE}, + {"itlbm", "ITLB_miss", REGNO_ANY, STXT ("ITLB Misses"), PRELOADS_5, 0, ABST_NONE}, + {"ecim", "L2_imiss", REGNO_ANY, STXT ("E$ Instr. Misses"), PRELOADS_4, 0, ABST_NONE}, + {"dcm", "DC_miss", REGNO_ANY, STXT ("D$ Misses"), PRELOADS_5, 0, ABST_EXACT}, + {"dtlbm", "DTLB_miss", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_5, 0, ABST_EXACT}, + {"ecdm", "L2_dmiss_ld", REGNO_ANY, STXT ("E$ Data Misses"), PRELOADS_4, 0, ABST_EXACT}, + {"flops", "FP_instr_cnt", REGNO_ANY, STXT ("Floating-point Ops"), PRELOADS_6, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"SB_full", NULL, REGNO_ANY, NULL, PRELOADS_6, 1, ABST_NONE}, + {"DC_miss", NULL, REGNO_ANY, NULL, PRELOADS_6, 0, ABST_EXACT}, + {"DTLB_miss", NULL, REGNO_ANY, NULL, PRELOADS_6, 0, ABST_EXACT}, + {"L2_dmiss_ld", NULL, REGNO_ANY, NULL, PRELOADS_6, 0, ABST_EXACT}, +#endif + + /* additional (hidden) aliases, for convenience */ + {"insts1", "Instr_cnt", 1, NULL, PRELOADS_75, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry niagara2[] = { + /* CPC_ULTRA_T2 , "UltraSPARC T2" */ + /* CPC_ULTRA_T2 , "UltraSPARC T2+" */ + {"insts", "Instr_cnt", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, + {"loads", "Instr_ld", REGNO_ANY, STXT ("Load Instructions"), PRELOADS_7, 0, ABST_EXACT}, + {"stores", "Instr_st", REGNO_ANY, STXT ("Store Instructions"), PRELOADS_6, 0, ABST_EXACT}, + {"dcm", "DC_miss", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_6, 0, ABST_EXACT}, + {"dtlbm", "DTLB_miss", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_6, 0, ABST_NONE}, + {"l2drm", "L2_dmiss_ld", REGNO_ANY, STXT ("L2 D-cache Read Misses (See Bug 15664448)"), PRELOADS_5, 0, ABST_EXACT}, + {"icm", "IC_miss", REGNO_ANY, STXT ("L1 I-cache Misses"), PRELOADS_5, 0, ABST_NONE}, + {"itlbm", "ITLB_miss", REGNO_ANY, STXT ("ITLB Misses"), PRELOADS_5, 0, ABST_NONE}, + {"l2im", "L2_imiss", REGNO_ANY, STXT ("L2 I-cache Misses"), PRELOADS_4, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"Instr_ld", NULL, REGNO_ANY, NULL, PRELOADS_7, 0, ABST_EXACT}, + {"Instr_st", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"Atomics", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"L2_dmiss_ld", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DTLB_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"DES_3DES_busy_cycle", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"AES_busy_cycle", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Kasumi_busy_cycle", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"MD5_SHA-1_SHA-256_busy_cycle", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"MA_busy_cycle", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* additional (hidden) aliases, for convenience */ + {"insts1", "Instr_cnt", 1, NULL, PRELOADS_75, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry sparc_t4[] = { + // Identical to sparc_t5_m6 except for: l3m_spec + // when updating this table, also update sparc_t5_m6[] + // obsolete aliases marked with REGNO_INVALID (allows reading of older experiments) + {"l2l3dh", "DC_miss_L2_L3_hit_nospec", REGNO_INVALID, STXT ("L2 or L3 D-cache Hits"), PRELOADS_6, 0, ABST_EXACT}, // undercounts due to thread-hog issue + {"l3m", "DC_miss_remote_L3_hit_nospec~emask=0x6", REGNO_INVALID, STXT ("L3 D-cache Misses"), PRELOADS_5, 0, ABST_EXACT}, // undercounts due to thread-hog issue + {"lmh", "DC_miss_local_hit_nospec", REGNO_INVALID, STXT ("Local Mem. Hits"), PRELOADS_5, 0, ABST_EXACT}, // undercounts due to thread-hog issue + {"rmh", "DC_miss_remote_L3_hit_nospec", REGNO_INVALID, STXT ("Remote Mem. Hits"), PRELOADS_5, 0, ABST_EXACT}, // undercounts due to thread-hog issue + {"pqs", "PQ_tag_wait", REGNO_INVALID, STXT ("Pick Queue Stalls"), PRELOADS_7, 1, ABST_NONE}, // old alias name + {"raw_stb", "RAW_hit_st_buf", REGNO_INVALID, STXT ("RAW Hazard in Store Buffer"), PRELOADS_55, 0, ABST_NONE}, // 11@full hit, 60@partial hit (in future, combine w/st_q) + {"raw_stq", "RAW_hit_st_q", REGNO_INVALID, STXT ("RAW Hazard in Store Queue"), PRELOADS_55, 0, ABST_NONE}, // 11@full hit, 60@partial hit (in future, combine w/st_buf) + {"sel_stalls", "Sel_0_ready", REGNO_INVALID, STXT ("Stalls Another Thread Selected"), PRELOADS_7, 1, ABST_NONE}, + {"icm", "IC_miss", REGNO_INVALID, STXT ("L1 I-Cache Misses"), PRELOADS_55, 0, ABST_NONE}, // 20@ l2/l3 hit (guess) + {"icm_stalls", "IC_miss", REGNO_INVALID, STXT ("L1 I-Cache Miss Est Stalls"), PRELOADS_55, 25, ABST_NONE}, // 25@ l2-20/l3-50 + + // current aliases + SPARC_CYCLES + {"cycles", "Cycles_user", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "Instr_all", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"c_stalls", "Commit_0", REGNO_ANY, STXT ("Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"loads", "Instr_ld", REGNO_ANY, STXT ("Load Instructions"), PRELOADS_7, 0, ABST_EXACT}, + {"stores", "Instr_st", REGNO_ANY, STXT ("Store Instructions"), PRELOADS_7, 0, ABST_EXACT}, + {"dcm", "DC_miss_nospec", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_EXACT}, + {"l3m_spec", "DC_miss_local_hit~emask=0x6", REGNO_ANY, STXT ("L3 D-cache Speculative Misses"), PRELOADS_5, 0, ABST_NONE, STXT ("Loads that speculatively missed local L3")}, // T4 encoding (430 lm, 690 rm) ~5 misses overlap on t5/pico_ile + // {"l3m_spec", "DC_miss_local_hit~emask=0x30", REGNO_ANY, STXT("L3 D-cache Speculative Misses"),PRELOADS_5,0, ABST_NONE, STXT("Loads that speculatively missed local L3")}, // T5/M6 encoding (430 lm, 690 rm) ~5 misses overlap on t5/pico_ile + {"lmh_spec", "DC_miss_local_hit", REGNO_ANY, STXT ("Local Mem Speculative Hits"), PRELOADS_5, 0, ABST_NONE}, + {"rmh_spec", "DC_miss_remote_L3_hit", REGNO_ANY, STXT ("Remote Mem Speculative Hits"), PRELOADS_5, 0, ABST_NONE}, + // + {"dtlbm", "DTLB_miss_asynch", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_55, 0, ABST_NONE}, // 10@l1 hit, 24@l2 hit, 60@l3 hit, 500@l3 miss, 5000@trap 0.001 events/cycle + {"dtlb_hwtw_stalls", "DTLB_HWTW_all", REGNO_ANY, STXT ("DTLB HWTW Est Stalls"), PRELOADS_55, 25, ABST_NONE, STXT ("Estimated time stalled on a DTLB miss requiring a HW tablewalk")}, // l2-20, l3-50 + {"dtlb_trap_stalls", "DTLB_fill_trap", REGNO_ANY, STXT ("DTLB Trap Est Stalls"), PRELOADS_35, 5000, ABST_NONE, STXT ("Estimated time stalled on a DTLB miss with HW tablewalk unsuccessful")}, // 5000@trap + {"rawhaz", "RAW_hit_st_q~emask=0xf", REGNO_ANY, STXT ("Read-after-write Hazards"), PRELOADS_55, 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write hazards)")}, + {"br_msp_stalls", "Br_mispred", REGNO_ANY, STXT ("Branch Mispredict Stalls"), PRELOADS_6, 24, ABST_NONE, STXT ("Estimated time stalled on Branch mispredictions")}, // 24@miss, %5 of branches is bad + {"br_msp", "Br_mispred", REGNO_ANY, STXT ("Branch Mispredict"), PRELOADS_6, 0, ABST_NONE}, // 24@miss, %5 of branches is bad + {"br_tkn", "Br_taken", REGNO_ANY, STXT ("Branch Taken"), PRELOADS_7, 0, ABST_NONE}, // 2 cycles minimum + {"br_ins", "Branches", REGNO_ANY, STXT ("Branch Instructions"), PRELOADS_7, 0, ABST_NONE}, // 24@miss, %5 of branches is bad + {"fgu", "Instr_FGU_crypto", REGNO_ANY, STXT ("FP/VIS/Crypto Instructions"), PRELOADS_7, 0, ABST_NONE}, // 1 cycle/event + + /* explicit definitions of (hidden) entries for proper counters */ + /* Counters that can be time converted, support memspace, or have a short_desc need to be in this table */ + + {"Sel_pipe_drain_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select waiting with correct instructions when pipeline has to drain after branch misprediction")}, + {"Sel_0_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select waiting for various conditions to be resolved")}, + {"Sel_0_ready", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread was ready to have its instructions selected but another hardware thread was selected instead")}, + {"Sel_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles that only 1 instruction or uop was selected")}, + {"Sel_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles that 2 instructions or uops were selected")}, + + {"Pick_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"Branches", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Control transfer instructions completed, excluding trap-related transfers")}, + {"Instr_FGU_crypto", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("FP and VIS instructions completed by the Floating Point and Graphics Unit")}, + {"Instr_ld", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Load instructions completed")}, + {"Instr_st", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Store instructions completed")}, + {"SPR_ring_ops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Specialized instructions that require internal use of SPR ring completed")}, + {"Instr_other", NULL, REGNO_ANY, NULL, PRELOAD (2, 4), 0, ABST_NONE, STXT ("Basic arithmetic and logical instructions completed")}, + {"Instr_all", NULL, REGNO_ANY, NULL, PRELOAD (1, 4), 0, ABST_NONE, STXT ("Total instructions completed")}, + + {"Br_taken", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Branch instructions taken and completed")}, + {"Sw_count_intr", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("SW Count instructions completed")}, + {"Atomics", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_EXACT, STXT ("Atomic instructions, including CASA/XA, completed")}, + {"SW_prefetch", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("PREFETCH and PREFETCHA instructions completed")}, + {"Block_ld_st", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_EXACT, STXT ("Block load/store instructions completed")}, + + {"BTC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Branches delayed a few extra cycles because branch target not found in Branch Target Cache")}, + + {"ITLB_fill_8KB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 8K page")}, + {"ITLB_fill_64KB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 64K page")}, + {"ITLB_fill_4MB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 4M page")}, + {"ITLB_fill_256MB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 256M page")}, + {"ITLB_fill_2GB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 2G or 16G page")}, + {"ITLB_fill_trap", NULL, REGNO_ANY, NULL, PRELOAD (1000, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk unsuccessful")}, + {"ITLB_miss_asynch", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk search done")}, + + {"Fetch_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_0_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"Instr_buffer_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"PQ_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_LB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"SB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_SB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LB_SB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_LB_SB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"DTLB_miss_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"ITLB_HWTW_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk hit local L2D")}, + {"ITLB_HWTW_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (80, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk hit local L3 or neighbor L2D")}, + {"ITLB_HWTW_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk missed all local caches")}, + {"DTLB_HWTW_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk hit local L2D")}, + {"DTLB_HWTW_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (80, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk hit local L3 or neighbor L2D")}, + {"DTLB_HWTW_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk missed all local caches")}, + {"DTLB_HWTW_all", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss requiring HW tablewalk")}, + + {"DC_miss_L2_L3_hit_nospec", NULL, REGNO_ANY, NULL, PRELOAD (25, 4), 0, ABST_EXACT}, + {"DC_miss_local_hit_nospec", NULL, REGNO_ANY, NULL, PRELOAD (500, 4), 0, ABST_EXACT}, + {"DC_miss_remote_L3_hit_nospec", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_EXACT}, + {"DC_miss_nospec", NULL, REGNO_ANY, NULL, PRELOAD (25, 4), 0, ABST_EXACT, STXT ("Loads that missed local L1D")}, + + {"DTLB_fill_8KB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 8K page")}, + {"DTLB_fill_64KB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 64K page")}, + {"DTLB_fill_4MB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 4M page")}, + {"DTLB_fill_256MB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 256M page")}, + {"DTLB_fill_2GB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 2G or 16G page")}, + {"DTLB_fill_trap", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk unsuccessful")}, + {"DTLB_miss_asynch", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk search done")}, + {"RAW_hit_st_buf", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write) still in store buffer not yet committed")}, + {"RAW_hit_st_q", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write) committed but in store queue not yet written to L2D")}, + + {"St_q_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"St_hit_L2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in local L2D")}, + {"St_hit_L3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in local L3")}, + + {"DC_miss_L2_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit local L2D or L3")}, + {"DC_miss_local_hit", NULL, REGNO_ANY, NULL, PRELOAD (500, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit local memory")}, + {"DC_miss_remote_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit remote cache or remote memory")}, + {"DC_miss", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_NONE, STXT ("Loads that speculatively missed L1D")}, + + {"L2_pipe_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"Br_dir_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Branch instructions completed whose direction was mispredicted")}, + {"Br_trg_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Branch instructions completed whose target was mispredicted")}, + {"Br_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Branch instructions completed whose direction or target was mispredicted")}, + + {"Cycles_user", NULL, REGNO_ANY, NULL, PRELOAD (1, 4), 1, ABST_NONE, STXT ("Cycles hardware thread is active in specified mode(s)")}, + // + {"Commit_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles no uop commits from this hardware thread")}, + {"Commit_0_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles no uop commits from any hardware thread on this core")}, + {"Commit_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles 1 uop commits from this hardware thread")}, + {"Commit_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles 2 uops commit from this hardware thread")}, + {"Commit_1_or_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles 1 or 2 uops commit from this hardware thread")}, + + /* additional (hidden) aliases, for convenience */ + {"cycles0", "Cycles_user", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "Cycles_user", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "Instr_all", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "Instr_all", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry sparc_t5_m6[] = { + // Identical to sparc_t4 except for: l3m_spec + // when updating this table, also update sparc_t4[] + // obsolete aliases marked with REGNO_INVALID (allows reading of older experiments) + {"l2l3dh", "DC_miss_L2_L3_hit_nospec", REGNO_INVALID, STXT ("L2 or L3 D-cache Hits"), PRELOADS_6, 0, ABST_EXACT}, // undercounts due to thread-hog issue + {"l3m", "DC_miss_remote_L3_hit_nospec~emask=0x6", REGNO_INVALID, STXT ("L3 D-cache Misses"), PRELOADS_5, 0, ABST_EXACT}, // undercounts due to thread-hog issue + {"lmh", "DC_miss_local_hit_nospec", REGNO_INVALID, STXT ("Local Mem. Hits"), PRELOADS_5, 0, ABST_EXACT}, // undercounts due to thread-hog issue + {"rmh", "DC_miss_remote_L3_hit_nospec", REGNO_INVALID, STXT ("Remote Mem. Hits"), PRELOADS_5, 0, ABST_EXACT}, // undercounts due to thread-hog issue + {"pqs", "PQ_tag_wait", REGNO_INVALID, STXT ("Pick Queue Stalls"), PRELOADS_7, 1, ABST_NONE}, // old alias name + {"raw_stb", "RAW_hit_st_buf", REGNO_INVALID, STXT ("RAW Hazard in Store Buffer"), PRELOADS_55, 0, ABST_NONE}, // 11@full hit, 60@partial hit (in future, combine w/st_q) + {"raw_stq", "RAW_hit_st_q", REGNO_INVALID, STXT ("RAW Hazard in Store Queue"), PRELOADS_55, 0, ABST_NONE}, // 11@full hit, 60@partial hit (in future, combine w/st_buf) + {"sel_stalls", "Sel_0_ready", REGNO_INVALID, STXT ("Stalls Another Thread Selected"), PRELOADS_7, 1, ABST_NONE}, + {"icm", "IC_miss", REGNO_INVALID, STXT ("L1 I-Cache Misses"), PRELOADS_55, 0, ABST_NONE}, // 20@ l2/l3 hit (guess) + {"icm_stalls", "IC_miss", REGNO_INVALID, STXT ("L1 I-Cache Miss Est Stalls"), PRELOADS_55, 25, ABST_NONE}, // 25@ l2-20/l3-50 + + // current aliases + SPARC_CYCLES + {"cycles", "Cycles_user", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "Instr_all", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"c_stalls", "Commit_0", REGNO_ANY, STXT ("Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + + {"loads", "Instr_ld", REGNO_ANY, STXT ("Load Instructions"), PRELOADS_7, 0, ABST_EXACT}, + {"stores", "Instr_st", REGNO_ANY, STXT ("Store Instructions"), PRELOADS_7, 0, ABST_EXACT}, + {"dcm", "DC_miss_nospec", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_EXACT}, + // {"l3m_spec", "DC_miss_local_hit~emask=0x6", REGNO_ANY, STXT("L3 D-cache Speculative Misses"),PRELOADS_5,0, ABST_NONE, STXT("Loads that speculatively missed local L3")}, // T4 encoding (430 lm, 690 rm) ~5 misses overlap on t5/pico_ile + {"l3m_spec", "DC_miss_local_hit~emask=0x30", REGNO_ANY, STXT ("L3 D-cache Speculative Misses"), PRELOADS_5, 0, ABST_NONE, STXT ("Loads that speculatively missed local L3")}, // T5/M6 encoding (430 lm, 690 rm) ~5 misses overlap on t5/pico_ile + {"lmh_spec", "DC_miss_local_hit", REGNO_ANY, STXT ("Local Mem Speculative Hits"), PRELOADS_5, 0, ABST_NONE}, + {"rmh_spec", "DC_miss_remote_L3_hit", REGNO_ANY, STXT ("Remote Mem Speculative Hits"), PRELOADS_5, 0, ABST_NONE}, + // + {"dtlbm", "DTLB_miss_asynch", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_55, 0, ABST_NONE}, // 10@l1 hit, 24@l2 hit, 60@l3 hit, 500@l3 miss, 5000@trap 0.001 events/cycle + {"dtlb_hwtw_stalls", "DTLB_HWTW_all", REGNO_ANY, STXT ("DTLB HWTW Est Stalls"), PRELOADS_55, 25, ABST_NONE, STXT ("Estimated time stalled on a DTLB miss requiring a HW tablewalk")}, // l2-20, l3-50 + {"dtlb_trap_stalls", "DTLB_fill_trap", REGNO_ANY, STXT ("DTLB Trap Est Stalls"), PRELOADS_35, 5000, ABST_NONE, STXT ("Estimated time stalled on a DTLB miss with HW tablewalk unsuccessful")}, // 5000@trap + {"rawhaz", "RAW_hit_st_q~emask=0xf", REGNO_ANY, STXT ("Read-after-write Hazards"), PRELOADS_55, 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write hazards)")}, + {"br_msp_stalls", "Br_mispred", REGNO_ANY, STXT ("Branch Mispredict Stalls"), PRELOADS_6, 24, ABST_NONE, STXT ("Estimated time stalled on Branch mispredictions")}, // 24@miss, %5 of branches is bad + {"br_msp", "Br_mispred", REGNO_ANY, STXT ("Branch Mispredict"), PRELOADS_6, 0, ABST_NONE}, // 24@miss, %5 of branches is bad + {"br_tkn", "Br_taken", REGNO_ANY, STXT ("Branch Taken"), PRELOADS_7, 0, ABST_NONE}, // 2 cycles minimum + {"br_ins", "Branches", REGNO_ANY, STXT ("Branch Instructions"), PRELOADS_7, 0, ABST_NONE}, // 24@miss, %5 of branches is bad + {"fgu", "Instr_FGU_crypto", REGNO_ANY, STXT ("FP/VIS/Crypto Instructions"), PRELOADS_7, 0, ABST_NONE}, // 1 cycle/event + + /* explicit definitions of (hidden) entries for proper counters */ + /* Counters that can be time converted, support memspace, or have a short_desc need to be in this table */ + + {"Sel_pipe_drain_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select waiting with correct instructions when pipeline has to drain after branch misprediction")}, + {"Sel_0_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select waiting for various conditions to be resolved")}, + {"Sel_0_ready", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread was ready to have its instructions selected but another hardware thread was selected instead")}, + {"Sel_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles that only 1 instruction or uop was selected")}, + {"Sel_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles that 2 instructions or uops were selected")}, + + {"Pick_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"Branches", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Control transfer instructions completed, excluding trap-related transfers")}, + {"Instr_FGU_crypto", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("FP and VIS instructions completed by the Floating Point and Graphics Unit")}, + {"Instr_ld", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Load instructions completed")}, + {"Instr_st", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Store instructions completed")}, + {"SPR_ring_ops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Specialized instructions that require internal use of SPR ring completed")}, + {"Instr_other", NULL, REGNO_ANY, NULL, PRELOAD (2, 4), 0, ABST_NONE, STXT ("Basic arithmetic and logical instructions completed")}, + {"Instr_all", NULL, REGNO_ANY, NULL, PRELOAD (1, 4), 0, ABST_NONE, STXT ("Total instructions completed")}, + + {"Br_taken", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Branch instructions taken and completed")}, + {"Sw_count_intr", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("SW Count instructions completed")}, + {"Atomics", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_EXACT, STXT ("Atomic instructions, including CASA/XA, completed")}, + {"SW_prefetch", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("PREFETCH and PREFETCHA instructions completed")}, + {"Block_ld_st", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_EXACT, STXT ("Block load/store instructions completed")}, + + {"BTC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Branches delayed a few extra cycles because branch target not found in Branch Target Cache")}, + + {"ITLB_fill_8KB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 8K page")}, + {"ITLB_fill_64KB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 64K page")}, + {"ITLB_fill_4MB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 4M page")}, + {"ITLB_fill_256MB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 256M page")}, + {"ITLB_fill_2GB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 2G or 16G page")}, + {"ITLB_fill_trap", NULL, REGNO_ANY, NULL, PRELOAD (1000, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk unsuccessful")}, + {"ITLB_miss_asynch", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk search done")}, + + {"Fetch_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_0_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"Instr_buffer_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"PQ_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_LB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"SB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_SB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LB_SB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_LB_SB_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"DTLB_miss_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"ITLB_HWTW_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk hit local L2D")}, + {"ITLB_HWTW_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (80, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk hit local L3 or neighbor L2D")}, + {"ITLB_HWTW_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk missed all local caches")}, + {"DTLB_HWTW_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk hit local L2D")}, + {"DTLB_HWTW_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (80, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk hit local L3 or neighbor L2D")}, + {"DTLB_HWTW_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk missed all local caches")}, + {"DTLB_HWTW_all", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss requiring HW tablewalk")}, + + {"DC_miss_L2_L3_hit_nospec", NULL, REGNO_ANY, NULL, PRELOAD (25, 4), 0, ABST_EXACT}, + {"DC_miss_local_hit_nospec", NULL, REGNO_ANY, NULL, PRELOAD (500, 4), 0, ABST_EXACT}, + {"DC_miss_remote_L3_hit_nospec", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_EXACT}, + {"DC_miss_nospec", NULL, REGNO_ANY, NULL, PRELOAD (25, 4), 0, ABST_EXACT, STXT ("Loads that missed local L1D")}, + + {"DTLB_fill_8KB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 8K page")}, + {"DTLB_fill_64KB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 64K page")}, + {"DTLB_fill_4MB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 4M page")}, + {"DTLB_fill_256MB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 256M page")}, + {"DTLB_fill_2GB", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 2G or 16G page")}, + {"DTLB_fill_trap", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk unsuccessful")}, + {"DTLB_miss_asynch", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk search done")}, + {"RAW_hit_st_buf", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write) still in store buffer not yet committed")}, + {"RAW_hit_st_q", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write) committed but in store queue not yet written to L2D")}, + + {"St_q_tag_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"St_hit_L2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in local L2D")}, + {"St_hit_L3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in local L3")}, + + {"DC_miss_L2_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit local L2D or L3")}, + {"DC_miss_local_hit", NULL, REGNO_ANY, NULL, PRELOAD (500, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit local memory")}, + {"DC_miss_remote_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit remote cache or remote memory")}, + {"DC_miss", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_NONE, STXT ("Loads that speculatively missed L1D")}, + + {"L2_pipe_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"Br_dir_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Branch instructions completed whose direction was mispredicted")}, + {"Br_trg_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Branch instructions completed whose target was mispredicted")}, + {"Br_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Branch instructions completed whose direction or target was mispredicted")}, + + {"Cycles_user", NULL, REGNO_ANY, NULL, PRELOAD (1, 4), 1, ABST_NONE, STXT ("Cycles hardware thread is active in specified mode(s)")}, + // + {"Commit_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles no uop commits from this hardware thread")}, + {"Commit_0_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles no uop commits from any hardware thread on this core")}, + {"Commit_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles 1 uop commits from this hardware thread")}, + {"Commit_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles 2 uops commit from this hardware thread")}, + {"Commit_1_or_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles 1 or 2 uops commit from this hardware thread")}, + + /* additional (hidden) aliases, for convenience */ + {"cycles0", "Cycles_user", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "Cycles_user", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "Instr_all", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "Instr_all", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry sparc_m7[] = { + // obsolete aliases marked with REGNO_INVALID (allows reading of older experiments) + {"icm", "IC_miss_commit", REGNO_INVALID, STXT ("L1 I-Cache Misses"), PRELOADS_6, 0, ABST_EXACT}, + {"raw_stb", "RAW_hit_st_buf", REGNO_INVALID, STXT ("RAW Hazard in Store Buffer"), PRELOADS_55, 0, ABST_NONE}, + {"raw_stq", "RAW_hit_st_q", REGNO_INVALID, STXT ("RAW Hazard in Store Queue"), PRELOADS_55, 0, ABST_NONE}, + {"pqs", "PQ_tag_wait_cyc", REGNO_INVALID, STXT ("Pick Queue Stalls"), PRELOADS_7, 1, ABST_NONE}, + {"sel_stalls", "Sel_0_ready_cyc", REGNO_INVALID, STXT ("Stalls Another Thread Selected"), PRELOADS_7, 1, ABST_NONE}, + + // current aliases + SPARC_CYCLES + {"cycles", "Cycles_user", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "Instr_all", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"c_stalls", "Commit_0_cyc", REGNO_ANY, STXT ("Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + + {"loads", "Instr_ld", REGNO_ANY, STXT ("Load Instructions"), PRELOADS_7, 0, ABST_EXACT}, + {"stores", "Instr_st", REGNO_ANY, STXT ("Store Instructions"), PRELOADS_6, 0, ABST_EXACT}, + {"dcm", "DC_miss_commit", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_6, 0, ABST_EXACT}, + + {"l3m_spec", "DC_miss_L3_miss", REGNO_ANY, STXT ("L3 D-cache Speculative Misses"), PRELOADS_5, 0, ABST_NONE}, + {"lmh_spec", "DC_miss_local_mem_hit", REGNO_ANY, STXT ("Local Mem Speculative Hits"), PRELOADS_5, 0, ABST_NONE}, + {"rmh_spec", "DC_miss_remote_mem_hit", REGNO_ANY, STXT ("Remote Mem Speculative Hits"), PRELOADS_5, 0, ABST_NONE}, + // + {"dtlbm", "DTLB_HWTW_search", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_55, 0, ABST_NONE}, // 10@l1 hit, 24@l2 hit, 60@l3 hit, 500@l3 miss, 5000@trap 0.001 events/cycle + {"dtlb_hwtw_stalls", "DTLB_HWTW_ref", REGNO_ANY, STXT ("DTLB HWTW Est Stalls"), PRELOADS_55, 25, ABST_NONE, STXT ("Estimated time stalled on a DTLB miss requiring a HW tablewalk")}, // l2-20, l3-50 + {"dtlb_trap_stalls", "DTLB_HWTW_miss_trap", REGNO_ANY, STXT ("DTLB Trap Est Stalls"), PRELOADS_35, 5000, ABST_NONE, STXT ("Estimated time stalled on a DTLB miss with HW tablewalk unsuccessful")}, // 5000@trap + {"rawhaz", "RAW_hit_st_q~emask=0xf", REGNO_ANY, STXT ("Read-after-write Hazards"), PRELOADS_55, 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write hazards)")}, + {"br_msp_stalls", "Br_mispred", REGNO_ANY, STXT ("Branch Mispredict Stalls"), PRELOADS_6, 24, ABST_NONE, STXT ("Estimated time stalled on Branch mispredictions")}, // 24@miss, %5 of branches is bad + {"br_msp", "Br_mispred", REGNO_ANY, STXT ("Branch Mispredict"), PRELOADS_6, 0, ABST_NONE}, + {"br_tkn", "Br_taken", REGNO_ANY, STXT ("Branch Taken"), PRELOADS_7, 0, ABST_NONE}, + {"br_ins", "Branches", REGNO_ANY, STXT ("Branch Instructions"), PRELOADS_7, 0, ABST_NONE}, + {"fgu", "Instr_FGU_crypto", REGNO_ANY, STXT ("FP/VIS/Crypto Instructions"), PRELOADS_7, 0, ABST_NONE}, + {"spill_fill", "Flush_arch_exception", REGNO_ANY, STXT ("Reg Window Spill/Fill Est Stalls"), PRELOAD (100, 4), 80, ABST_NONE, STXT ("Estimated time stalled on flushing pipeline due to register window spill/fill")}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Counters that can be time converted, support memspace, or have a short_desc need to be in this table */ + {"Sel_pipe_drain_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select waiting with correct instructions when pipeline has to drain after branch misprediction")}, + {"Sel_0_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select waiting for various conditions to be resolved")}, + {"Sel_0_ready_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread was ready to have its instructions selected but another hardware thread was selected instead")}, + {"Sel_1_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles that only 1 instruction or uop was selected")}, + {"Sel_2_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles that 2 instructions or uops were selected")}, + + {"Pick_0_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_1_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_2_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_3_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Pick_any_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"Branches", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Control transfer instructions completed, excluding trap-related transfers")}, + {"Instr_FGU_crypto", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("FP and VIS instructions completed by the Floating Point and Graphics Unit")}, + {"Instr_ld", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Load instructions completed")}, + {"Instr_st", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Store instructions completed")}, + {"Instr_SPR_ring_ops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Specialized instructions that require internal use of SPR ring completed")}, + {"Instr_other", NULL, REGNO_ANY, NULL, PRELOAD (2, 4), 0, ABST_NONE, STXT ("Basic arithmetic and logical instructions completed")}, + {"Instr_all", NULL, REGNO_ANY, NULL, PRELOAD (1, 4), 0, ABST_NONE, STXT ("Total instructions completed")}, + + {"Br_taken", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Branch instructions taken and completed")}, + {"Instr_SW_count", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("SW Count instructions completed")}, + {"Instr_atomic", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_EXACT, STXT ("Atomic instructions, including CASA/XA, completed")}, + {"Instr_SW_prefetch", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("PREFETCH and PREFETCHA instructions completed")}, + {"Instr_block_ld_st", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_EXACT, STXT ("Block load/store instructions completed")}, + + {"Br_BTC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Branches delayed a few extra cycles because branch target not found in Branch Target Cache")}, + + {"ITLB_HWTW_hit_8K", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 8K page")}, + {"ITLB_HWTW_hit_64K", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 64K page")}, + {"ITLB_HWTW_hit_4M", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 4M page")}, + {"ITLB_HWTW_hit_256M", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 256M page")}, + {"ITLB_HWTW_hit_2G_16G", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 2G or 16G page")}, + {"ITLB_HWTW_miss_trap", NULL, REGNO_ANY, NULL, PRELOAD (1000, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk unsuccessful")}, + {"ITLB_HWTW_search", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk search done")}, + + {"Fetch_0_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_0_all_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"Instr_buffer_full_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"PQ_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LB_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"SB_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_LB_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_SB_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LB_SB_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"ROB_LB_SB_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"DTLB_miss_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"ITLB_HWTW_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk hit local L2D")}, + {"ITLB_HWTW_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (80, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk hit local L3 or neighbor L2D")}, + {"ITLB_HWTW_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk missed all local caches")}, + {"DTLB_HWTW_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk hit local L2D")}, + {"DTLB_HWTW_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (80, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk hit local L3 or neighbor L2D")}, + {"DTLB_HWTW_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk missed all local caches")}, + {"DTLB_HWTW_ref", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss requiring HW tablewalk")}, + + {"DC_miss_L2_L3_hit_commit", NULL, REGNO_ANY, NULL, PRELOAD (25, 4), 0, ABST_EXACT}, + {"DC_miss_nbr_scc_hit_commit", NULL, REGNO_ANY, NULL, PRELOAD (500, 4), 0, ABST_EXACT}, + {"DC_miss_nbr_scc_miss_commit", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_EXACT}, + {"DC_miss_commit", NULL, REGNO_ANY, NULL, PRELOAD (25, 4), 0, ABST_EXACT, STXT ("Loads that missed local L1D")}, + + {"DTLB_HWTW_hit_8K", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 8K page")}, + {"DTLB_HWTW_hit_64K", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 64K page")}, + {"DTLB_HWTW_hit_4M", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 4M page")}, + {"DTLB_HWTW_hit_256M", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 256M page")}, + {"DTLB_HWTW_hit_2G_16G", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 2G or 16G page")}, + {"DTLB_HWTW_miss_trap", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk unsuccessful")}, + {"DTLB_HWTW_search", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk search done")}, + {"RAW_hit_st_buf", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write) still in store buffer not yet committed")}, + {"RAW_hit_st_q", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write) committed but in store queue not yet written to L2D")}, + + {"St_q_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"St_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in local L2D")}, + {"St_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in local L3")}, + + {"DC_hit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Loads that speculatively hit local L1D")}, + {"DC_miss_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (20, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit local L2D")}, + {"DC_miss_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit local L3")}, + {"DC_miss_nbr_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (100, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit neighbor L2D via local L3")}, + {"DC_miss_nbr_scc_hit", NULL, REGNO_ANY, NULL, PRELOAD (100, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit neighbor L3 on same socket")}, + {"DC_miss_nbr_scc_miss", NULL, REGNO_ANY, NULL, PRELOAD (400, 4), 0, ABST_NONE, STXT ("Loads that speculatively missed all caches on same socket")}, + {"DC_miss", NULL, REGNO_ANY, NULL, PRELOAD (10, 4), 0, ABST_NONE, STXT ("Loads that speculatively missed local L1D")}, + {"DC_miss_L2_miss", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Loads that speculatively missed local L2D")}, + {"DC_miss_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (200, 4), 0, ABST_NONE, STXT ("Loads that speculatively missed local L3")}, + + {"DC_miss_remote_scc_hit", NULL, REGNO_ANY, NULL, PRELOAD (800, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit remote cache on different socket")}, + {"DC_miss_local_mem_hit", NULL, REGNO_ANY, NULL, PRELOAD (500, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit local memory")}, + {"DC_miss_remote_mem_hit", NULL, REGNO_ANY, NULL, PRELOAD (1000, 4), 0, ABST_NONE, STXT ("Loads that speculatively hit remote memory")}, + {"Br_dir_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Branch instructions completed whose direction was mispredicted")}, + {"Br_tgt_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Branch instructions completed whose target was mispredicted")}, + {"Br_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Branch instructions completed whose direction or target was mispredicted")}, + + {"Cycles_user", NULL, REGNO_ANY, NULL, PRELOAD (1, 4), 1, ABST_NONE, STXT ("Cycles hardware thread is active in specified mode(s)")}, + + {"Flush_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Pipeline flushes due to a load that misses L3 when more than 1 hardware thread is active on the core")}, + {"Flush_br_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Pipeline flushes due to a branch misprediction")}, + {"Flush_arch_exception", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Pipeline flushes due to SPARC architecture exceptions and trap entry/return")}, + {"Flush_other", NULL, REGNO_ANY, NULL, PRELOAD (40, 4), 0, ABST_NONE, STXT ("Pipeline flushes due to hardware thread state change to/from halted/paused state")}, + // + {"Commit_0_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles no uop commits from this hardware thread")}, + {"Commit_0_all_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles no uop commits from any hardware thread on this core")}, + {"Commit_1_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles 1 uop commits from this hardware thread")}, + {"Commit_2_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles 2 uops commit from this hardware thread")}, + {"Commit_1_or_2_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles 1 or 2 uops commit from this hardware thread")}, + + + /* additional (hidden) aliases, for convenience */ + {"cycles0", "Cycles_user", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "Cycles_user", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "Instr_all", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "Instr_all", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry sparc_m8[] = { + // current aliases + SPARC_CYCLES + {"cycles", "Cycles_user", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "Instr_all", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"c_stalls", "Commit_0_cyc", 3, STXT ("Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, // 22825776: limit to reg 3 + {"Sel_0_wait_cyc", "Sel_0_cyc~emask=0x3f", REGNO_ANY, STXT ("Select Stall Cycles"), PRELOADS_7, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select waiting for various conditions to be resolved that prevent it being selected")}, + + {"loads", "Instr_ld", REGNO_ANY, STXT ("Load Instructions"), PRELOADS_7, 0, ABST_EXACT}, + {"stores", "Instr_st", REGNO_ANY, STXT ("Store Instructions"), PRELOADS_6, 0, ABST_EXACT}, + {"dcm", "DC_miss_commit", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_6, 0, ABST_EXACT}, + + {"lmh_spec", "DC_miss_local_mem_hit", REGNO_ANY, STXT ("Local Mem Speculative Hits"), PRELOADS_5, 0, ABST_NONE}, + {"rmh_spec", "DC_miss_remote_mem_hit", REGNO_ANY, STXT ("Remote Mem Speculative Hits"), PRELOADS_5, 0, ABST_NONE}, + + {"dtlbm", "DTLB_HWTW", REGNO_ANY, STXT ("DTLB Misses"), PRELOAD (40, 5), 0, ABST_NONE}, // 10@l1 hit, 24@l2 hit, 60@l3 hit, 500@l3 miss, 5000@trap 0.001 events/cycle + {"dtlb_hwtw_stalls", "DTLB_HWTW", REGNO_ANY, STXT ("DTLB HWTW Est Stalls"), PRELOAD (40, 5), 25, ABST_NONE, STXT ("Estimated time stalled on a DTLB miss requiring a HW tablewalk")}, // l2-20, l3-50 + {"dtlb_trap_stalls", "DTLB_HWTW_miss_trap", REGNO_ANY, STXT ("DTLB Trap Est Stalls"), PRELOAD (800, 5), 5000, ABST_NONE, STXT ("Estimated time stalled on a DTLB miss with HW tablewalk unsuccessful")}, // 5000@trap + {"rawhaz", "RAW_hit", REGNO_ANY, STXT ("Read-after-write Hazards"), PRELOAD (40, 5), 0, ABST_NONE}, + {"br_msp_stalls", "Br_mispred", REGNO_ANY, STXT ("Branch Mispredict Stalls"), PRELOAD (40, 5), 24, ABST_NONE, STXT ("Estimated time stalled on Branch mispredictions")}, // 24@miss, %5 of branches is bad + {"br_msp", "Br_mispred", REGNO_ANY, STXT ("Branch Mispredict"), PRELOAD (40, 5), 0, ABST_NONE}, + {"br_tkn", "Br_taken", REGNO_ANY, STXT ("Branch Taken"), PRELOADS_7, 0, ABST_NONE}, + {"br_ins", "Branches", REGNO_ANY, STXT ("Branch Instructions"), PRELOADS_7, 0, ABST_NONE}, + {"fgu", "Instr_FGU_crypto", REGNO_ANY, STXT ("FP/VIS/Crypto Instructions"), PRELOADS_7, 0, ABST_NONE}, + {"spill_fill", "Flush_spill_fill", REGNO_ANY, STXT ("Reg Window Spill/Fill Est Stalls"), PRELOAD (100, 5), 80, ABST_NONE, STXT ("Estimated time stalled on flushing pipeline due to register window spill/fill")}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Counters that can be time converted, support memspace, or have a short_desc need to be in this table */ + //0x01 + {"Fetch_stall_IFU_reset_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_stall_IC_miss_MB_full_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_stall_IC_miss_MB_avail_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_stall_IC_miss_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_stall_ITLB_miss_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_stall_SEL_buf_full_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_ready_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_0_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_0_all_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + //0x02 + {"Fetch_1_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_2_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_3_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_4_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_5_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_6_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_7_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_8_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + //0x07 + {"ITLB_HWTW_hit_8K", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 8K page")}, + {"ITLB_HWTW_hit_64K", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 64K page")}, + {"ITLB_HWTW_hit_4M", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 4M page")}, + {"ITLB_HWTW_hit_256M", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 256M page")}, + {"ITLB_HWTW_hit_16G", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 16G page")}, + {"ITLB_HWTW_hit_1T", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk successfully loaded translation for 1T page")}, + // { "ITLB_HWTW_miss_RA2PAC", 0x0740, 0xf07ff }, + // { "ITLB_HWTW_miss_not_RA2PAC", 0x0780, 0xf07ff }, + {"ITLB_HWTW_miss_trap", NULL, REGNO_ANY, NULL, PRELOAD (1000, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk unsuccessful")}, + {"ITLB_HWTW", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk search done")}, + //0x08 + {"Br_BTC_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Branches delayed a few extra cycles because branch target not found in Branch Target Cache")}, + //0x09 + {"Sel_0_no_instr_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select because no instructions are available")}, + {"Sel_0_pipe_drain_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select waiting with correct instructions when pipeline has to drain after branch misprediction")}, + {"Sel_0_postsync_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select waiting for prior instructions to commit")}, + {"Sel_0_presync_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select with instruction that cannot decode until prior instructions have committed")}, + {"Sel_0_thread_hog_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select to prevent strand monopolizing resources")}, + {"Sel_0_tag_stall_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread stalls at Select because no required tags are available")}, + {"Sel_0_ready_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread was ready to have its instructions selected but another hardware thread was selected instead")}, + {"Sel_0_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles a hardware thread is not selected")}, + // No direct equivalent Sel_1/2_cyc. Nearest is Decode_uop, which increments by 0-4 each cycle according to how many uops were decoded. + //0x13 + {"ITLB_HWTW_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk hit local L2D")}, + {"ITLB_HWTW_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (80, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk hit local L3 or neighbor L2D")}, + {"ITLB_HWTW_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (800, 5), 0, ABST_NONE, STXT ("ITLB miss and HW tablewalk missed all local caches")}, + {"DTLB_HWTW_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk hit local L2D")}, + {"DTLB_HWTW_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (80, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk hit local L3 or neighbor L2D")}, + {"DTLB_HWTW_L3_miss", NULL, REGNO_ANY, NULL, PRELOAD (800, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk missed all local caches")}, + {"DTLB_HWTW_ref", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("DTLB miss requiring HW tablewalk")}, + //0x0E + {"Instr_FGU_crypto", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("FP and VIS instructions completed by the Floating Point and Graphics Unit")}, + {"Instr_ld", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Load instructions completed")}, + {"Instr_st", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Store instructions completed")}, + {"Instr_block_ld_st", NULL, REGNO_ANY, NULL, PRELOAD (20, 5), 0, ABST_EXACT, STXT ("Block load/store instructions completed")}, + {"Instr_SPR_ring_ops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("Specialized instructions that require internal use of SPR ring completed")}, + {"Instr_atomic", NULL, REGNO_ANY, NULL, PRELOAD (20, 5), 0, ABST_EXACT, STXT ("Atomic instructions, including CASA/XA, completed")}, + {"Instr_SW_prefetch", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT, STXT ("PREFETCH and PREFETCHA instructions completed")}, + {"Instr_other", NULL, REGNO_ANY, NULL, PRELOAD (2, 5), 0, ABST_NONE, STXT ("Basic arithmetic and logical instructions completed")}, + {"Instr_all", NULL, REGNO_ANY, NULL, PRELOAD (1, 5), 0, ABST_NONE, STXT ("Total instructions completed")}, + //0x0F + {"Branches", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Control transfer instructions completed, excluding trap-related transfers")}, + //0x10 + {"Br_taken", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Branch instructions taken and completed")}, + //0x11 + {"Rename_tag_wait_PQ_1_EXU_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Rename_tag_wait_PQ_0_LSU_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Rename_wait_crypto_diag_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Sel_0_wait_ROB_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Sel_0_wait_WRF_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Sel_0_wait_LB_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Sel_0_wait_SB_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + //0x12 + {"Fetch_stall_BDA_tag_unavail_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_stall_BTA_tag_unavail_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_stall_misc_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"Fetch_stall_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"MMU_TTE_buffer_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"MMU_PRQ_pool_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + //0x15 + {"L2I_request_block_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2I_thread_hog_stall_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2I_MB_full_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2I_snoop_eviction", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2I_stall_no_request_credit_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2I_stall_no_response_credit_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + //0x16 + {"Flush_thread_hog", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes to prevent thread from monopolizing resources")}, + {"Flush_br_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to a branch misprediction")}, + {"Flush_arch_exception", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to SPARC architecture exceptions and trap entry/return")}, + {"Flush_evil_twin", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to detecting floating point evil twin condition")}, + {"Flush_LSU_trap", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes to refetch Next-PC")}, + {"Flush_mode_change", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to strand mode change")}, + {"Flush_misalign", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to detecting misaligned load/store requiring transition to misaligned mitigation mode")}, + {"Flush_other", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to hardware thread state change to/from halted/paused state")}, + {"Flush_all", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to any reason")}, + //0x17 + {"Flush_spill_n_normal", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to spill_n_normal exception")}, + {"Flush_spill_n_other", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to spill_n_other exception")}, + {"Flush_fill_n_normal", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to fill_n_normal exception")}, + {"Flush_fill_n_other", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to fill_n_other exception")}, + {"Flush_spill_fill", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to spill/fill exceptions")}, + {"Flush_lost_load", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Pipeline flushes due to speculatively executed load violating memory order")}, + //0x21 + {"Br_dir_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Branch instructions completed whose direction was mispredicted")}, + {"Br_tgt_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Branch instructions completed whose target was mispredicted")}, + {"Br_mispred", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Branch instructions completed whose direction or target was mispredicted")}, + //0x23 + {"LSU_st_q_tag_wait_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LSU_st_q_tag_wait_all_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2D_stall_no_request_credit_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2D_stall_no_response_credit_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + //0x27 + {"DC_miss_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (20, 5), 0, ABST_NONE, STXT ("Loads that speculatively hit local L2D")}, + {"DC_miss_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Loads that speculatively hit local L3")}, + {"DC_miss_L3_dirty_copyback", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Loads that speculatively hit local L3 but require copyback from L2D within same CPC")}, + {"DC_miss_nbr_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (100, 5), 0, ABST_NONE, STXT ("Loads that speculatively hit neighbor L3 on same socket")}, + {"DC_miss_remote_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (400, 5), 0, ABST_NONE, STXT ("Loads that speculatively hit remote cache on different socket")}, + {"DC_miss_local_mem_hit", NULL, REGNO_ANY, NULL, PRELOAD (500, 5), 0, ABST_NONE, STXT ("Loads that speculatively hit local memory")}, + {"DC_miss_remote_mem_hit", NULL, REGNO_ANY, NULL, PRELOAD (1000, 5), 0, ABST_NONE, STXT ("Loads that speculatively hit remote memory")}, + {"DC_miss", NULL, REGNO_ANY, NULL, PRELOAD (10, 5), 0, ABST_NONE, STXT ("Loads that speculatively missed local L1D")}, + //0x28 + {"DC_sec_miss_L2_hit_commit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DC_miss_L2_hit_commit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DC_miss_L3_hit_commit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DC_miss_L3_dirty_copyback_commit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DC_miss_nbr_L3_hit_commit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DC_miss_remote_L3_hit_commit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DC_miss_local_mem_hit_commit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DC_miss_remote_mem_hit_commit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_EXACT}, + {"DC_miss_commit", NULL, REGNO_ANY, NULL, PRELOAD (25, 5), 0, ABST_EXACT, STXT ("Loads that missed local L1D")}, + //0x29 + // {"Store_DC_sec_miss_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT("")}, + {"Store_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (20, 5), 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in local L2D")}, + {"Store_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in local L3")}, + {"Store_nbr_L2_hit", NULL, REGNO_ANY, NULL, PRELOAD (100, 5), 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in neighbor L2 on same socket")}, + {"Store_nbr_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (100, 5), 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in neighbor L3 on same socket")}, + {"Store_remote_L3_hit", NULL, REGNO_ANY, NULL, PRELOAD (400, 5), 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in remote cache on different socket")}, + {"Store_local_mem_hit", NULL, REGNO_ANY, NULL, PRELOAD (500, 5), 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in local memory")}, + {"Store_remote_mem_hit", NULL, REGNO_ANY, NULL, PRELOAD (1000, 5), 0, ABST_NONE, STXT ("Stores whose cacheline being updated was in remote memory")}, + {"Store_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE, STXT ("Stores whose cacheline being updated was observed to be somewhere in the memory hierarchy")}, + //0x2d + {"RAW_hit_st_buf", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write) still in store buffer not yet committed")}, + {"RAW_hit_st_q", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write) committed but in store queue not yet written to L2D")}, + {"RAW_hit", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("Loads delayed by a previous store (read-after-write hazards)")}, + //0x2f + {"Cycles_user_non_MLA", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {"Cycles_user_MLA", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {"Cycles_user", NULL, REGNO_ANY, NULL, PRELOAD (1, 5), 1, ABST_NONE, STXT ("Cycles hardware thread is active in specified mode(s)")}, + //0x37 + {"DTLB_HWTW_hit_8K", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 8K page")}, + {"DTLB_HWTW_hit_64K", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 64K page")}, + {"DTLB_HWTW_hit_4M", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 4M page")}, + {"DTLB_HWTW_hit_256M", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 256M page")}, + {"DTLB_HWTW_hit_16G", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 16G page")}, + {"DTLB_HWTW_hit_1T", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk successfully loaded translation for 1T page")}, + {"DTLB_HWTW_miss_trap", NULL, REGNO_ANY, NULL, PRELOAD (800, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk unsuccessful")}, + {"DTLB_HWTW", NULL, REGNO_ANY, NULL, PRELOAD (40, 5), 0, ABST_NONE, STXT ("DTLB miss and HW tablewalk search done")}, + //0x3f + {"Commit_0_cyc", /*22825776*/ NULL, 3, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles no uop commits from this hardware thread")}, + {"Commit_0_all_cyc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE, STXT ("Cycles no uop commits from any hardware thread on this core")}, + // Similar situation to Sel_1_cyc etc. No direct equivalent, nearest is Commit_uop, which increments by 0-4 each cycle according to how many uops were committed. + + /* additional (hidden) aliases, for convenience */ + {"cycles0", "Cycles_user", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "Cycles_user", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "Instr_all", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "Instr_all", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry usfuji_V_list[] = { + {"cycles", "cycle_counts", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"insts", "instruction_counts", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, + {"flops", "floating_instructions", REGNO_ANY, STXT ("Floating-point Ops"), PRELOADS_6, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"cycle_counts", NULL, REGNO_ANY, NULL, PRELOADS_7, 1, ABST_NONE}, + {"load_store_instructions", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + + /* additional (hidden) aliases for convenience */ + {"cycles0", "cycle_counts", 0, NULL, PRELOADS_75, 1, ABST_NONE}, + {"cycles1", "cycle_counts", 1, NULL, PRELOADS_75, 1, ABST_NONE}, + {"insts0", "instruction_counts", 0, NULL, PRELOADS_75, 0, ABST_NONE}, + {"insts1", "instruction_counts", 1, NULL, PRELOADS_75, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry usfuji_VI_VII_list[] = { + {"cycles", "cycle_counts", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "instruction_counts", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"dcm", "op_r_iu_req_mi_go", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_6, 0, ABST_NONE}, + {"dcstall", "op_wait_all", REGNO_ANY, STXT ("L1 D-cache Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"dtlbm", "write_op_uTLB", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_5, 0, ABST_NONE}, + // l2m: mem_cache_load test shows undercount of 3x, however, we don't care too much about this chip, keeping the alias for now + {"l2m", "sx_miss_count_dm", REGNO_ANY, STXT ("L2 Cache Misses"), PRELOADS_5, 0, ABST_NONE}, /*YXXX undercounts?*/ + {"l2wm", "dvp_count_dm", REGNO_ANY, STXT ("L2 Cache Writeback Misses"), PRELOADS_5, 0, ABST_NONE}, + {"l2ref", "sx_read_count_dm", REGNO_ANY, STXT ("L2 Cache Refs"), PRELOADS_6, 0, ABST_NONE}, + {"l2stall", "sx_miss_wait_dm", REGNO_ANY, STXT ("L2 Cache Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"icm", "if_r_iu_req_mi_go", REGNO_ANY, STXT ("L1 I-cache Misses"), PRELOADS_6, 0, ABST_NONE}, + {"icstall", "if_wait_all", REGNO_ANY, STXT ("L1 I-cache Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"itlbm", "write_if_uTLB", REGNO_ANY, STXT ("ITLB Misses"), PRELOADS_5, 0, ABST_NONE}, + {"flops", "floating_instructions", REGNO_ANY, STXT ("Floating-point Ops"), PRELOADS_7, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"cycle_counts", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {"op_stv_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"load_store_instructions", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"active_cycle_count", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_sxmiss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"branch_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"write_op_uTLB", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"sx_miss_wait_pf", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"sx_miss_wait_dm", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_nc_pend", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_sxmiss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"eu_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"sx_miss_count_dm", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"fl_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_r_iu_req_mi_go", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"sx_miss_count_dm_if", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"op_stv_wait_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"swpf_lbs_hit", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"sx_read_count_dm", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"trap_DMMU_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"op_wait_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"sx_miss_count_dm_opex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"if_wait_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"dvp_count_dm", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + {"sx_miss_count_dm_opsh", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 0, ABST_NONE}, + + /* additional (hidden) aliases for convenience */ + {"cycles0", "cycle_counts", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "cycle_counts", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "instruction_counts", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "instruction_counts", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + + +static Hwcentry usfuji_X_list[] = { + {"cycles", "cycle_counts", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "instruction_counts", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"dcm", "L1D_miss", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, + {"dcstall", "L1D_wait_all", REGNO_ANY, STXT ("L1 D-cache Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"cycle_counts", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {"w_op_stv_wait_nc_pend", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"eu_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_pf_bank0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_pfp_busy_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_dm_bank0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_branch_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_sxmiss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_nc_pend", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_pf_bank2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_eu_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_sxmiss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_sxmiss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"branch_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_dm_bank2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"d_move_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_fl_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_pfp_busy", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"fl_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_pf_bank1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_dm_bank1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_sxmiss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_swpf", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L1D_wait_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_pf_bank3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"cse_priority_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_pfp_busy_swpf", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L1I_wait_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_dm_bank3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"single_mode_cycle_counts", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"suspend_cycle", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"sleep_cycle", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* additional (hidden) aliases for convenience */ + {"cycles0", "cycle_counts", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "cycle_counts", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "instruction_counts", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "instruction_counts", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry usfuji_XII_list[] = { + {"cycles", "cycle_counts", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "instruction_counts", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"dcm", "L1D_miss", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, + {"dcstall", "L1D_wait_all", REGNO_ANY, STXT ("L1 D-cache Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"cycle_counts", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {"L1D_wait_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L1I_wait_all", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_dm_bank0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_dm_bank1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_dm_bank2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_dm_bank3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_pf_bank0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_pf_bank1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_pf_bank2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"L2_miss_wait_pf_bank3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LL_miss_wait_dm_bank0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LL_miss_wait_dm_bank1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LL_miss_wait_dm_bank2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LL_miss_wait_dm_bank3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LL_miss_wait_pf_bank0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LL_miss_wait_pf_bank1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LL_miss_wait_pf_bank2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"LL_miss_wait_pf_bank3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"branch_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"cse_priority_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"d_move_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"eu_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"fl_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"l2_sy_miss_wait_dm_part1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"l2_sy_miss_wait_dm_part2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"msgr_reqp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"msgr_rtnp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"msgs_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_l1d_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_l1d_miss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_l2_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_l2_miss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_ll_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_ll_miss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_nc_pend", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_pfp_busy", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_pfp_busy_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_pfp_busy_swpf", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_swpf", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_sxmiss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"op_stv_wait_sxmiss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_branch_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_eu_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_fl_comp_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_l1d_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_l1d_miss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_l2_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_l2_miss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_ll_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_ll_miss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_nc_pend", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_pfp_busy", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_pfp_busy_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_pfp_busy_swpf", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_sxmiss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"w_op_stv_wait_sxmiss_ex", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {"single_mode_cycle_counts", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"suspend_cycle", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"sleep_cycle", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* additional (hidden) aliases for convenience */ + {"cycles0", "cycle_counts", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "cycle_counts", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "instruction_counts", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "instruction_counts", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +/* Kernel profiling pseudo-chip, OBSOLETE (To support 12.3 and earlier, TBR) */ +static Hwcentry kproflist[] = { + {"kcycles", "kcycles", 0, STXT ("KCPU Cycles"), PRELOADS_5, 1, ABST_NONE}, + {"kucycles", "kucycles", 0, STXT ("KUCPU Cycles"), PRELOADS_5, 1, ABST_NONE}, + {"kthr", "kthr", 0, STXT ("KTHR Cycles"), PRELOADS_5, 1, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry pentiumIIlist[] = { + /* note -- missing entries for dtlbm, ecm */ + {"cycles", "cpu_clk_unhalted", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"insts", "inst_retired", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, + {"icm", "ifu_ifetch_miss", REGNO_ANY, STXT ("I$ Misses"), PRELOADS_5, 0, ABST_NONE}, + {"dcrm", "dcu_m_lines_in", REGNO_ANY, STXT ("D$ Read Misses"), PRELOADS_5, 0, ABST_NONE}, + {"dcwm", "dcu_m_lines_out", REGNO_ANY, STXT ("D$ Write Misses"), PRELOADS_5, 0, ABST_NONE}, + {"flops", "flops", REGNO_ANY, STXT ("Floating-point Ops"), PRELOADS_7, 0, ABST_NONE}, + {"itlbm", "itlb_miss", REGNO_ANY, STXT ("ITLB Misses"), PRELOADS_5, 0, ABST_NONE}, + {"ecim", "l2_ifetch", REGNO_ANY, STXT ("E$ Instr. Misses"), PRELOADS_5, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"cpu_clk_unhalted", NULL, REGNO_ANY, NULL, PRELOADS_7, 1, ABST_NONE}, + + /* additional (hidden) aliases for convenience */ + {"cycles0", "cpu_clk_unhalted", 0, NULL, PRELOADS_75, 1, ABST_NONE}, + {"cycles1", "cpu_clk_unhalted", 1, NULL, PRELOADS_75, 1, ABST_NONE}, + {"insts0", "inst_retired", 0, NULL, PRELOADS_75, 0, ABST_NONE}, + {"insts1", "inst_retired", 1, NULL, PRELOADS_75, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry pentiumIIIlist[] = { + /* note -- many missing entries; no reference machine to try */ + {"cycles", "cpu_clk_unhalted", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"insts", "inst_retired", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"cpu_clk_unhalted", NULL, REGNO_ANY, NULL, PRELOADS_7, 1, ABST_NONE}, + + /* additional (hidden) aliases for convenience */ + {"cycles0", "cpu_clk_unhalted", 0, NULL, PRELOADS_75, 1, ABST_NONE}, + {"cycles1", "cpu_clk_unhalted", 1, NULL, PRELOADS_75, 1, ABST_NONE}, + {"insts0", "inst_retired", 0, NULL, PRELOADS_75, 0, ABST_NONE}, + {"insts1", "inst_retired", 1, NULL, PRELOADS_75, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry pentium4[] = { + {"cycles", "TC_deliver_mode~threshold=0xf~complement=1~compare=1", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"insts", "instr_retired~emask=0x3", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, + {"l1m", "BSQ_cache_reference~emask=0x0507", REGNO_ANY, STXT ("L1 Cache Misses"), PRELOADS_7, 0, ABST_NONE}, + {"l2h", "BSQ_cache_reference~emask=0x0007", REGNO_ANY, STXT ("L2 Cache Hits"), PRELOADS_7, 0, ABST_NONE}, + {"l2m", "BSQ_cache_reference~emask=0x0500", REGNO_ANY, STXT ("L2 Cache Misses"), PRELOADS_6, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"TC_deliver_mode", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"machine_clear", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* additional (hidden) aliases, for convenience */ + {"cycles0", "TC_deliver_mode~threshold=0xf~complement=1~compare=1", 5, NULL, PRELOADS_75, 1, ABST_NONE}, + {"cycles1", "TC_deliver_mode~threshold=0xf~complement=1~compare=1", 6, NULL, PRELOADS_75, 1, ABST_NONE}, + {"insts0", "instr_retired~emask=0x3", 15, NULL, PRELOADS_75, 0, ABST_NONE}, + {"insts1", "instr_retired~emask=0x3", 16, NULL, PRELOADS_75, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry intelCore2list[] = { + // For post-processing, both Linux and Solaris definitions need to be "live". + // However, for data collection, OS-specific definitions may need to be hidden. + // Use REGNO_INVALID for definitions that should be hidden for data collection. +#define LINUX_ONLY REGNO_ANY +#define SOLARIS_ONLY REGNO_INVALID /* hidden for Linux data collection */ + + {"cycles", "cpu_clk_unhalted.core", /*6759307*/ SOLARIS_ONLY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"cycles", "cpu_clk_unhalted.thread", /*6759307*/ SOLARIS_ONLY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + /* Linux Note: 7046312 Many HWC tests fail on system Core2 system with perf_events if above alias used */ + {"cycles", "cpu_clk_unhalted", LINUX_ONLY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + + {"insts", "instr_retired.any", SOLARIS_ONLY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + /* Linux Note: 7046312 Many HWC tests fail on system Core2 system with perf_events if above alias used */ + {"insts", "inst_retired", LINUX_ONLY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + + // The following counters were identified in "Cycle Accounting Analysis on Intel Core2 Processors" by David Levinthal + {"uops_stalled", "rs_uops_dispatched~cmask=1~inv=1", REGNO_ANY, STXT ("uOps Stalled"), PRELOADS_7, 1, ABST_NONE}, + {"l2m", "mem_load_retired~umask=0x08", REGNO_ANY, STXT ("L2 Line Misses"), PRELOADS_5, 0, ABST_NONE}, + {"dtlbm", "mem_load_retired~umask=0x10", REGNO_ANY, STXT ("L1 DTLB Misses"), PRELOADS_5, 0, ABST_NONE}, + {"l1m", "mem_load_retired~umask=0x02", REGNO_ANY, STXT ("L1 Line Misses"), PRELOADS_6, 0, ABST_NONE}, + // {"stalls_resources","resource_stalls~umask=0x1f", REGNO_ANY, STXT("Resource Stalls"), PRELOADS_6, 1, ABST_NONE}, + {"rs_full", "resource_stalls~umask=0x02", REGNO_ANY, STXT ("Reservation Station Full"), PRELOADS_6, 1, ABST_NONE}, + {"br_miss_flush", "resource_stalls~umask=0x10", REGNO_ANY, STXT ("Mispredicted Branch Flushes"), PRELOADS_6, 1, ABST_NONE}, + {"ld_st_full", "resource_stalls~umask=0x04", REGNO_ANY, STXT ("Load/Store Buffers Full"), PRELOADS_6, 1, ABST_NONE}, + {"rob_full", "resource_stalls~umask=0x01", REGNO_ANY, STXT ("Reorder Buffer Full"), PRELOADS_6, 1, ABST_NONE}, + {"slow_decode", "ild_stall", REGNO_ANY, STXT ("Slow Instruction Decode"), PRELOADS_6, 1, ABST_NONE}, + {"br_miss", "br_cnd_missp_exec", REGNO_ANY, STXT ("Mispredicted Branches"), PRELOADS_5, 0, ABST_NONE}, + {"ret_miss", "br_call_missp_exec", REGNO_ANY, STXT ("Mispredicted Return Calls"), PRELOADS_5, 0, ABST_NONE}, + {"div_busy", "idle_during_div", REGNO_ANY, STXT ("Divider Unit Busy"), PRELOADS_5, 1, ABST_NONE}, + {"fp_assists", "fp_assist", REGNO_ANY, STXT ("FP Microcode Assists"), PRELOADS_5, 0, ABST_NONE}, + {"bus_busy", "bus_drdy_clocks~umask=0x60", REGNO_ANY, STXT ("Busy Data Bus"), PRELOADS_5, 1, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {/*30a*/"cpu_clk_unhalted.core", /*6759307*/ NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*30a*/"cpu_clk_unhalted.thread", /*6759307*/ NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*03*/"store_block", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*03*/"store_block.drain_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*03*/"store_block.order", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*03*/"store_block.snoop", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*09*/"memory_disambiguation.reset", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0c*/"page_walks.cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*14*/"cycles_div_busy", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*18*/"idle_during_div", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*19*/"delayed_bypass.load", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*21*/"l2_ads", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*23*/"l2_dbus_busy_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*32*/"l2_no_req", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c*/"cpu_clk_unhalted", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c*/"cpu_clk_unhalted.core_p", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*3c*/"cpu_clk_unhalted.bus", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c*/"cpu_clk_unhalted.no_other", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*42*/"l1d_cache_lock.duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*62*/"bus_drdy_clocks", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63*/"bus_lock_clocks", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*64*/"bus_data_rcv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7a*/"bus_hit_drv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7b*/"bus_hitm_drv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7d*/"busq_empty", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7e*/"snoop_stall_drv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7f*/"bus_io_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*83*/"inst_queue", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*83*/"inst_queue.full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*86*/"cycles_l1i_mem_stalled", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87*/"ild_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1*/"rs_uops_dispatched", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1*/"rs_uops_dispatched_port", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1*/"rs_uops_dispatched_port.0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1*/"rs_uops_dispatched_port.1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1*/"rs_uops_dispatched_port.2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1*/"rs_uops_dispatched_port.3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1*/"rs_uops_dispatched_port.4", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1*/"rs_uops_dispatched_port.5", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*6c*/"cycles_int", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*6c*/"cycles_int.masked", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*6c*/"cycles_int.pending_and_masked", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.rob_read_port", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.partial_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.flags", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.fpsw", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.other_serialization_stalls", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d4*/"seg_rename_stalls", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d4*/"seg_rename_stalls.es", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d4*/"seg_rename_stalls.ds", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d4*/"seg_rename_stalls.fs", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d4*/"seg_rename_stalls.gs", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d4*/"seg_rename_stalls.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*dc*/"resource_stalls", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*dc*/"resource_stalls.rob_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*dc*/"resource_stalls.rs_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*dc*/"resource_stalls.ld_st", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*dc*/"resource_stalls.fpcw", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*dc*/"resource_stalls.br_miss_clear", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*dc*/"resource_stalls.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + /* "Architectural" events: */ + {/*3c*/"unhalted-core-cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* additional (hidden) aliases for convenience */ + {"cycles0", "cpu_clk_unhalted", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "cpu_clk_unhalted", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "inst_retired", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "inst_retired", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + + +static Hwcentry intelNehalemList[] = { + /* 6832635: on Linux, we're not seeing consistent overflows on FFCs */ + /* 15634344==6940930: HWC overflow profiling can cause system hang on Solaris/core-i7 systems */ + /* 17578620: counter overflow for fixed-function counters hangs systems */ + /* same issues for intelSandyBridgeList and intelHaswellList */ + PERF_EVENTS_SW_EVENT_ALIASES + USE_INTEL_REF_CYCLES (133) + {"cycles", "cpu_clk_unhalted.thread_p", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "inst_retired.any_p", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + // cpu_clk_unhalted.ref: at the ref requency of the cpu. Should not be affected by Speedstep or Turbo. + // cpu_clk_unhalted.thread_p: with HT & 2 threads, 2x cycles. Affected by Speedstep and Turbo. + + // PEBs (Sampling) + {"l2m_latency", "mem_inst_retired.latency_above_threshold", REGNO_ANY, STXT ("L2 Cache Miss Est. Latency"), PRELOADS_4, 33, ABST_EXACT_PEBS_PLUS1}, + + // See file hwctable.README.corei7 + {"dch", "mem_load_retired.l1d_hit", REGNO_ANY, STXT ("L1 D-cache Hits"), PRELOADS_7, 0, ABST_NONE}, + {"dcm", "0xCB~umask=0x1e", REGNO_ANY, STXT ("L1 D-Cache Misses"), PRELOADS_65, 0, ABST_NONE}, /*mem_load_retired*/ + {"lfbdh", "mem_load_retired.hit_lfb", REGNO_ANY, STXT ("LFB D-cache Hits"), PRELOADS_65, 0, ABST_NONE}, + {"l2h", "mem_load_retired.l2_hit", REGNO_ANY, STXT ("L2 Cache Hits"), PRELOADS_65, 0, ABST_NONE}, + {"l2m", "0xCB~umask=0x1c", REGNO_ANY, STXT ("L2 Cache Misses"), PRELOADS_6, 0, ABST_NONE}, /*mem_load_retired*/ + {"l3h", "mem_load_retired.llc_unshared_hit", REGNO_ANY, STXT ("L3 Cache Hit w/o Snoop"), PRELOADS_6, 0, ABST_NONE}, + {"l3h_stall", "mem_load_retired.llc_unshared_hit", REGNO_ANY, STXT ("L3 Cache Hit w/o Snoop x 35: Est. Stalls"), PRELOADS_6, 35, ABST_NONE}, + {"l3hsnoop", "mem_load_retired.other_core_l2_hit_hitm", REGNO_ANY, STXT ("L3 Cache Hit w/Snoop"), PRELOADS_6, 0, ABST_NONE}, + {"l3hsnoop_stall", "mem_load_retired.other_core_l2_hit_hitm", REGNO_ANY, STXT ("L3 Cache Hit w/Snoop x 74: Est. Stalls"), PRELOADS_6, 74, ABST_NONE}, + {"l3m", "mem_load_retired.llc_miss", REGNO_ANY, STXT ("L3 Cache Misses"), PRELOADS_5, 0, ABST_NONE}, + {"l3m_stall", "mem_load_retired.llc_miss", REGNO_ANY, STXT ("L3 Cache Misses x 180: Estimated Stalls"), PRELOADS_5, 180, ABST_NONE}, + {"dtlbm", "dtlb_load_misses.walk_completed", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_6, 0, ABST_NONE}, + {"dtlbm_stall", "dtlb_load_misses.walk_completed", REGNO_ANY, STXT ("DTLB Misses x 30: Estimated Stalls"), PRELOADS_6, 30, ABST_NONE}, + {"addr_alias_stall", "partial_address_alias", REGNO_ANY, STXT ("Partial Address Aliases x 3: Est. Stalls"), PRELOADS_6, 3, ABST_NONE}, + {"uope_stall", "uops_executed.port234~cmask=1~inv=1", REGNO_ANY, STXT ("UOP Execute Stalls per Core"), PRELOADS_7, 1, ABST_NONE}, + {"uopr_stall", "uops_retired.any~cmask=1~inv=1", REGNO_ANY, STXT ("UOP Retired Stalls"), PRELOADS_7, 1, ABST_NONE}, + {"itlbm", "itlb_miss_retired", REGNO_ANY, STXT ("ITLB Misses"), PRELOADS_6, 0, ABST_NONE}, + {"l1i_stall", "l1i.cycles_stalled", REGNO_ANY, STXT ("L1 I-cache Stalls"), PRELOADS_6, 1, ABST_NONE}, + {"br_rets", "br_inst_retired.all_branches", REGNO_ANY, STXT ("Branch Instruction Retires"), PRELOADS_7, 0, ABST_NONE}, + {"br_misp", "br_misp_exec.any", REGNO_ANY, STXT ("Branch Mispredicts"), PRELOADS_6, 0, ABST_NONE}, + {"mach_clear", "machine_clears.cycles", REGNO_ANY, STXT ("Machine Clear Asserted"), PRELOADS_6, 1, ABST_NONE}, + {"fp_mmx", "fp_mmx_trans.any", REGNO_ANY, STXT ("FP-MMX Transistions"), PRELOADS_6, 0, ABST_NONE}, + {"div_busy", "arith.cycles_div_busy", REGNO_ANY, STXT ("Divider Busy Cycles"), PRELOADS_6, 1, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {/*30a*/"cpu_clk_unhalted.core", /*6759307*/ NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*30a*/"cpu_clk_unhalted.thread", /*6759307*/ NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*04*/"sb_drain.cycles", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*08.04*/"dtlb_load_misses.walk_cycles", /*westmere*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + //{/*0e*/"uops_issued.stalled_cycles",/*future, multibit*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*09*/"memory_disambiguation.reset", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*09*/"memory_disambiguation.watch_cycles", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0b*/"mem_inst_retired.latency_above_threshold", /*PEBS*/ NULL, REGNO_ANY, NULL, PRELOADS_4, 33, ABST_EXACT_PEBS_PLUS1}, //non-standard overflow + {/*14*/"arith.cycles_div_busy", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*17*/"inst_queue_write_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*1d*/"hw_int.cycles_masked", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*1d*/"hw_int.cycles_pending_and_masked", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c*/"cpu_clk_unhalted.thread_p", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*48*/"l1d_pend_miss.load_buffers_full", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*49.04*/"dtlb_misses.walk_cycles", /*westmere*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*4e*/"sfence_cycles", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*4f.10*/"ept.walk_cycles", /*westmere*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60*/"offcore_requests_outstanding.demand.read_data", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60*/"offcore_requests_outstanding.demand.read_code", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60*/"offcore_requests_outstanding.demand.rfo", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60*/"offcore_requests_outstanding.any.read", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63*/"cache_lock_cycles.l1d", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63*/"cache_lock_cycles.l1d_l2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*80*/"l1i.cycles_stalled", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*85*/"itlb_misses.walk_cycles", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*85*/"itlb_misses.pmh_busy_cycles", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87*/"ild_stall.lcp", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87*/"ild_stall.mru", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87*/"ild_stall.iq_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87*/"ild_stall.regen", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87*/"ild_stall.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2*/"resource_stalls.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2*/"resource_stalls.load", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2*/"resource_stalls.rs_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2*/"resource_stalls.store", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2*/"resource_stalls.rob_full", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2*/"resource_stalls.fpcw", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2*/"resource_stalls.mxcsr", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2*/"resource_stalls.other", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b0*/"offcore_requests_sq_full", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b3*/"snoopq_requests_outstanding.data", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b3*/"snoopq_requests_outstanding.invalidate", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b3*/"snoopq_requests_outstanding.code", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + //{/*c2*/"uops_retired.stalled_cycles",/*future, multibit*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c3*/"machine_clears.cycles", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.flags", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.registers", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.rob_read_port", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.scoreboard", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d2*/"rat_stalls.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*d4*/"seg_rename_stalls", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*f6*/"sq_full_stall_cycles", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + /* "Architectural" events: */ + {/*3c*/"unhalted-core-cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + PERF_EVENTS_SW_EVENT_DEFS + + /* additional (hidden) aliases for convenience */ +#if 0 + USE_INTEL_REF_CYCLES (133), +#endif + {"insts0", "inst_retired.any_p", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "inst_retired.any_p", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + + +static Hwcentry intelSandyBridgeList[] = { + /* see comments for "cycles" and "insts" for intelNehalemList */ + PERF_EVENTS_SW_EVENT_ALIASES + USE_INTEL_REF_CYCLES (100) + {"cycles", "cpu_clk_unhalted.thread_p", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "inst_retired.any_p", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + + // PEBS (sampling) + {"l2m_latency", "mem_trans_retired.load_latency", REGNO_ANY, STXT ("L2 Cache Miss Est. Latency"), PRELOADS_4, 65, ABST_EXACT_PEBS_PLUS1}, + + // See file hwctable.README.sandybridge + {"dch", "mem_load_uops_retired.l1_hit", REGNO_ANY, STXT ("L1 D-cache Hits"), PRELOADS_7, 0, ABST_NONE}, + {"dcm", "mem_load_uops_retired.l1_miss", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, /*mem_load_uops_retired*/ + {"l2h", "mem_load_uops_retired.l2_hit", REGNO_ANY, STXT ("L2 Cache Hits"), PRELOADS_65, 0, ABST_NONE}, + {"l2m", "mem_load_uops_retired.l2_miss", REGNO_ANY, STXT ("L2 Cache Misses"), PRELOADS_6, 0, ABST_NONE}, /*mem_load_uops_retired*/ + // Intel errata: BT241 and BT243 says the mem_load_uops_retired.llc* counters may not be reliable on some CPU variants + {"l3h", "mem_load_uops_retired.llc_hit", REGNO_ANY, STXT ("L3 Cache Hit w/o Snoop"), PRELOADS_6, 0, ABST_NONE}, // may undercount + {"l3m", "longest_lat_cache.miss", REGNO_ANY, STXT ("L3 Cache Misses"), PRELOADS_5, 0, ABST_NONE}, + + /* dtlbm has not been confirmed via Intel white paper */ + {"dtlbm", "dtlb_load_misses.walk_completed", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_6, 0, ABST_NONE}, + {"dtlbm_stall", "dtlb_load_misses.walk_completed", REGNO_ANY, STXT ("DTLB Misses x 30: Estimated Stalls"), PRELOADS_6, 30, ABST_NONE}, + {"dtlbm", "dtlb_load_misses.demand_ld_walk_completed", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_6, 0, ABST_NONE}, + {"dtlbm_stall", "dtlb_load_misses.demand_ld_walk_completed", REGNO_ANY, STXT ("DTLB Misses x 30: Estimated Stalls"), PRELOADS_6, 30, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {/* 30a */"cpu_clk_unhalted.thread", /*15634344==6940930*/ NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + //{/* 30a */"cpu_clk_unhalted.core", /*6759307*/ NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*08.04*/"dtlb_load_misses.walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*08.84*/"dtlb_load_misses.demand_ld_walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d.03*/"int_misc.recovery_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d.40*/"int_misc.rat_stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0e.01*/"uops_issued.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0e.01*/"uops_issued.core_stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*14.01*/"arith.fpu_div_active", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c.00*/"cpu_clk_unhalted.thread_p", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*48.01*/"l1d_pend_miss.pending_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*49.04*/"dtlb_store_misses.walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*59.20*/"partial_rat_stalls.flags_merge_uop", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*59.20*/"partial_rat_stalls.flags_merge_uop_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*59.40*/"partial_rat_stalls.slow_lea_window", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + //{/*59.80*/"partial_rat_stalls.mul_single_uop", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5b.0c*/"resource_stalls2.all_fl_empty", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5b.0f*/"resource_stalls2.all_prf_control", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5b.40*/"resource_stalls2.bob_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5b.4f*/"resource_stalls2.ooo_rsrc", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.01*/"cpl_cycles.ring0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.02*/"cpl_cycles.ring123", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.xx*/"cpl_cycles.ring0_trans", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.xx*/"cpl_cycles.ring0_transition", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5e.01*/"rs_events.empty_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.01*/"offcore_requests_outstanding.cycles_with_demand_data_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.01*/"offcore_requests_outstanding.demand_data_rd_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.04*/"offcore_requests_outstanding.cycles_with_demand_rfo", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.04*/"offcore_requests_outstanding.demand_rfo_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.08*/"offcore_requests_outstanding.cycles_with_data_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.08*/"offcore_requests_outstanding.all_data_rd_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.02*/"offcore_requests_outstanding.demand_code_rd_cycles", /*?*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63.01*/"lock_cycles.split_lock_uc_lock_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63.02*/"lock_cycles.cache_lock_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.00*/"idq.empty", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.04*/"idq.mite_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.08*/"idq.dsb_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.10*/"idq.ms_dsb_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.20*/"idq.ms_mite_uops_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.20*/"idq.ms_mite_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.30*/"idq.ms_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.18*/"idq.all_dsb_cycles_any_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.18*/"idq.all_dsb_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.18*/"idq.all_dsb_cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.24*/"idq.all_mite_cycles_any_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.24*/"idq.all_mite_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.24*/"idq.all_mite_cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.3c*/"idq.mite_all_cycles", /* Linux, but not in docs? */ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*80.04*/"icache.ifetch_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*85.04*/"itlb_misses.walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87.01*/"ild_stall.lcp", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87.04*/"ild_stall.iq_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_0_uops_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_le_1_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_le_2_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_le_3_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.01*/"idq_uops_not_delivered.cycles_ge_1_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.01*/"idq_uops_not_delivered.cycles_fe_was_ok", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.01*/"uops_executed_port.port_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.02*/"uops_executed_port.port_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.04*/"uops_executed_port.port_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.08*/"uops_executed_port.port_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.10*/"uops_executed_port.port_4", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.20*/"uops_executed_port.port_5", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.01*/"resource_stalls.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.02*/"resource_stalls.lb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.04*/"resource_stalls.rs", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.08*/"resource_stalls.sb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.0a*/"resource_stalls.lb_sb", /*sb-ep*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.0e*/"resource_stalls.mem_rs", /*sb-ep*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.10*/"resource_stalls.rob", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.20*/"resource_stalls.fcsw", /*sb*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.40*/"resource_stalls.mxcsr", /*sb*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.80*/"resource_stalls.other", /*sb*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.F0*/"resource_stalls.ooo_rsrc", /*sb-ep*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*a3.01*/"cycle_activity.cycles_l2_pending", /*F6M62*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*??.??*/"cycle_activity.stalls_l2_pending", /*F6M62*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.02*/"cycle_activity.cycles_ldm_pending", /*F6M62*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*??.??*/"cycle_activity.stalls_ldm_pending", /*F6M62*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.04*/"cycle_activity.cycles_no_execute", /*F6M62*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.04*/"cycle_activity.cycles_no_dispatch", /*sandybridge*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.08*/"cycle_activity.cycles_l1d_pending", /*F6M62*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*??.??*/"cycle_activity.stalls_l1d_pending", /*F6M62*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*ab.02*/"dsb2mite_switches.penalty_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.??*/"uops_executed.stall_cycles", /*? not in PRM*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_dispatched.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.stall_cycles", /*F6M62*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.cycles_ge_1_uop_exec", /*F6M62,not doc'd*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.cycles_ge_2_uops_exec", /*F6M62,not doc'd*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.cycles_ge_3_uops_exec", /*F6M62,not doc'd*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.cycles_ge_4_uops_exec", /*F6M62,not doc'd*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*bf.05*/"l1d_blocks.bank_conflict_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.total_cycles", /*cmask==0x10*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.core_stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.active_cycles", /*cmask==0x1*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, +#if 0 // need to see documentation on the following before marking them as cycles + uops_executed.cycles_ge_1_uop_exec[ / {0 | 1 | 2 | 3}], 1000003 (events) + uops_executed.cycles_ge_2_uops_exec[ / + {0 | 1 | 2 | 3} + ], 1000003 (events) + uops_executed.cycles_ge_3_uops_exec[ / + {0 | 1 | 2 | 3} + ], 1000003 (events) + uops_executed.cycles_ge_4_uops_exec[ / + {0 | 1 | 2 | 3} + ], 1000003 (events) +#endif + {/*cd.01*/"mem_trans_retired.load_latency", /*PEBS*/ NULL, REGNO_ANY, NULL, PRELOADS_4, 65, ABST_EXACT_PEBS_PLUS1}, //non-standard overflow + + /* "Architectural" events: */ + {/*3c*/"unhalted-core-cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + PERF_EVENTS_SW_EVENT_DEFS + + /* additional (hidden) aliases for convenience */ +#if 0 + USE_INTEL_REF_CYCLES (100), +#endif + {"insts0", "inst_retired.any_p", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "inst_retired.any_p", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + + +static Hwcentry intelHaswellList[] = { + /* see comments for "cycles" and "insts" for intelNehalemList */ + PERF_EVENTS_SW_EVENT_ALIASES + USE_INTEL_REF_CYCLES (100) + {"cycles", "cpu_clk_unhalted.thread_p", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "inst_retired.any_p", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + + // PEBS (sampling) + {"l2m_latency", "mem_trans_retired.load_latency", REGNO_ANY, STXT ("L2 Cache Miss Est. Latency"), PRELOADS_4, 65, ABST_EXACT_PEBS_PLUS1}, + + {"dch", "mem_load_uops_retired.l1_hit", REGNO_ANY, STXT ("L1 D-cache Hits"), PRELOADS_7, 0, ABST_NONE}, + {"dcm", "mem_load_uops_retired.l1_miss", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, //mem_load_uops_retired + {"dcm", "0xd1~umask=0x08", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, //mem_load_uops_retired + {"l2h", "mem_load_uops_retired.l2_hit", REGNO_ANY, STXT ("L2 Cache Hits"), PRELOADS_65, 0, ABST_NONE}, + {"l2m", "mem_load_uops_retired.l2_miss", REGNO_ANY, STXT ("L2 Cache Misses"), PRELOADS_6, 0, ABST_NONE}, //mem_load_uops_retired + {"l2m", "0xd1~umask=0x10", REGNO_ANY, STXT ("L2 Cache Misses"), PRELOADS_6, 0, ABST_NONE}, //mem_load_uops_retired + {"l3h", "mem_load_uops_retired.l3_hit", REGNO_ANY, STXT ("L3 Cache Hit w/o Snoop"), PRELOADS_6, 0, ABST_NONE}, + {"l3m", "mem_load_uops_retired.l3_miss", REGNO_ANY, STXT ("L3 Cache Misses"), PRELOADS_5, 0, ABST_NONE}, //mem_load_uops_retired + {"l3m", "0xd1~umask=0x20", REGNO_ANY, STXT ("L3 Cache Misses"), PRELOADS_5, 0, ABST_NONE}, //mem_load_uops_retired + + /* dtlbm has not been confirmed via Intel white paper */ + {"dtlbm", "dtlb_load_misses.walk_completed", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_6, 0, ABST_NONE}, + {"dtlbm_stall", "dtlb_load_misses.walk_completed", REGNO_ANY, STXT ("DTLB Misses x 30: Estimated Stalls"), PRELOADS_6, 30, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {/* 30a */"cpu_clk_unhalted.thread", /*15634344==6940930*/ NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + //{/* 30a */"cpu_clk_unhalted.core", /*6759307*/ NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*08.10*/"dtlb_load_misses.walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d.03*/"int_misc.recovery_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0e.01*/"uops_issued.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0e.01*/"uops_issued.core_stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c.00*/"cpu_clk_unhalted.thread_p", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*48.01*/"l1d_pend_miss.pending_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*49.04*/"dtlb_store_misses.walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.01*/"cpl_cycles.ring0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.02*/"cpl_cycles.ring123", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.xx*/"cpl_cycles.ring0_trans", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5e.01*/"rs_events.empty_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.01*/"offcore_requests_outstanding.cycles_with_demand_data_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.02*/"offcore_requests_outstanding.demand_code_rd_cycles", /*?*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.04*/"offcore_requests_outstanding.demand_rfo_cycles", /*?*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.08*/"offcore_requests_outstanding.cycles_with_data_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63.01*/"lock_cycles.split_lock_uc_lock_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63.02*/"lock_cycles.cache_lock_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.00*/"idq.empty", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.04*/"idq.mite_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.08*/"idq.dsb_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.10*/"idq.ms_dsb_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.20*/"idq.ms_mite_uops_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.20*/"idq.ms_mite_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.30*/"idq.ms_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.18*/"idq.all_dsb_cycles_any_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.18*/"idq.all_dsb_cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.24*/"idq.all_mite_cycles_any_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.24*/"idq.all_mite_cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*80.04*/"icache.ifetch_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*85.04*/"itlb_misses.walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87.01*/"ild_stall.lcp", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, // Intel SDM says these are stalls, not cycles + {/*87.04*/"ild_stall.iq_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_0_uops_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_le_1_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_le_2_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_le_3_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + // {/*9c.01*/"idq_uops_not_delivered.cycles_ge_1_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.01*/"idq_uops_not_delivered.cycles_fe_was_ok", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*a1.01*/"uops_executed_port.port_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.02*/"uops_executed_port.port_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.04*/"uops_executed_port.port_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.08*/"uops_executed_port.port_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.10*/"uops_executed_port.port_4", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.20*/"uops_executed_port.port_5", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.40*/"uops_executed_port.port_6", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.80*/"uops_executed_port.port_7", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.01*/"uops_executed_port.port_0_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.02*/"uops_executed_port.port_1_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.04*/"uops_executed_port.port_2_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.08*/"uops_executed_port.port_3_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.10*/"uops_executed_port.port_4_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.20*/"uops_executed_port.port_5_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.40*/"uops_executed_port.port_6_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.80*/"uops_executed_port.port_7_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*a2.01*/"resource_stalls.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.04*/"resource_stalls.rs", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.08*/"resource_stalls.sb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.10*/"resource_stalls.rob", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*a3.01*/"cycle_activity.cycles_l2_pending_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + // {/*a3.01*/"cycle_activity.cycles_l2_pending", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.02*/"cycle_activity.cycles_ldm_pending_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + // {/*a3.05*/"cycle_activity.stalls_l2_pending", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.08*/"cycle_activity.cycles_l1d_pending_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + // {/*a3.??*/"cycle_activity.cycles_no_execute", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + // {/*a3.??*/"cycle_activity.stalls_ldm_pending",/*?*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*ab.02*/"dsb2mite_switches.penalty_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*b1.??*/"uops_executed.stall_cycles", /*? not in PRM*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.??*/"uops_executed.cycles_ge_1_uop_exec", /*?*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.??*/"uops_executed.cycles_ge_2_uops_exec", /*?*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.??*/"uops_executed.cycles_ge_3_uops_exec", /*?*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.??*/"uops_executed.cycles_ge_4_uops_exec", /*?*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*c2.01*/"uops_retired.stall_cycles", /*cmask==1 + INV*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.total_cycles", /*cmask==0x1*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.core_stall_cycles", /*PEBS Any==1*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*c3.01*/"machine_clears.cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*ca.1e*/"fp_assist.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + {/*cd.01*/"mem_trans_retired.load_latency", /*PEBS*/ NULL, REGNO_ANY, NULL, PRELOADS_4, 65, ABST_EXACT_PEBS_PLUS1}, //non-standard overflow + + /* "Architectural" events: */ + {/*3c*/"unhalted-core-cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + PERF_EVENTS_SW_EVENT_DEFS + + /* additional (hidden) aliases for convenience */ +#if 0 + USE_INTEL_REF_CYCLES (100), +#endif + {"insts0", "inst_retired.any_p", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "inst_retired.any_p", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + + +static Hwcentry intelBroadwellList[] = { + /* see comments for "cycles" and "insts" for intelNehalemList */ + PERF_EVENTS_SW_EVENT_ALIASES + USE_INTEL_REF_CYCLES (100) + {"cycles", "cpu_clk_unhalted.thread_p", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "inst_retired.any_p", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + + // PEBS (sampling) + {"l2m_latency", "mem_trans_retired.load_latency", REGNO_ANY, STXT ("L2 Cache Miss Est. Latency"), PRELOADS_4, 65, ABST_EXACT_PEBS_PLUS1}, + {/*cd.01*/"mem_trans_retired.load_latency", NULL, REGNO_ANY, NULL, PRELOADS_4, 65, ABST_EXACT_PEBS_PLUS1}, + + // aliases (the first set are PEBS, but on Intel the only precise counter we support is l2m_latency) + {"dch", "mem_load_uops_retired.l1_hit", REGNO_ANY, STXT ("L1 D-cache Hits"), PRELOADS_7, 0, ABST_NONE}, + {"dcm", "mem_load_uops_retired.l1_miss", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, + {"l2h", "mem_load_uops_retired.l2_hit", REGNO_ANY, STXT ("L2 Cache Hits"), PRELOADS_65, 0, ABST_NONE}, + {"l2m", "mem_load_uops_retired.l2_miss", REGNO_ANY, STXT ("L2 Cache Misses"), PRELOADS_6, 0, ABST_NONE}, + {"l3h", "mem_load_uops_retired.l3_hit", REGNO_ANY, STXT ("L3 Cache Hits"), PRELOADS_6, 0, ABST_NONE}, + {"l3m", "mem_load_uops_retired.l3_miss", REGNO_ANY, STXT ("L3 Cache Misses"), PRELOADS_5, 0, ABST_NONE}, + {"dtlbm", "dtlb_load_misses.walk_completed", REGNO_ANY, STXT ("DTLB Misses"), PRELOADS_6, 0, ABST_NONE}, + + // counters that can be time converted (add FFCs if we decide to support them) + // counters that are load-store (did not include any... do we want to?) + {/*08.10*/"dtlb_load_misses.walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d.03*/"int_misc.recovery_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0e.01*/"uops_issued.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0e.01*/"uops_issued.core_stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*14.01*/"arith.fpu_div_active", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c.00*/"cpu_clk_unhalted.thread_p_any", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*3c.00*/"cpu_clk_unhalted.thread_p", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*3c.02*/"cpu_clk_thread_unhalted.one_thread_active", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*48.01*/"l1d_pend_miss.pending_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*48.01*/"l1d_pend_miss.pending_cycles_any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*49.10*/"dtlb_store_misses.walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*4f.10*/"ept.walk_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.01*/"cpl_cycles.ring0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.01*/"cpl_cycles.ring0_trans", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5c.02*/"cpl_cycles.ring123", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5e.01*/"rs_events.empty_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.01*/"offcore_requests_outstanding.cycles_with_demand_data_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.02*/"offcore_requests_outstanding.demand_code_rd_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.04*/"offcore_requests_outstanding.demand_rfo_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.08*/"offcore_requests_outstanding.cycles_with_data_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63.01*/"lock_cycles.split_lock_uc_lock_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63.02*/"lock_cycles.cache_lock_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.02*/"idq.empty", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.04*/"idq.mite_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.08*/"idq.dsb_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.10*/"idq.ms_dsb_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.18*/"idq.all_dsb_cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.18*/"idq.all_dsb_cycles_any_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.24*/"idq.all_mite_cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.24*/"idq.all_mite_cycles_any_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.30*/"idq.ms_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*85.10*/"itlb_misses.walk_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_0_uops_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_le_1_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_le_2_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.xx*/"idq_uops_not_delivered.cycles_le_3_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.01*/"idq_uops_not_delivered.cycles_fe_was_ok", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.01*/"uops_executed_port.port_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.02*/"uops_executed_port.port_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.04*/"uops_executed_port.port_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.08*/"uops_executed_port.port_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.10*/"uops_executed_port.port_4", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.20*/"uops_executed_port.port_5", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.40*/"uops_executed_port.port_6", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.80*/"uops_executed_port.port_7", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.01*/"uops_executed_port.port_0_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.02*/"uops_executed_port.port_1_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.04*/"uops_executed_port.port_2_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.08*/"uops_executed_port.port_3_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.10*/"uops_executed_port.port_4_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.20*/"uops_executed_port.port_5_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.40*/"uops_executed_port.port_6_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.80*/"uops_executed_port.port_7_core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.01*/"resource_stalls.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.04*/"resource_stalls.rs", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.08*/"resource_stalls.sb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.10*/"resource_stalls.rob", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.01*/"cycle_activity.cycles_l2_pending", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.02*/"cycle_activity.cycles_ldm_pending", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.04*/"cycle_activity.cycles_no_execute", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.08*/"cycle_activity.cycles_l1d_pending", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a8.01*/"lsd.cycles_active", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a8.01*/"lsd.cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*ab.02*/"dsb2mite_switches.penalty_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.total_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.core_stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c3.01*/"machine_clears.cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*ca.1e*/"fp_assist.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* "Architectural" events: */ + {/*3c*/"unhalted-core-cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + PERF_EVENTS_SW_EVENT_DEFS + + /* additional (hidden) aliases for convenience */ +#if 0 + USE_INTEL_REF_CYCLES (100), +#endif + {"insts0", "inst_retired.any_p", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "inst_retired.any_p", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry intelSkylakeList[] = { + /* see comments for "cycles" and "insts" for intelNehalemList */ + PERF_EVENTS_SW_EVENT_ALIASES + USE_INTEL_REF_CYCLES (25) + {"cycles", "cpu_clk_unhalted.thread_p", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "inst_retired.any_p", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + + // PEBS (sampling) + {"l2m_latency", "mem_trans_retired.load_latency", REGNO_ANY, STXT ("L2 Cache Miss Est. Latency"), PRELOADS_4, 65, ABST_EXACT_PEBS_PLUS1}, + {/*cd.01*/"mem_trans_retired.load_latency", NULL, REGNO_ANY, NULL, PRELOADS_4, 65, ABST_EXACT_PEBS_PLUS1}, + + // aliases (the first set are PEBS, but on Intel the only precise counter we support is l2m_latency) + {"dch", "mem_load_retired.l1_hit", REGNO_ANY, STXT ("L1 D-cache Hits"), PRELOADS_7, 0, ABST_NONE}, + {"dcm", "mem_load_retired.l1_miss", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, + {"l2h", "mem_load_retired.l2_hit", REGNO_ANY, STXT ("L2 Cache Hits"), PRELOADS_65, 0, ABST_NONE}, + {"l2m", "mem_load_retired.l2_miss", REGNO_ANY, STXT ("L2 Cache Misses"), PRELOADS_6, 0, ABST_NONE}, + {"l2m_stall", "cycle_activity.stalls_l2_miss", REGNO_ANY, STXT ("L2 Cache Miss Stall"), PRELOADS_7, 1, ABST_NONE}, // needs validation + {"l3h", "mem_load_retired.l3_hit", REGNO_ANY, STXT ("L3 Cache Hits"), PRELOADS_6, 0, ABST_NONE}, + {"l3m", "mem_load_retired.l3_miss", REGNO_ANY, STXT ("L3 Cache Misses"), PRELOADS_5, 0, ABST_NONE}, + {"l3m_stall", "cycle_activity.stalls_l3_miss", REGNO_ANY, STXT ("L3 Cache Miss Stall"), PRELOADS_7, 1, ABST_NONE}, // needs validation + {"dtlbm_stall", "dtlb_load_misses.walk_active", REGNO_ANY, STXT ("DTLB Miss Est Stall"), PRELOADS_7, 1, ABST_NONE, STXT ("Estimated time stalled on DTLB misses requiring a tablewalk. Does not include time related to STLB hits.")}, // needs validation + // PEBS mem_inst_retired.stlb_miss_loads for finding location of DTLB issues + // what about: dtlb_load_misses.walk_completed, dtlb_load_misses.walk_pending, dtlb_load_misses.stlb_hit + + {"fp_scalar", "fp_arith_inst_retired.scalar_double~umask=0x3", REGNO_ANY, STXT ("FP Scalar uOps"), PRELOADS_7, 0, ABST_NONE, STXT ("Floating-point scalar micro-ops that retired")}, + {"fp_vector", "fp_arith_inst_retired.128b_packed_double~umask=0x3c", REGNO_ANY, STXT ("FP Vector uOps"), /*needs test*/ PRELOADS_7, 0, ABST_NONE, STXT ("Floating-point vector micro-ops that retired")}, + + // counters that can be time converted (add FFCs if we decide to support them) + // counters that are load-store (did not include any... do we want to?) + {/*08.10*/"dtlb_load_misses.walk_active", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*08.10*/"dtlb_load_misses.walk_pending", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d.01*/"int_misc.recovery_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d.01*/"int_misc.recovery_cycles_any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d.80*/"int_misc.clear_resteer_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0e.01*/"uops_issued.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*14.01*/"arith.divider_active", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c.00*/"cpu_clk_unhalted.ring0_trans", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*3c.00*/"cpu_clk_unhalted.thread_p_any", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*3c.00*/"cpu_clk_unhalted.thread_p", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*3c.00*/"cpu_clk_unhalted.core", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*48.01*/"l1d_pend_miss.pending_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*48.01*/"l1d_pend_miss.pending_cycles_any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*49.10*/"dtlb_store_misses.walk_active", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*49.10*/"dtlb_store_misses.walk_pending", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*4f.10*/"ept.walk_pending", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*5e.01*/"rs_events.empty_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.01*/"offcore_requests_outstanding.cycles_with_demand_data_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.01*/"offcore_requests_outstanding.demand_data_rd_ge_6", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.02*/"offcore_requests_outstanding.cycles_with_demand_code_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.04*/"offcore_requests_outstanding.cycles_with_demand_rfo", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.08*/"offcore_requests_outstanding.cycles_with_data_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.10*/"offcore_requests_outstanding.cycles_with_l3_miss_demand_data_rd", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*60.10*/"offcore_requests_outstanding.l3_miss_demand_data_rd_ge_6", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63.02*/"lock_cycles.cache_lock_duration", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.04*/"idq.mite_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.08*/"idq.dsb_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.10*/"idq.ms_dsb_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.18*/"idq.all_dsb_cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.18*/"idq.all_dsb_cycles_any_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.24*/"idq.all_mite_cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.24*/"idq.all_mite_cycles_any_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*79.30*/"idq.ms_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*80.04*/"icache_16b.ifdata_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*83.04*/"icache_64b.iftag_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*85.10*/"itlb_misses.walk_active", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*85.10*/"itlb_misses.walk_pending", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*87.01*/"ild_stall.lcp", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.01*/"idq_uops_not_delivered.cycles_0_uops_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.01*/"idq_uops_not_delivered.cycles_le_1_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.01*/"idq_uops_not_delivered.cycles_le_2_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.01*/"idq_uops_not_delivered.cycles_le_3_uop_deliv.core", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*9c.01*/"idq_uops_not_delivered.cycles_fe_was_ok", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.01*/"uops_dispatched_port.port_0", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.02*/"uops_dispatched_port.port_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.04*/"uops_dispatched_port.port_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.08*/"uops_dispatched_port.port_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.10*/"uops_dispatched_port.port_4", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.20*/"uops_dispatched_port.port_5", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.40*/"uops_dispatched_port.port_6", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a1.80*/"uops_dispatched_port.port_7", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.01*/"resource_stalls.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a2.08*/"resource_stalls.sb", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.01*/"cycle_activity.cycles_l2_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.02*/"cycle_activity.cycles_l3_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.04*/"cycle_activity.stalls_total", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.05*/"cycle_activity.stalls_l2_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.06*/"cycle_activity.stalls_l3_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.08*/"cycle_activity.cycles_l1d_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.0c*/"cycle_activity.stalls_l1d_miss", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.10*/"cycle_activity.cycles_mem_any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a3.14*/"cycle_activity.stalls_mem_any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a6.01*/"exe_activity.exe_bound_0_ports", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a6.02*/"exe_activity.1_ports_util", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a6.04*/"exe_activity.2_ports_util", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a6.08*/"exe_activity.3_ports_util", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a6.10*/"exe_activity.4_ports_util", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a6.40*/"exe_activity.bound_on_stores", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a8.01*/"lsd.cycles_4_uops", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*a8.01*/"lsd.cycles_active", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*ab.02*/"dsb2mite_switches.penalty_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.cycles_ge_1_uop_exec", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.cycles_ge_2_uops_exec", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.cycles_ge_3_uops_exec", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.cycles_ge_4_uops_exec", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.01*/"uops_executed.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.02*/"uops_executed.core_cycles_ge_1", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.02*/"uops_executed.core_cycles_ge_2", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.02*/"uops_executed.core_cycles_ge_3", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.02*/"uops_executed.core_cycles_ge_4", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*b1.02*/"uops_executed.core_cycles_none", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c0.1*/"inst_retired.total_cycles_ps", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.stall_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c2.01*/"uops_retired.total_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*ca.1e*/"fp_assist.any", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* "Architectural" events: */ + {/* FFC */"cpu_clk_unhalted.thread", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/* FFC */"unhalted-core-cycles", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + PERF_EVENTS_SW_EVENT_DEFS + + /* additional (hidden) aliases for convenience */ +#if 0 + USE_INTEL_REF_CYCLES (25), +#endif + {"insts0", "inst_retired.any_p", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "inst_retired.any_p", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry intelLinuxUnknown[] = { + PERF_EVENTS_SW_EVENT_ALIASES + // USE_INTEL_REF_CYCLES(100) // freq is unknown + {"cycles", "unhalted-core-cycles", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"cycles", "PERF_COUNT_HW_CPU_CYCLES", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "instruction-retired", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"insts", "PERF_COUNT_HW_INSTRUCTIONS", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + + {"dcm", "PERF_COUNT_HW_CACHE_MISSES.L1D", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, + {"llm", "llc-misses", REGNO_ANY, STXT ("Last-Level Cache Misses"), PRELOADS_5, 0, ABST_NONE}, + {"llm", "PERF_COUNT_HW_CACHE_MISSES.LL", REGNO_ANY, STXT ("Last-Level Cache Misses"), PRELOADS_5, 0, ABST_NONE}, + + {"br_msp", "branch-misses-retired", REGNO_ANY, STXT ("Branch Mispredict"), PRELOADS_6, 0, ABST_NONE}, + {"br_msp", "PERF_COUNT_HW_BRANCH_MISSES", REGNO_ANY, STXT ("Branch Mispredict"), PRELOADS_6, 0, ABST_NONE}, + {"br_ins", "branch-instruction-retired", REGNO_ANY, STXT ("Branch Instructions"), PRELOADS_7, 0, ABST_NONE}, + {"br_ins", "PERF_COUNT_HW_BRANCH_INSTRUCTIONS", REGNO_ANY, STXT ("Branch Instructions"), PRELOADS_7, 0, ABST_NONE}, + + // counters that can be time converted (add FFCs if we decide to support them) + // counters that are load-store (did not include any... do we want to?) + /* "Architectural" events: */ + {/* FFC */"cpu_clk_unhalted.thread", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/* FFC */"unhalted-core-cycles", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + PERF_EVENTS_SW_EVENT_DEFS + + /* additional (hidden) aliases for convenience */ + {"cycles0", "unhalted-reference-cycles", 0, NULL, PRELOADS_6, -(25), ABST_NONE}, //YXXX -can't do with ref cycles # + {"cycles0", "PERF_COUNT_HW_BUS_CYCLES", 0, NULL, PRELOADS_6, -(25), ABST_NONE}, //YXXX -can't do with ref cycles # + {"cycles1", "unhalted-reference-cycles", 1, NULL, PRELOADS_65, -(25), ABST_NONE}, //YXXX - can't do with ref cycles # + {"cycles1", "PERF_COUNT_HW_BUS_CYCLES", 1, NULL, PRELOADS_65, -(25), ABST_NONE}, //YXXX - can't do with ref cycles # + {"insts0", "instruction-retired", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts0", "PERF_COUNT_HW_INSTRUCTIONS", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "instruction-retired", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "PERF_COUNT_HW_INSTRUCTIONS", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry intelAtomList[] = { + {"cycles", "cpu_clk_unhalted.core", /*6759307*/ REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"cycles", "cpu_clk_unhalted.thread", /*6759307*/ REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"insts", "instr_retired.any", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_7, 0, ABST_NONE}, + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + /* XXXX add core2-related entries if appropriate */ + {/*30A*/"cpu_clk_unhalted.core", /*6759307*/ NULL, REGNO_ANY, NULL, PRELOADS_7, 1, ABST_NONE}, + {/*30A*/"cpu_clk_unhalted.thread", /*6759307*/ NULL, REGNO_ANY, NULL, PRELOADS_7, 1, ABST_NONE}, + {/*0c*/"page_walks.cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*14*/"cycles_div_busy", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*21*/"l2_ads", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*22*/"l2_dbus_busy", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*32*/"l2_no_req", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c*/"cpu_clk_unhalted.core_p", NULL, REGNO_ANY, NULL, PRELOADS_7, 1, ABST_NONE}, + {/*3c*/"cpu_clk_unhalted.bus", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*3c*/"cpu_clk_unhalted.no_other", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*62*/"bus_drdy_clocks", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*63*/"bus_lock_clocks", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*64*/"bus_data_rcv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7a*/"bus_hit_drv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7b*/"bus_hitm_drv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7d*/"busq_empty", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7e*/"snoop_stall_drv", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*7f*/"bus_io_wait", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c6*/"cycles_int_masked.cycles_int_masked", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*c6*/"cycles_int_masked.cycles_int_pending_and_masked", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* "Architectural" events: */ + {/*3c*/"unhalted-core-cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* additional (hidden) aliases for convenience */ + {"cycles0", "cpu_clk_unhalted.core_p", 0, NULL, PRELOADS_75, 1, ABST_NONE}, + {"cycles1", "cpu_clk_unhalted.core_p", 1, NULL, PRELOADS_75, 1, ABST_NONE}, + {"insts0", "inst_retired.any_p", 0, NULL, PRELOADS_75, 0, ABST_NONE}, + {"insts1", "inst_retired.any_p", 1, NULL, PRELOADS_75, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry amd_opteron_10h_11h[] = { + {"cycles", "BU_cpu_clk_unhalted", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "FR_retired_x86_instr_w_excp_intr", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"icr", "IC_fetch", REGNO_ANY, STXT ("L1 I-cache Refs"), PRELOADS_7, 0, ABST_NONE}, /* new */ + {"icm", "IC_miss", REGNO_ANY, STXT ("L1 I-cache Misses"), PRELOADS_6, 0, ABST_NONE}, + {"l2itlbh", "IC_itlb_L1_miss_L2_hit", REGNO_ANY, STXT ("L2 ITLB Hits"), PRELOADS_6, 0, ABST_NONE}, /* new */ + {"l2itlbm", "IC_itlb_L1_miss_L2_miss", REGNO_ANY, STXT ("L2 ITLB Misses"), PRELOADS_5, 0, ABST_NONE}, /* new */ + {"l2ir", "BU_internal_L2_req~umask=0x1", REGNO_ANY, STXT ("L2 I-cache Refs"), PRELOADS_6, 0, ABST_NONE}, + {"l2im", "BU_fill_req_missed_L2~umask=0x1", REGNO_ANY, STXT ("L2 I-cache Misses"), PRELOADS_4, 0, ABST_NONE}, + {"dcr", "DC_access", REGNO_ANY, STXT ("L1 D-cache Refs"), PRELOADS_7, 0, ABST_NONE}, /* new */ + {"dcm", "DC_miss", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, /* new */ + {"l2dtlbh", "DC_dtlb_L1_miss_L2_hit", REGNO_ANY, STXT ("L2 DTLB Hits"), PRELOADS_6, 0, ABST_NONE}, /* new */ + {"l2dtlbm", "DC_dtlb_L1_miss_L2_miss", REGNO_ANY, STXT ("L2 DTLB Misses"), PRELOADS_5, 0, ABST_NONE}, /* new */ + {"l2dr", "BU_internal_L2_req~umask=0x2", REGNO_ANY, STXT ("L2 D-cache Refs"), PRELOADS_65, 0, ABST_NONE}, /* hwc_cache_load: 1.6x overcount on shanghai01 */ + {"l2dm", "BU_fill_req_missed_L2~umask=0x2", REGNO_ANY, STXT ("L2 D-cache Misses"), PRELOADS_6, 0, ABST_NONE}, /* new */ + {"fpadd", "FP_dispatched_fpu_ops~umask=0x1", REGNO_ANY, STXT ("FP Adds"), PRELOADS_7, 0, ABST_NONE}, + {"fpmul", "FP_dispatched_fpu_ops~umask=0x2", REGNO_ANY, STXT ("FP Muls"), PRELOADS_7, 0, ABST_NONE}, + {"fpustall", "FR_dispatch_stall_fpu_full", REGNO_ANY, STXT ("FPU Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + {"memstall", "FR_dispatch_stall_ls_full", REGNO_ANY, STXT ("Memory Unit Stall Cycles"), PRELOADS_7, 1, ABST_NONE}, + // For PAPI mappings, see hwctable.README.family10h + // For PAPI mappings, see hwctable.README.opteron + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {"BU_cpu_clk_unhalted", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {"FP_cycles_no_fpu_ops_retired", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"FP_serialize_ops_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"FR_dispatch_stall_branch_abort_to_retire", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_dispatch_stall_far_ctl_trsfr_resync_branch_pend", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_dispatch_stall_fpu_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_dispatch_stall_ls_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_dispatch_stall_reorder_buffer_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_dispatch_stall_resv_stations_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_dispatch_stall_segment_load", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_dispatch_stall_serialization", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_dispatch_stall_waiting_all_quiet", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_dispatch_stalls", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_intr_masked_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_intr_masked_while_pending_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"FR_nothing_to_dispatch", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"IC_instr_fetch_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"LS_buffer_2_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + {"NB_mem_ctrlr_dram_cmd_slots_missed", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {"NB_mem_ctrlr_turnaround", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_TBD}, + + /* additional (hidden) aliases, for convenience */ + {"cycles0", "BU_cpu_clk_unhalted", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "BU_cpu_clk_unhalted", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "FR_retired_x86_instr_w_excp_intr", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "FR_retired_x86_instr_w_excp_intr", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry amd_15h[] = { + {"cycles", "CU_cpu_clk_unhalted", REGNO_ANY, STXT ("CPU Cycles"), PRELOADS_75, 1, ABST_NONE}, + {"insts", "EX_retired_instr_w_excp_intr", REGNO_ANY, STXT ("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"icr", "IC_fetch", REGNO_ANY, STXT ("L1 I-cache Refs"), PRELOADS_7, 0, ABST_NONE}, /* new */ + {"icm", "IC_miss", REGNO_ANY, STXT ("L1 I-cache Misses"), PRELOADS_6, 0, ABST_NONE}, + {"l2im", "IC_refill_from_system", REGNO_ANY, STXT ("L2 I-cache Misses"), PRELOADS_6, 0, ABST_NONE}, + {"dcr", "DC_access", REGNO_ANY, STXT ("L1 D-cache Refs"), PRELOADS_7, 0, ABST_NONE}, /* new */ + {"dcm", "DC_miss~umask=0x3", REGNO_ANY, STXT ("L1 D-cache Misses"), PRELOADS_65, 0, ABST_NONE}, /* new */ + {"l2dm", "DC_refill_from_system", REGNO_ANY, STXT ("L2 D-cache Misses"), PRELOADS_6, 0, ABST_NONE}, /* new */ + {"dtlbm", "DC_unified_tlb_miss~umask=0x7", REGNO_ANY, STXT ("L2 DTLB Misses"), PRELOADS_5, 0, ABST_NONE}, /* new */ + // For PAPI mappings, see hwctable.README.family15h + + /* explicit definitions of (hidden) entries for proper counters */ + /* Only counters that can be time converted, or are load-store need to be in this table */ + {/*001.xx*/"FP_scheduler_empty", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*006.xx*/"FP_bottom_execute_uops_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*023.xx*/"LS_ldq_stq_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*024.xx*/"LS_locked_operation", /*umask!=0*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*069.xx*/"CU_mab_wait_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*076.xx*/"CU_cpu_clk_unhalted", NULL, REGNO_ANY, NULL, PRELOADS_75, 1, ABST_NONE}, + {/*087.xx*/"IC_instr_fetch_stall", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0cd.xx*/"EX_intr_masked_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0ce.xx*/"EX_intr_masked_while_pending_cycles", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d0.xx*/"DE_nothing_to_dispatch", /*future*/ NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d1.xx*/"DE_dispatch_stalls", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d3.xx*/"DE_dispatch_stall_serialization", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d5.xx*/"DE_dispatch_stall_instr_retire_q_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d6.xx*/"DE_dispatch_stall_int_scheduler_q_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d7.xx*/"DE_dispatch_stall_fp_scheduler_q_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d8.xx*/"DE_dispatch_stall_ldq_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*0d9.xx*/"DE_dispatch_stall_waiting_all_quiet", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + {/*1d8.xx*/"EX_dispatch_stall_stq_full", NULL, REGNO_ANY, NULL, PRELOAD_DEF, 1, ABST_NONE}, + + /* additional (hidden) aliases, for convenience */ + {"cycles0", "CU_cpu_clk_unhalted", 0, NULL, PRELOADS_8, 1, ABST_NONE}, + {"cycles1", "CU_cpu_clk_unhalted", 1, NULL, PRELOADS_8, 1, ABST_NONE}, + {"insts0", "EX_retired_instr_w_excp_intr", 0, NULL, PRELOADS_8, 0, ABST_NONE}, + {"insts1", "EX_retired_instr_w_excp_intr", 1, NULL, PRELOADS_8, 0, ABST_NONE}, + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +#define USE_ARM_REF_CYCLES \ + {"usr_time","cycles", REGNO_ANY, STXT("User CPU"), PRELOADS_85, 1, ABST_NONE}, \ + {"sys_time","cycles~system=1~user=0", REGNO_ANY, STXT("System CPU"), PRELOADS_85, 1, ABST_NONE}, \ + +static Hwcentry armlist[] = { + USE_ARM_REF_CYCLES +// Hardware event: + {"branch-instructions", NULL, REGNO_ANY, STXT("Branch-instructions"), PRELOADS_35, 0, ABST_NONE}, + {"branch-misses", NULL, REGNO_ANY, STXT("Branch-misses"), PRELOADS_35, 0, ABST_NONE}, + {"bus-cycles", NULL, REGNO_ANY, STXT("Bus Cycles"), PRELOADS_35, 1, ABST_NONE}, + {"cache-misses", NULL, REGNO_ANY, STXT("Cache-misses"), PRELOADS_35, 0, ABST_NONE}, + {"cache-references", NULL, REGNO_ANY, STXT("Cache-references"), PRELOADS_35, 0, ABST_NONE}, + {"cycles", NULL, REGNO_ANY, STXT("CPU Cycles"), PRELOADS_85, 1, ABST_NONE}, + {"insts", "instructions", REGNO_ANY, STXT("Instructions Executed"), PRELOADS_75, 0, ABST_NONE}, + {"ref-cycles", NULL, REGNO_ANY, STXT("Total Cycles"), PRELOADS_85, 1, ABST_NONE}, + {"stalled-cycles-backend", NULL, REGNO_ANY, STXT("Stalled Cycles during issue."), PRELOADS_85, 1, ABST_NONE}, + {"stalled-cycles-frontend", NULL, REGNO_ANY, STXT("Stalled Cycles during retirement."), PRELOADS_85, 1, ABST_NONE}, + +// Software event: + {"alignment-faults", NULL, REGNO_ANY, STXT("Alignment Faults"), PRELOADS_85, 0, ABST_NONE}, + {"context-switches", NULL, REGNO_ANY, STXT("Context Switches"), PRELOADS_85, 0, ABST_NONE}, + {"cpu-clock", NULL, REGNO_ANY, STXT("CPU Clock"), PRELOADS_85, 1, ABST_NONE}, + {"cpu-migrations", NULL, REGNO_ANY, STXT("CPU Migrations"), PRELOADS_85, 0, ABST_NONE}, + {"emulation-faults", NULL, REGNO_ANY, STXT("Emulation Faults"), PRELOADS_85, 0, ABST_NONE}, + {"major-faults", NULL, REGNO_ANY, STXT("Major Page Faults"), PRELOADS_85, 0, ABST_NONE}, + {"minor-faults", NULL, REGNO_ANY, STXT("Minor Page Faults"), PRELOADS_85, 0, ABST_NONE}, + {"page-faults", NULL, REGNO_ANY, STXT("Page Faults"), PRELOADS_85, 0, ABST_NONE}, + {"task-clock", NULL, REGNO_ANY, STXT("Clock Count Specific"), PRELOADS_85, 1, ABST_NONE}, + +// Hardware cache event + {"L1-dcache-load-misses", NULL, REGNO_ANY, STXT("L1 D-cache Load Misses"), PRELOADS_35, 0, ABST_NONE}, + {"L1-dcache-loads", NULL, REGNO_ANY, STXT("L1 D-cache Loads"), PRELOADS_35, 0, ABST_NONE}, + {"L1-dcache-store-misses", NULL, REGNO_ANY, STXT("L1 D-cache Store Misses"), PRELOADS_35, 0, ABST_NONE}, + {"L1-dcache-stores", NULL, REGNO_ANY, STXT("L1 D-cache Store Stores"), PRELOADS_35, 0, ABST_NONE}, + {"L1-icache-load-misses", NULL, REGNO_ANY, STXT("L1 Instructions Load Misses"), PRELOADS_35, 0, ABST_NONE}, + {"L1-icache-load-misses", NULL, REGNO_ANY, STXT("L1 Instructions Loads"), PRELOADS_35, 0, ABST_NONE}, + {"dTLB-load-misses", NULL, REGNO_ANY, STXT("D-TLB Load Misses"), PRELOADS_35, 0, ABST_NONE}, + {"dTLB-loads", NULL, REGNO_ANY, STXT("D-TLB Loads"), PRELOADS_35, 0, ABST_NONE}, + {"iTLB-load-misses", NULL, REGNO_ANY, STXT("The Instruction TLB Load Misses"), PRELOADS_35, 0, ABST_NONE}, + {"iTLB-loads", NULL, REGNO_ANY, STXT("The Instruction TLB Loads"), PRELOADS_35, 0, ABST_NONE}, + + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +static Hwcentry unknownlist[] = + /* used for unrecognized CPU type */{ + {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE} +}; + +/* structure defining the counters for a CPU type */ +typedef struct +{ + int cputag; + Hwcentry *stdlist_table; +#define MAX_DEFAULT_HWC_DEFS 4 // allows multiple defs to handle OS variations; extend as needed + char *default_exp_p[MAX_DEFAULT_HWC_DEFS + 1]; // end of list MUST be marked with NULL +} cpu_list_t; + +/* IMPORTANT NOTE: + * + * Any default HWC string must consist of counter names separated by -TWO- commas, + * with a no trailing comma/value after the last counter name + * + * Only aliased counters should be specified; non-aliased counters will + * not get the right overflow values set. + * If the string is not formatted that way, -h hi and -h lo will fail + */ +static cpu_list_t cputabs[] = { + {CPC_ULTRA1, usIlist, {NULL}}, /* bind will fail */ + {CPC_ULTRA2, usIlist, {NULL}}, /* bind will fail */ + {CPC_ULTRA3, usIIIlist, {"insts,,ecstall", 0}}, + {CPC_ULTRA3_PLUS, usIIIlist, {"insts,,ecstall", 0}}, + {CPC_ULTRA3_I, usIIIlist, {"insts,,ecstall", 0}}, + {CPC_ULTRA4_PLUS, usIVplist, {"insts,,ecstall", 0}}, + {CPC_ULTRA_T1, niagara1, {"insts", 0}}, + {CPC_ULTRA_T2, niagara2, {"insts,,+l2drm", 0}}, + {CPC_ULTRA_T2P, niagara2, {"insts,,+l2drm", 0}}, + {CPC_ULTRA_T3, niagara2, {"insts,,+l2drm", 0}}, + {CPC_SPARC_T4, sparc_t4, {"insts,,cycles,,c_stalls,,dcm", "c_stalls", 0}}, + {CPC_SPARC_M4, sparc_t5_m6, {"insts,,cycles,,c_stalls,,dcm", "c_stalls", 0}}, // renamed to m5 + {CPC_SPARC_T5, sparc_t5_m6, {"insts,,cycles,,c_stalls,,dcm", "c_stalls", 0}}, + {CPC_SPARC_M5, sparc_t5_m6, {"insts,,cycles,,c_stalls,,dcm", "c_stalls", 0}}, + {CPC_SPARC_T6, sparc_t5_m6, {"insts,,cycles,,c_stalls,,dcm", "c_stalls", 0}}, // no such processor + {CPC_SPARC_M6, sparc_t5_m6, {"insts,,cycles,,c_stalls,,dcm", "c_stalls", 0}}, + {CPC_SPARC_M7, sparc_m7, {"insts,,cycles,,c_stalls,,dcm", "c_stalls", 0}}, // includes T7 + {CPC_SPARC_M8, sparc_m8, {"insts,,cycles,,c_stalls,,dcm", "c_stalls", 0}}, + {CPC_PENTIUM_PRO_MMX, pentiumIIlist, {"insts", 0}}, + {CPC_PENTIUM_PRO, pentiumIIIlist, {"insts", 0}}, + {CPC_PENTIUM_4, pentium4, {"insts", 0}}, + {CPC_PENTIUM_4_HT, pentium4, {"insts", 0}}, + {CPC_INTEL_CORE2, intelCore2list, {"insts,,cycles", 0}}, + {CPC_INTEL_NEHALEM, intelNehalemList, {"insts,,cycles,,+l2m_latency,,dtlbm_stall", + "insts,,cycles,,l3m_stall,,dtlbm_stall", 0}}, + {CPC_INTEL_WESTMERE, intelNehalemList, {"insts,,cycles,,+l2m_latency,,dtlbm_stall", + "insts,,cycles,,l3m_stall,,dtlbm_stall", 0}}, + {CPC_INTEL_SANDYBRIDGE, intelSandyBridgeList, {"insts,,cycles,,+l2m_latency,,dtlbm_stall", + "insts,,cycles,,l3m,,dtlbm", 0}}, + {CPC_INTEL_IVYBRIDGE, intelSandyBridgeList, {"insts,,cycles,,+l2m_latency,,dtlbm_stall", + "insts,,cycles,,l3m,,dtlbm", 0}}, + {CPC_INTEL_HASWELL, intelHaswellList, {"insts,,cycles,,+l2m_latency,,dtlbm_stall", + "insts,,cycles,,l3m,,dtlbm", 0}}, + {CPC_INTEL_BROADWELL, intelBroadwellList, {"insts,,cycles,,+l2m_latency,,dtlbm", + "insts,,cycles,,l3m,,dtlbm", 0}}, + {CPC_INTEL_SKYLAKE, intelSkylakeList, {"insts,,cycles,,+l2m_latency,,dtlbm_stall", + "insts,,cycles,,l2m_stall,,dtlbm_stall", 0}}, + {CPC_INTEL_UNKNOWN, intelLinuxUnknown, {"cycles,,insts,,llm", + "user_time,,system_time,,cycles,,insts,,llm", 0}}, + {CPC_INTEL_ATOM, intelAtomList, {"insts", 0}}, + {CPC_AMD_K8C, amd_opteron_10h_11h, {"insts,,cycles,,l2dm,,l2dtlbm", 0}}, + {CPC_AMD_FAM_10H, amd_opteron_10h_11h, {"insts,,cycles,,l2dm,,l2dtlbm", 0}}, + {CPC_AMD_FAM_11H, amd_opteron_10h_11h, {"insts,,cycles,,l2dm,,l2dtlbm", 0}}, + {CPC_AMD_FAM_15H, amd_15h, {"insts,,cycles", 0}}, + {CPC_SPARC64_V, usfuji_V_list, {"insts,,cycles", 0}}, + {CPC_SPARC64_VI, usfuji_VI_VII_list, {"insts,,cycles,,dcstall", 0}}, + {CPC_SPARC64_VII, usfuji_VI_VII_list, {"insts,,cycles,,dcstall", 0}}, + {CPC_SPARC64_X, usfuji_X_list, {"insts,,cycles,,dcstall", 0}}, + {CPC_SPARC64_XII, usfuji_XII_list, {"insts,,cycles,,dcstall", 0}}, + {CPC_KPROF, kproflist, {NULL}}, // OBSOLETE (To support 12.3 and earlier, TBR) + {ARM_CPU_IMP_APM, armlist, {"insts,,cycles", 0}}, + {0, unknownlist, {NULL}} /* processor is unknown, but experiment is allowed */ +}; + +/*---------------------------------------------------------------------------*/ +/* state variables */ +static int initialized; +static int signals_disabled; + +// Simple array list +typedef struct +{ + void** array; // array of ptrs, last item set to null + int sz; // num live elements in array + int max; // array allocation size +} ptr_list; + +static void +ptr_list_init (ptr_list *lst) +{ + lst->sz = 0; + lst->max = 0; + lst->array = 0; +} + +static void +ptr_list_add (ptr_list *lst, char* ptr) +{ // ptr must be freeable + if (lst->sz >= lst->max - 1) + { + void * * new; + int newmax = lst->max ? lst->max * 2 : 16; + new = (void**) realloc (lst->array, newmax * sizeof (void*)); + if (!new) return; // failed, discard add + lst->max = newmax; + lst->array = new; + } + lst->array[lst->sz++] = ptr; + lst->array[lst->sz] = NULL; // mark new end-of-list +} + +static void +ptr_list_free (ptr_list *lst) +{ // includes shallow free of all elements + if (lst->array) + { + for (int ii = 0; lst->array[ii]; ii++) + free (lst->array[ii]); + free (lst->array); + } + lst->sz = 0; + lst->max = 0; + lst->array = 0; +} + +// Capabilities of this machine (initialized by setup_cpc()) +static int cpcx_cpuver = CPUVER_UNDEFINED; +static uint_t cpcx_npics; +static const char *cpcx_cciname; +static const char *cpcx_docref; +static uint64_t cpcx_support_bitmask; + +// cpcx_*[0]: collect lists +// cpcx_*[1]: er_kernel lists +// Each cpcx_*[] list is an array of ptrs with null ptr marking end of list +static char **cpcx_attrs[2]; + +static Hwcentry **cpcx_std[2]; +static Hwcentry **cpcx_raw[2]; +static Hwcentry **cpcx_hidden[2]; + +static uint_t cpcx_max_concurrent[2]; +static char *cpcx_default_hwcs[2]; +static char *cpcx_orig_default_hwcs[2]; +static int cpcx_has_precise[2]; + +#define VALID_FOR_KERNEL(forKernel) ((forKernel)>=0 && (forKernel)<=1) +#define IS_KERNEL(forKernel) ((forKernel)==1) + +// used to build lists: +static ptr_list unfiltered_attrs; +static ptr_list unfiltered_raw; + +/*---------------------------------------------------------------------------*/ +/* misc internal utilities */ + +/* compare 2 strings to either \0 or <termchar> */ +#define IS_EOL(currchar, termchar) ((currchar)==(termchar) || (currchar)==0) + +static int +is_same (const char * regname, const char * int_name, char termchar) +{ + do + { + char a = *regname; + char b = *int_name; + if (IS_EOL (a, termchar)) + { + if (IS_EOL (b, termchar)) + return 1; /* strings are the same up to terminating char */ + else + break; /* strings differ */ + } + if (a != b) + break; /* strings differ */ + regname++; + int_name++; + } + while (1); + return 0; +} + +static int +is_numeric (const char *name, uint64_t *pval) +{ + char *endptr; + uint64_t val = strtoull (name, &endptr, 0); + if (!*name || *endptr) + return 0; /* name does not specify a numeric value */ + if (pval) + *pval = val; + return 1; +} + +static int +is_visible_alias (Hwcentry* pctr) +{ + if (!pctr) + return 0; + if (pctr->name && pctr->int_name && pctr->metric) + return 1; + return 0; +} + +static int +is_hidden_alias (Hwcentry* pctr) +{ + if (!pctr) + return 0; + if (pctr->name && pctr->int_name && pctr->metric == NULL) + return 1; + return 0; +} + +static int +is_numeric_alias (Hwcentry* pctr) +{ + int is_numeric_alias = 0; + regno_t regno; + char *nameOnly = NULL; + hwcfuncs_parse_ctr (pctr->int_name, NULL, &nameOnly, NULL, NULL, ®no); + if (is_numeric (nameOnly, NULL)) + is_numeric_alias = 1; + free (nameOnly); + return is_numeric_alias; +} + +/* print list of register to a buffer */ +/* + * style e x a m p l e s + * 0 NONE 2 {0|1|2|3} + * 1 NONE 2 : 0, 1, 2, or 3 + * 2 0 1 2 3 6 + */ +static char * +get_regnolist (char *buf, size_t sz, const regno_t *reg_list, int style) +{ + if (!buf || !sz) + return "INTERNAL ERROR"; + buf[0] = 0; + if (style == 2) + { + int ii; + // width should be consistent with that in format_columns() + // the format will accommodate cpcx_npics regs + if (cpcx_npics < 1) + return "INTERNAL ERROR"; + // clear out the buffer + for (ii = 0; ii < sz; ii++) + buf[ii] = '_'; + if (cpcx_npics <= 9) + { + // one char per reg, plus terminating null char + if (cpcx_npics + 1 > sz) + return "INTERNAL ERROR"; + buf[cpcx_npics] = '\0'; + + // fill buf with regnos + for (ii = 0; ii < MAX_PICS; ii++) + { + regno_t regno = reg_list[ii]; + if (REG_LIST_EOL (regno)) + break; + if (regno < 0 || regno >= cpcx_npics) + return "INTERNAL ERROR"; + buf[regno] = '0' + regno; + } + } + else + { + /* space between regs, which may be 1 or 2 digits each + * 1 char for reg 0 + * 2 chars for regs 1-9 each + * 3 chars for regs 10- each + * 1 char for terminating null char + */ + int nchars = 17 + 3 * (cpcx_npics - 9); + if (nchars > sz) + return "INTERNAL ERROR"; + buf[nchars - 1] = '\0'; + + // fill buf with regnos + for (ii = 0; ii < MAX_PICS; ii++) + { + regno_t regno = reg_list[ii]; + if (REG_LIST_EOL (regno)) + break; + if (regno <= 9) + buf[2 * regno ] = '0' + regno; + else + { + buf[3 * (regno - 9) + 17] = '0' + (regno / 10); + buf[3 * (regno - 9) + 18] = '0' + (regno % 10); + } + } + } + return buf; + } + if (REG_LIST_IS_EMPTY (reg_list)) + { + snprintf (buf, sz, GTXT ("NONE")); + return buf; + } + else if (REG_LIST_EOL (reg_list[1])) + { + /* 1 item in list */ + snprintf (buf, sz, "%d", reg_list[0]); + return buf; + } + else + { + /* 2 more items in list */ + int ii, num_regs; + for (ii = 0; ii < MAX_PICS; ii++) + { + regno_t regno = reg_list[ii]; + if (REG_LIST_EOL (regno)) + break; + } + num_regs = ii; + buf[0] = 0; + for (ii = 0; ii < num_regs; ii++) + { + regno_t regno = reg_list[ii]; + if (style == 0) + snprintf (buf + strlen (buf), sz - strlen (buf), + "%c%d", ii ? '|' : '{', regno); + else + { + if (num_regs == 2) + snprintf (buf + strlen (buf), sz - strlen (buf), + "%d%s", regno, !ii ? " or " : ""); + else + { + /* 3 or more items in list */ + if (ii < num_regs - 2) + snprintf (buf + strlen (buf), sz - strlen (buf), + "%d, ", regno); + else if (ii == num_regs - 2) + snprintf (buf + strlen (buf), sz - strlen (buf), + "%d, or ", regno); + else + snprintf (buf + strlen (buf), sz - strlen (buf), + "%d", regno); + } + } + } + if (style == 0) + snprintf (buf + strlen (buf), sz - strlen (buf), "}"); + } + return buf; +} + +#if !HWC_DEBUG +#define hwcentry_print(lvl,x1,x2) +#else + +/* print a Hwcentry */ +static void +hwcentry_print (int lvl, const char * header, const Hwcentry *pentry) +{ + char buf[1024]; + Tprintf (lvl, "%s '%s', '%s', %d, '%s', %d, %d, %d, %d, %d, %d, /", + header, + pentry->name ? pentry->name : "NULL", + pentry->int_name ? pentry->int_name : "NULL", + pentry->reg_num, + pentry->metric ? pentry->metric : "NULL", + pentry->lval, /* low-resolution/long run */ + pentry->val, /* normal */ + pentry->hval, /* high-resolution/short run */ + pentry->timecvt, + pentry->memop, /* type of instruction that can trigger */ + pentry->sort_order); + get_regnolist (buf, sizeof (buf), pentry->reg_list, 0); + Tprintf (lvl, "%s\n", buf); +} +#endif + +/* add <regno> to a Hwcentry's list */ +static void +regno_add (Hwcentry * pctr, regno_t regno) +{ + int jj; + regno_t *reg_list; + if (!pctr) + { + Tprintf (0, "hwctable: regno_add(): ERROR: pctr==NULL\n"); + return; + } + reg_list = pctr->reg_list; + if (!reg_list) + { + /* create list */ + reg_list = (regno_t*) malloc (sizeof (regno_t*) * MAX_PICS); + if (!reg_list) + { + hwcentry_print (DBG_LT0, "hwctable: regno_add: ERROR:" + " Out of memory: ", pctr); + return; + } + /* initialize list */ + for (jj = 0; jj < MAX_PICS; jj++) + reg_list[jj] = REGNO_ANY; + pctr->reg_list = reg_list; + } + if (regno == REGNO_ANY) + { + /* add all counters up to cpcx_npics */ + for (jj = 0; jj < MAX_PICS && jj < cpcx_npics; jj++) + reg_list[jj] = jj; + } + else + { + /* add <regno> to list of registers */ + for (jj = 0; jj < MAX_PICS; jj++) + { + if (reg_list[jj] == regno) + { + hwcentry_print (DBG_LT0, "hwctable: regno_add: WARNING: " + "Duplicate regno: ", pctr); + break; + } + if (reg_list[jj] == REGNO_ANY) + { + reg_list[jj] = regno; + break; + } + } + } + if (jj == MAX_PICS) + hwcentry_print (DBG_LT0, "hwctable: regno_add: WARNING:" + " regno list is full:", pctr); +} + +/*---------------------------------------------------------------------------*/ +/* utilities for rawlist (list of raw counters with reglist[] filled in) */ + +/* search the 'raw' list of counters for <name> */ +static Hwcentry * +ptrarray_find_by_name (Hwcentry** array, const char * name) +{ + if (name == NULL) + return NULL; + Tprintf (DBG_LT3, "hwctable: array_find_by_name(%s):\n", name); + for (int ii = 0; array && array[ii]; ii++) + if (strcmp (array[ii]->name, name) == 0) + return array[ii]; + return NULL; /* not found */ +} + +/* add Hwcentry to the 'raw' list of counters */ +static Hwcentry * +alloc_shallow_copy (const Hwcentry *pctr) +{ + Hwcentry *node = (Hwcentry *) malloc (sizeof (Hwcentry)); + if (!node) + return NULL; // fail + *node = *pctr; /* shallow copy! */ + if (pctr->name) + node->name = strdup (pctr->name); + return node; +} + +/* add Hwcentry to the 'raw' list of counters */ +static Hwcentry * +list_append_shallow_copy (ptr_list *list, const Hwcentry *pctr) +{ + Hwcentry *node = alloc_shallow_copy (pctr); + if (!node) + return NULL; // fail + ptr_list_add (list, (void*) node); + return node; +} + +static Hwcentry * +list_add (ptr_list *list, uint_t regno, const char *name) +{ + Hwcentry *praw; + praw = ptrarray_find_by_name ((Hwcentry**) list->array, name); + if (!praw) + { + Hwcentry tmpctr = empty_ctr; + tmpctr.name = (char *) name; + praw = list_append_shallow_copy (list, &tmpctr); + } + if (praw) + regno_add (praw, regno); + return praw; +} + +/*---------------------------------------------------------------------------*/ +/* utilities for stdlist (table of aliased, hidden, & convenience, ctrs) */ + +/* find top level definition for <cpuid> */ +static cpu_list_t* +cputabs_find_entry (int cpuid) +{ + int i; + /* now search for the appropriate table */ + for (i = 0;; i++) + { + if (cputabs[i].cputag == 0) + break; + if (cpuid == cputabs[i].cputag) + return &cputabs[i]; + } + Tprintf (0, "hwctable: cputabs_find_entry: WARNING: " + "cpu_id = %d not defined. No 'standard' counters are available\n", + cpuid); + return &cputabs[i]; +} + +/* find Hwcentry table for <cpuid> */ +static Hwcentry* +stdlist_get_table (int cpuid) +{ + cpu_list_t* tmp = cputabs_find_entry (cpuid); + if (tmp) + return tmp->stdlist_table; + return NULL; +} + +/* search the 'standard' list of counters for <name>,<regno> */ +/* note: <regno>=REGNO_ANY is a wildcard that matches any value. */ + +/* note: int_name==NULL is a wildcard */ +static const Hwcentry * +ptrarray_find (const Hwcentry **array, const char *name, const char *int_name, + int check_regno, regno_t regno) +{ + const Hwcentry *pctr; + if (!array) + return NULL; + for (int ii = 0; array[ii]; ii++) + { + pctr = array[ii]; + if (strcmp (pctr->name, name)) + continue; + if (int_name && int_name[0] != 0 && pctr->int_name) + { + if (NULL == strstr (int_name, pctr->int_name)) + continue; + } + if (!check_regno) + return pctr; + else + { + /* duplicates aliases are allowed in table because of 6759307 */ + if (REG_LIST_IS_EMPTY (pctr->reg_list)) + { + /* skip aliases that don't have a valid list of registers */ + hwcentry_print (1, "hwctable: stdlist_find_by_name:" + " WARNING: alias found, but event not supported by HW:", + pctr); + continue; + } + if (!regno_is_valid (pctr, regno)) + { + hwcentry_print (1, "hwctable: stdlist_find_by_name():" + " WARNING: alias found, but regno doesn't match:", + pctr); + continue; + } + return pctr; + } + } + return NULL; +} + +/* search the 'standard' list of counters for <name>,<regno> */ + +/* note: <regno>=REGNO_ANY is a wildcard that matches any value. */ +static const Hwcentry * +static_table_find (const Hwcentry *table, const char *name, const char *int_name, + int check_regno, regno_t regno) +{ + int sz; + for (sz = 0; table && table[sz].name; sz++) + ; + if (!sz) + return NULL; + const Hwcentry ** list = calloc (sz + 1, sizeof (void*)); + if (!list) + return NULL; + for (int ii = 0; ii < sz; ii++) + list[ii] = &table[ii]; + list[sz] = NULL; + const Hwcentry *pctr = ptrarray_find (list, name, int_name, check_regno, regno); + free (list); + return pctr; +} + +#if !HWC_DEBUG +#define stdlist_print(dbg_lvl,table) +#else + +/* print all Hwcentries in standard table. Check for weird stuff */ +static void +stdlist_print (int dbg_lvl, const Hwcentry* table) +{ + const Hwcentry *pctr; + if (!table) + { + Tprintf (0, "hwctable: stdlist_print: ERROR: " + "table is invalid.\n"); + return; + } + for (pctr = table; pctr->name; pctr++) + { + int ii; + hwcentry_print (dbg_lvl, "hwctable: stdlist: ", pctr); + if (REG_LIST_IS_EMPTY (pctr->reg_list)) + { + if (pctr->int_name || !pctr->metric) + hwcentry_print (DBG_LT1, "hwctable: stdlist_print: WARNING: " + "no hardware event found for table entry", pctr); + continue; + } + /* check if incorrect reg_num used in table */ + if (!regno_is_valid (pctr, pctr->reg_num)) + { + hwcentry_print (DBG_LT0, "hwctable: stdlist_print: ERROR: " + "reg_num is not in table. ", pctr); + continue; + } + for (ii = 0; ii < MAX_PICS; ii++) + { + regno_t regno = pctr->reg_list[ii]; + if (REG_LIST_EOL (regno)) + break; + } + if (ii > 1 && pctr->reg_num != REGNO_ANY) + { + /* several regnos were valid, but only one can be specified */ + if (pctr->metric || !pctr->int_name) + { + /* pctr is standard or a raw definition */ + /* (pctr is not an alias like cycles0) */ + hwcentry_print (DBG_LT0, "hwctable: stdlist_print: ERROR: " + "regno in table should have been REGNO_ANY. ", + pctr); + } + } + } +} +#endif + +/*---------------------------------------------------------------------------*/ +/* utilities for init */ + +/* try to bind counters to hw. Return 0 on success, nonzero otherwise */ +static int +test_hwcs (const Hwcentry* entries[], unsigned numctrs) +{ + int rc = -1; + hwc_event_t sample; + int created = 0; + hwcdrv_api_t *hwcdrv = get_hwcdrv (); + Tprintf (DBG_LT2, "hwctable: test_hwcs()...\n"); + rc = hwcfuncs_bind_hwcentry (entries, numctrs); + if (rc) + { + Tprintf (0, "hwctable: WARNING: test " + "counters could not be created\n"); + goto end_test_hwcs; + } + created = 1; + if (!signals_disabled) + { + (void) signal (HWCFUNCS_SIGNAL, SIG_IGN); + signals_disabled = 1; + } + rc = hwcdrv->hwcdrv_start (); + if (rc) + { + Tprintf (0, "hwctable: WARNING: test " + "counters could not be started\n"); + goto end_test_hwcs; + } + rc = hwcdrv->hwcdrv_read_events (&sample, NULL); + if (rc) + Tprintf (0, "hwctable: WARNING: test sample failed\n"); + rc = 0; +#if HWC_DEBUG + { + unsigned ii; + Tprintf (DBG_LT1, "hwctable: test_hwcs("); + for (ii = 0; ii < numctrs; ii++) + Tprintf (DBG_LT1, "%s%s", ii ? "," : "", entries[ii]->name); + Tprintf (DBG_LT1, ") PASS\n"); + } +#endif + +end_test_hwcs: + if (created && hwcdrv->hwcdrv_free_counters ()) + Tprintf (0, "hwctable: WARNING: test counters could not be freed\n"); + return rc; +} + +#if !HWC_DEBUG +#define check_tables() +#else + +/* check for typos in tables */ +static void +check_tables () +{ + int i; + /* now search the known table of counters */ + for (i = 0;; i++) + { + Hwcentry * pentry; + int cputag = cputabs[i].cputag; + if (cputag == 0) + break; + if (cputag == CPC_KPROF) + continue; + pentry = cputabs[i].stdlist_table; + for (; pentry; pentry++) + { + if (!pentry->name) + break; + if (!pentry->int_name) + {/* internal, only to supply ABST and timecvt */ + if (pentry->metric) + Tprintf (DBG_LT0, "hwctable: check_tables: ERROR:" + " internal && metric @%d, %s\n", cputag, pentry->name); + if (pentry->reg_num != REGNO_ANY) + Tprintf (DBG_LT1, "hwctable: check_tables: WARNING:" + " internal && reg_num!=REGNO_ANY @%d, %s\n", + cputag, pentry->name); + if (pentry->val != PRELOAD_DEF + && pentry->memop != ABST_EXACT_PEBS_PLUS1) + Tprintf (DBG_LT2, "hwctable: check_tables: INFO:" + " internal && custom val=%d @%d, %s\n", + pentry->val, cputag, pentry->name); +#if 0 + if (!pentry->timecvt && pentry->memop == ABST_NONE) + Tprintf (DBG_LT0, "hwctable: check_tables: ERROR:" + " internal && not special! @%d, %s\n", + cputag, pentry->name); +#endif + } + if (pentry->metric) + { /* aliased */ + if (!pentry->int_name) + Tprintf (DBG_LT0, "hwctable: check_tables: ERROR:" + " aliased && !int_name @%d, %s\n", cputag, pentry->name); +#if 0 + else if (!strcmp (pentry->name, pentry->int_name)) + Tprintf (DBG_LT0, "hwctable: check_tables: ERROR:" + " name==int_name @%d, %s\n", + cputag, pentry->name); +#endif + if (pentry->reg_num != REGNO_ANY && pentry->reg_num != REGNO_INVALID) + Tprintf (DBG_LT1, "hwctable: check_tables: INFO:" + " aliased && custom reg_num==%d @%d, %s\n", + pentry->reg_num, cputag, pentry->name); + if (pentry->reg_num == REGNO_INVALID) + Tprintf (DBG_LT2, "hwctable: check_tables: INFO:" + " aliased && reg_num==REGNO_INVALID @%d, %s\n", + cputag, pentry->name); + } + if (pentry->int_name && !pentry->metric) + { /* convenience */ + if (!strcmp (pentry->name, pentry->int_name)) + Tprintf (DBG_LT0, "hwctable: check_tables: ERROR:" + " convenience && name==int_name @%d, %s\n", + cputag, pentry->name); + if (pentry->reg_num == REGNO_ANY) + Tprintf (DBG_LT0, "hwctable: check_tables: ERROR:" + " convenience && reg_num==REGNO_ANY @%d, %s\n", + cputag, pentry->name); + } + } + } +} +#endif + +static int try_a_counter (); +static void hwc_process_raw_ctrs (int forKernel, Hwcentry ***pstd_out, + Hwcentry ***praw_out, Hwcentry ***phidden_out, + Hwcentry**static_tables, + Hwcentry **raw_unfiltered_in); + +/* internal call to initialize libs, ctr tables */ +static void +setup_cpc_general (int skip_hwc_test) +{ + const cpu_list_t* cputabs_entry; + int rc = -1; + Tprintf (DBG_LT2, "hwctable: setup_cpc()... \n"); + if (initialized) + { + Tprintf (0, "hwctable: WARNING: setup_cpc() has already been called\n"); + return; + } + initialized = 1; + cpcx_cpuver = CPUVER_UNDEFINED; + cpcx_cciname = NULL; + cpcx_npics = 0; + cpcx_docref = NULL; + cpcx_support_bitmask = 0; + for (int kk = 0; kk < 2; kk++) + { // collect-0 and kernel-1 + cpcx_attrs[kk] = NULL; + cpcx_std[kk] = NULL; + cpcx_raw[kk] = NULL; + cpcx_hidden[kk] = NULL; + cpcx_max_concurrent[kk] = 0; + cpcx_default_hwcs[kk] = NULL; + cpcx_orig_default_hwcs[kk] = NULL; + cpcx_has_precise[kk] = 0; + } + check_tables (); + hwcdrv_api_t *hwcdrv = get_hwcdrv (); + if (hwcdrv->hwcdrv_init_status) + { + Tprintf (0, "WARNING: setup_cpc_general() failed. init_status=%d \n", + hwcdrv->hwcdrv_init_status); + goto setup_cpc_wrapup; + } + hwcdrv->hwcdrv_get_info (&cpcx_cpuver, &cpcx_cciname, &cpcx_npics, + &cpcx_docref, &cpcx_support_bitmask); + +#ifdef DISALLOW_USI_USII_6357446 + if (cpcx_cpuver == CPC_ULTRA1 || cpcx_cpuver == CPC_ULTRA2) + { + Tprintf (0, "hwctable: WARNING: setup_cpc(): cpu=%d" + " US-I/US-II cannot provide profile interrupts\n", cpcx_cpuver); + /* profiling interrupts don't work on US-I, US-II */ + hwcfuncs_int_logerr (GTXT ("UltraSPARC I and II cannot provide overflow interrupts\n")); + goto setup_cpc_wrapup; + } +#endif + +#ifdef DISALLOW_PENTIUM_PRO_MMX_7007575 + if (cpcx_cpuver == CPC_PENTIUM_PRO_MMX) + { + Tprintf (0, "hwctable: WARNING: setup_cpc(): cpu=%d" + " `Pentium Pro with MMX, Pentium II' is not supported\n", cpcx_cpuver); + hwcfuncs_int_logerr (GTXT ("libcpc cannot identify processor type\n")); + goto setup_cpc_wrapup; + } +#endif + + /* now search the known table of counters */ + cputabs_entry = cputabs_find_entry (cpcx_cpuver); + if (cputabs_entry == NULL) + { + Tprintf (0, "hwctable: WARNING: setup_cpc(): cpu=%d" + " could not be found in the tables\n", cpcx_cpuver); + /* strange, should have at least selected "unknownlist" */ + hwcfuncs_int_logerr (GTXT ("Analyzer CPU table could not be found\n")); + goto setup_cpc_wrapup; + } + + Hwcentry * valid_cpu_tables[2]; // [0]:static table of counters, [1]:static table of generic counters + valid_cpu_tables[0] = cputabs_entry->stdlist_table; + if (valid_cpu_tables[0] == NULL) + { + Tprintf (0, "hwctable: WARNING: setup_cpc(): " + " valid_cpu_tables was NULL??\n"); + /* strange, someone put a NULL in the lookup table? */ + hwcfuncs_int_logerr (GTXT ("Analyzer CPU table is invalid\n")); + goto setup_cpc_wrapup; + } + valid_cpu_tables[1] = papi_generic_list; + Tprintf (DBG_LT2, "hwctable: setup_cpc(): getting descriptions \n"); + // populate cpcx_raw and cpcx_attr + hwcdrv->hwcdrv_get_descriptions (hwc_cb, attrs_cb); + for (int kk = 0; kk < 2; kk++) + { // collect and er_kernel + hwc_process_raw_ctrs (kk, &cpcx_std[kk], &cpcx_raw[kk], &cpcx_hidden[kk], + valid_cpu_tables, (Hwcentry**) unfiltered_raw.array); + cpcx_has_precise[kk] = 0; + for (int rr = 0; cpcx_raw[kk] && cpcx_raw[kk][rr]; rr++) + { + int memop = cpcx_raw[kk][rr]->memop; + if (ABST_MEMSPACE_ENABLED (memop)) + { + cpcx_has_precise[kk] = 1; + break; + } + } + cpcx_attrs[kk] = (char**) unfiltered_attrs.array; + cpcx_max_concurrent[kk] = cpcx_npics; + } +#if 1 // 22897042 - DTrace cpc provider does not support profiling on multiple ctrs on some systems + if ((cpcx_support_bitmask & HWCFUNCS_SUPPORT_OVERFLOW_CTR_ID) != HWCFUNCS_SUPPORT_OVERFLOW_CTR_ID) + { + // kernel profiling only supports one counter if overflowing counter can't be identified + cpcx_max_concurrent[1] = cpcx_npics ? 1 : 0; + } +#endif + + /* --- quick test of the cpc interface --- */ + if (skip_hwc_test) + rc = 0; + else + rc = try_a_counter (0); + + /* initialize the default counter string definition */ + for (int kk = 0; kk < 2; kk++) + { + char * default_exp = 0; + int jj; + for (jj = 0; (default_exp = cputabs_entry->default_exp_p[jj]); jj++) + { + int rc = hwc_lookup (kk, 0, default_exp, NULL, 0, NULL, NULL); + if (rc > 0) + break; + } + if (!default_exp) + { + char * fallback[3] = {NTXT ("insts,,cycles,,l3m"), NTXT ("insts,,cycles"), NTXT ("insts")}; + for (int ff = 0; ff < 3; ff++) + { + int rc = hwc_lookup (kk, 0, fallback[ff], NULL, 0, NULL, NULL); + if (rc > 0) + { + default_exp = strdup (fallback[ff]); + break; + } + } + } + cpcx_default_hwcs[kk] = default_exp; + cpcx_orig_default_hwcs[kk] = default_exp; + } + +setup_cpc_wrapup: + if (rc) + { + cpcx_npics = 0; + /* + ptr_list_free(&tmp_raw); // free stuff... YXXX + ptr_list_free(&unfiltered_attrs); + */ + } + return; +} + +static void +setup_cpcx () +{ + if (initialized) + return; + setup_cpc_general (0); // set up and include a hwc test run +} + +static void +setup_cpc_skip_hwctest () +{ + if (initialized) + return; + setup_cpc_general (1); // set up but skip hwc test run +} + +static int +try_a_counter (int forKernel) +{ + if (!VALID_FOR_KERNEL (forKernel)) + return -1; + int rc = -1; + const Hwcentry * testevent; + if (cpcx_std[forKernel] == NULL) + { + Tprintf (0, "hwctable: WARNING: cpcx_std not initialized"); + return 0; /* consider this an automatic PASS */ + } + /* look for a valid table entry, only try valid_cpu_tables[0] */ + { + testevent = cpcx_std[forKernel][0]; + if (!testevent || !testevent->name) + { + Tprintf (0, "hwctable: WARNING: no test metric" + " available to verify counters\n"); + return 0; /* consider this an automatic PASS */ + } + if (REG_LIST_IS_EMPTY (testevent->reg_list)) + return 0; // weird + } + Hwcentry tmp_testevent; + tmp_testevent = *testevent; /* shallow copy */ + if (tmp_testevent.int_name == NULL) + { + /* counter is defined in 'hidden' section of table, supply int_name */ + tmp_testevent.int_name = strdup (tmp_testevent.name); + } + Hwcentry * test_array[1] = {&tmp_testevent}; + rc = hwcfuncs_assign_regnos (test_array, 1); /* may modify test_array */ + if (rc) + return rc; + rc = test_hwcs ((const Hwcentry**) test_array, 1); + if (rc == HWCFUNCS_ERROR_UNAVAIL) + { + // consider this a pass (allow HWC table to be printed) + Tprintf (0, "hwctable: WARNING: " + "cpc_bind_event() shows counters busy; allow to continue\n"); + return 0; + } + else if (rc) + { + // failed to start for some other reason + Tprintf (0, "hwctable: WARNING: " + "test of counter '%s' failed\n", + testevent->name); + return rc; + } + return 0; +} + +void +hwc_update_val (Hwcentry *hwc) +{ + if (hwc->ref_val == 0) + hwc->ref_val = hwc->val; // save original reference + int64_t newVal; + hrtime_t min_time_nsec = hwc->min_time; + if (min_time_nsec == HWCTIME_TBD) + min_time_nsec = hwc->min_time_default; + switch (min_time_nsec) + { + case 0: // disable time-based intervals + // do not modify val + return; + case HWCTIME_ON: + case HWCTIME_TBD: + newVal = HWC_VAL_ON (hwc->ref_val); + break; + case HWCTIME_LO: + newVal = HWC_VAL_LO (hwc->ref_val); + break; + case HWCTIME_HI: + newVal = HWC_VAL_HI (hwc->ref_val); + break; + default: + newVal = HWC_VAL_CUSTOM (hwc->ref_val, min_time_nsec); + break; + } +#define MAX_INT_VAL (2*1000*1000*1000 + 1000100)// yuck, limited to signed int + if (newVal >= MAX_INT_VAL) + newVal = MAX_INT_VAL; + hwc->val = newVal; +} + +/* convert value string to value and store result in hwc->val */ +/* This function moved here from collctrl.cc */ +/* + * Keep the HWCTIME_* definitions in sync with those in + * collctrl.cc Coll_Ctrl::add_hwcstring(). + */ +static int +set_hwcval (Hwcentry *hwc, hrtime_t global_min_time_nsec, const char *valptr) +{ + hwc->min_time_default = global_min_time_nsec; + if (hwc->val == 1) + { + // An interval of 1 is used for certain types of count data. + // (er_bit, er_generic, er_rock ...) + // Hi and Lo do not apply. + /* use the default */ + } + else if (valptr == NULL || valptr[0] == 0 || strcmp (valptr, "auto") == 0) + hwc->min_time = HWCTIME_TBD; + else if (strcmp (valptr, "on") == 0) + hwc->min_time = HWCTIME_ON; + else if (strcmp (valptr, "lo") == 0 || strcmp (valptr, "low") == 0) + hwc->min_time = HWCTIME_LO; + else if (strcmp (valptr, "hi") == 0 || strcmp (valptr, "high") == 0 + || strcmp (valptr, "h") == 0) + hwc->min_time = HWCTIME_HI; + else + { + /* the remaining string should be a number > 0 */ + char *endchar = NULL; + long long tmp = strtoll (valptr, &endchar, 0); + int value = (int) tmp; + if (*endchar != 0 || tmp <= 0 || value != tmp) + { + // also covers errno == ERANGE + Tprintf (0, "hwctable: set_hwcval(): ERROR: " + "Invalid counter value %s for counter `%s'\n", + valptr, hwc->name); + return -1; + } + if (tmp > UINT32_MAX / 2) + { + /* Roch B. says that we MUST do this check for er_kernel + because some platforms deliver overflow interrupts without + identifying which counter overflowed. The only way to + determine which counter overflowed is to have enough + margin on 32 bit counters to make sure they don't + wrap. + */ + Tprintf (0, "hwctable: set_hwcval(): ERROR: " + "Counter value %s exceeds %lu\n", + valptr, (unsigned long) UINT32_MAX / 2); + return -1; + } + /* set the value */ + if (value != 0) + { + if (hwc->ref_val == 0) + hwc->ref_val = hwc->val; // save original reference + hwc->val = value; + hwc->min_time = 0; // turn off auto-adjust + } + } + hwc_update_val (hwc); + return 0; +} + +static char * +canonical_name (const char *counter) +{ + char *nameOnly = NULL; + char *attrs = NULL; + char tmpbuf[1024]; + tmpbuf[0] = 0; + hwcfuncs_parse_ctr (counter, NULL, &nameOnly, &attrs, NULL, NULL); + snprintf (tmpbuf + strlen (tmpbuf), sizeof (tmpbuf) - strlen (tmpbuf), + "%s", nameOnly); + if (attrs) + { + hwcfuncs_attr_t cpc2_attrs[HWCFUNCS_MAX_ATTRS]; + void * attr_mem; + unsigned nattrs; + int ii, jj; + + /* extract attributes from counter */ + attr_mem = hwcfuncs_parse_attrs (counter, cpc2_attrs, HWCFUNCS_MAX_ATTRS, + &nattrs, NULL); + if (!attr_mem) + { + snprintf (tmpbuf + strlen (tmpbuf), sizeof (tmpbuf) - strlen (tmpbuf), + "~UNKNOWN"); + goto canonical_attrs_wrapup; + } + + /* sort the attributes */ + for (ii = 0; ii < (int) nattrs - 1; ii++) + { + for (jj = ii + 1; jj < nattrs; jj++) + { + int cmp = strcmp (cpc2_attrs[ii].ca_name, + cpc2_attrs[jj].ca_name); + if (cmp > 0) + { + hwcfuncs_attr_t tmp = cpc2_attrs[jj]; + cpc2_attrs[jj] = cpc2_attrs[ii]; + cpc2_attrs[ii] = tmp; + } + } + } + + /* print attributes in canonical format */ + for (ii = 0; ii < nattrs; ii++) + snprintf (tmpbuf + strlen (tmpbuf), sizeof (tmpbuf) - strlen (tmpbuf), + "~%s=0x%llx", cpc2_attrs[ii].ca_name, (long long) cpc2_attrs[ii].ca_val); + free (attr_mem); + } +canonical_attrs_wrapup: + free (nameOnly); + free (attrs); + return strdup (tmpbuf); +} + +/* process counter and value strings - put results in <*pret_ctr> */ + +/* Print errors to UEbuf for any failure that results in nonzero return */ +static int +process_ctr_def (int forKernel, hrtime_t global_min_time_nsec, + const char *counter, const char *value, Hwcentry *pret_ctr, + char* UWbuf, size_t UWsz, char* UEbuf, size_t UEsz) +{ + int rc = -1; + char *nameOnly = NULL; + char *attrs = NULL; + char *regstr = NULL; + int plus; + regno_t regno; + const Hwcentry *pfound = NULL; + const char *uname = NULL; + int disable_backtrack; + UEbuf[0] = 0; + UWbuf[0] = 0; + Tprintf (DBG_LT3, "hwctable: process_ctr_def(): counter=%s value=%s \n", + counter, value ? value : "NULL"); + hwcfuncs_parse_ctr (counter, &plus, &nameOnly, &attrs, ®str, ®no); + + /* search for the counter in the std and raw lists */ + { + pfound = ptrarray_find ((const Hwcentry**) cpcx_std[forKernel], nameOnly, NULL, 1, regno); + if (pfound) + hwcentry_print (DBG_LT1, "hwctable: process_ctr_def: found in stdlist:", + pfound); + } + if (!pfound) + { + pfound = ptrarray_find ((const Hwcentry**) cpcx_hidden[forKernel], nameOnly, NULL, 1, regno); + if (pfound) + hwcentry_print (DBG_LT1, "hwctable: process_ctr_def: found in stdlist(hidden):", pfound); + } + if (!pfound) + { + pfound = ptrarray_find_by_name (cpcx_raw[forKernel], nameOnly); /* (regno match checked later) */ + if (pfound) + hwcentry_print (DBG_LT1, "hwctable: process_ctr_def: found in rawlist:", pfound); + } + if (!pfound) + { + pfound = ptrarray_find ((const Hwcentry**) cpcx_std[forKernel], nameOnly, NULL, 1, REGNO_ANY); + if (pfound) + hwcentry_print (DBG_LT1, "hwctable: process_ctr_def: found in stdlist but regno didn't match:", pfound); + } + if (!pfound) + { + pfound = ptrarray_find ((const Hwcentry**) cpcx_hidden[forKernel], nameOnly, NULL, 1, REGNO_ANY); + if (pfound) + hwcentry_print (DBG_LT1, "hwctable: process_ctr_def: found in stdlist(hidden) but regno didn't match:", pfound); + } + if (!pfound) + { + uint64_t val = 0; + if (is_numeric (nameOnly, &val)) + { + Hwcentry *tmp = alloc_shallow_copy (&empty_ctr); // Leaks? + if (tmp) + { + tmp->name = strdup (nameOnly); + regno_add (tmp, REGNO_ANY); + pfound = tmp; + } + } + if (pfound) + hwcentry_print (DBG_LT1, "hwctable: process_ctr_def: counter specified by numeric value:", pfound); + } + if (!pfound) + { + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + GTXT ("Invalid HW counter name: %s\n"), nameOnly); + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + GTXT ("Run \"%s -h\" with no other arguments for more information on HW counters on this system.\n"), + (IS_KERNEL (forKernel) ? "er_kernel" : "collect")); + goto process_ctr_def_wrapup; + } + + /* counter found */ + *pret_ctr = *pfound; /* shallow copy */ + pret_ctr->int_name = NULL; /* so free doesn't try to free these pfound's ptrs */ + pret_ctr->name = NULL; /* so free doesn't try to free these pfound's ptrs */ + + /* update uname,memop */ + uname = counter; + disable_backtrack = 0; + if (plus != 0 || ABST_PLUS_BY_DEFAULT (pret_ctr->memop)) + { + // attempt to process memoryspace profiling + int message_printed = 0; + if (cpcx_cpuver == CPUVER_GENERIC) + { + // accept plus, since we don't know what this CPU is + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + GTXT ("`+' may not be correctly supported on `%s' because processor is not recognized."), + cpcx_cciname); + pret_ctr->memop = ABST_LDST; // supply a backtracking data type - required for collector + } + else if (cpcx_cpuver == CPC_ULTRA1 || cpcx_cpuver == CPC_ULTRA2 + || cpcx_cpuver == CPC_ULTRA3 || cpcx_cpuver == CPC_ULTRA3_PLUS + || cpcx_cpuver == CPC_ULTRA3_I || cpcx_cpuver == CPC_ULTRA4_PLUS + || cpcx_cpuver == CPC_ULTRA4 || cpcx_cpuver == CPC_ULTRA_T1 + || cpcx_cpuver == CPC_ULTRA_T2 || cpcx_cpuver == CPC_ULTRA_T2P + || cpcx_cpuver == CPC_ULTRA_T3) + { + if (!ABST_BACKTRACK_ENABLED (pret_ctr->memop)) + disable_backtrack = 1; + } + else if (cpcx_cpuver == CPC_SPARC_T4 || cpcx_cpuver == CPC_SPARC_T5 + || cpcx_cpuver == CPC_SPARC_T6 || cpcx_cpuver == CPC_SPARC_M4 + || cpcx_cpuver == CPC_SPARC_M5 || cpcx_cpuver == CPC_SPARC_M6 + || cpcx_cpuver == CPC_SPARC_M7 || cpcx_cpuver == CPC_SPARC_M8) + { + if (pret_ctr->memop != ABST_EXACT) + disable_backtrack = 1; + } + else if (cpcx_cpuver == CPC_INTEL_NEHALEM || cpcx_cpuver == CPC_INTEL_WESTMERE + || cpcx_cpuver == CPC_INTEL_SANDYBRIDGE + || cpcx_cpuver == CPC_INTEL_IVYBRIDGE + || cpcx_cpuver == CPC_INTEL_HASWELL + || cpcx_cpuver == CPC_INTEL_BROADWELL + || cpcx_cpuver == CPC_INTEL_SKYLAKE) + { + if (pret_ctr->memop != ABST_EXACT_PEBS_PLUS1) + disable_backtrack = 1; + else if (plus < 0) + { + // disabling memoryspace not supported for + // remove specified - + uname++; + plus = 0; + snprintf (UWbuf + strlen (UWbuf), UWsz - strlen (UWbuf), + GTXT ("Warning: `-' is not supported on `%s' -- memory reference backtracking will remain enabled for this counter\n"), + nameOnly); + } + } + else + { + message_printed = 1; + snprintf (UWbuf + strlen (UWbuf), UWsz - strlen (UWbuf), + GTXT ("Warning: `+' is not supported on `%s' -- memory reference backtracking will not be enabled for `%s'\n"), + cpcx_cciname, nameOnly); + disable_backtrack = 1; + } + if (disable_backtrack) + { + if (plus != 0) + uname++; // remove specified + or - + if (!message_printed && plus > 0) + snprintf (UWbuf + strlen (UWbuf), UWsz - strlen (UWbuf), + GTXT ("Warning: `+' is not supported on `%s' -- memory reference backtracking will not be enabled for this counter\n"), + nameOnly); + } + } + else + disable_backtrack = 1; + if (disable_backtrack || plus < 0) + if (pret_ctr->memop != ABST_NOPC) + pret_ctr->memop = ABST_NONE; + if (pret_ctr->memop == ABST_NOPC) + snprintf (UWbuf + strlen (UWbuf), UWsz - strlen (UWbuf), + GTXT ("Warning: HW counter `%s' is not program-related -- callstacks will be not be recorded for this counter\n"), + uname); + + /* update reg_num */ + if (!regno_is_valid (pfound, regno)) + { + char buf[1024]; + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + GTXT ("For counter `%s', %s is not a valid register; valid registers: %s\n"), + nameOnly, regstr ? regstr + 1 : "?", + get_regnolist (buf, sizeof (buf), pfound->reg_list, 1)); + goto process_ctr_def_wrapup; + } + if (pret_ctr->reg_num == REGNO_ANY) + { /* table's regno is a wildcard */ + if (REG_LIST_EOL (pfound->reg_list[1])) + { + /* valid list only contains one regno, so use it */ + pret_ctr->reg_num = pfound->reg_list[0]; + } + else + pret_ctr->reg_num = regno; /* use user's selection */ + } + + /* update name and int_name */ + { + // validate attributes + if (attrs) + { + hwcfuncs_attr_t cpc2_attrs[HWCFUNCS_MAX_ATTRS]; + void * attr_mem; + unsigned nattrs; + char *errbuf; + /* extract attributes from uname */ + attr_mem = hwcfuncs_parse_attrs (uname, cpc2_attrs, HWCFUNCS_MAX_ATTRS, + &nattrs, &errbuf); + if (!attr_mem) + { + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + "%s\n", errbuf); + free (errbuf); + goto process_ctr_def_wrapup; + } + /* make sure all attributes are valid */ + for (unsigned ii = 0; ii < nattrs; ii++) + { + if (!attr_is_valid (forKernel, cpc2_attrs[ii].ca_name)) + { + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + GTXT ("Invalid attribute specified for counter `%s': %s\n"), + nameOnly, cpc2_attrs[ii].ca_name); + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + GTXT ("Run \"%s -h\" with no other arguments for more information on HW counters on this system.\n"), + (IS_KERNEL (forKernel) ? "er_kernel" : "collect")); + free (attr_mem); + goto process_ctr_def_wrapup; + } + for (unsigned jj = ii + 1; jj < nattrs; jj++) + { + if (strcmp (cpc2_attrs[ii].ca_name, + cpc2_attrs[jj].ca_name) == 0) + { + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + GTXT ("Duplicate attribute specified for counter `%s': %s\n"), + nameOnly, cpc2_attrs[ii].ca_name); + free (attr_mem); + goto process_ctr_def_wrapup; + } + } + } + free (attr_mem); + } + pret_ctr->name = strdup (uname); + + // assign int_name + if (pfound->int_name) + { + // Counter is one of the following: + // - aliased (e.g. cycles~system=1), + // - convenience (e.g. cycles0~system=1), + if (!attrs) // convert alias to internal name + pret_ctr->int_name = strdup (pfound->int_name); + else + { + // convert alias to internal name and + // append user-supplied attributes + size_t sz = strlen (pfound->int_name) + strlen (attrs) + 1; + char *tbuf = calloc (sz, 1); + if (tbuf) + snprintf (tbuf, sz, "%s%s", pfound->int_name, attrs); + pret_ctr->int_name = tbuf; + } + } + else + pret_ctr->int_name = strdup (uname); // user-supplied name + } + + /* update val */ + if (set_hwcval (pret_ctr, global_min_time_nsec, value)) + { + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + GTXT ("Invalid interval for HW counter `%s': %s\n"), + nameOnly, value); + goto process_ctr_def_wrapup; + } + hwcentry_print (DBG_LT2, "hwctable: process_ctr_def:", pret_ctr); + rc = 0; + +process_ctr_def_wrapup: + free (regstr); + free (attrs); + free (nameOnly); + return rc; +} + +/*---------------------------------------------------------------------------*/ + +/* external interfaces, see hwcentry.h for descriptions. */ + +extern int +hwc_lookup (int forKernel, hrtime_t global_min_time_nsec, const char *instring, + Hwcentry *caller_entries[], unsigned maxctrs, char **emsg, char **wmsg) +{ + unsigned ii; + char *instr_copy = NULL, *ss = NULL; + unsigned numctrs = 0; + int rc = 0; + char *tokenptr[MAX_PICS * 2]; + unsigned numtokens = 0; + char UEbuf[1024 * 5]; /* error message buffer; strdup of it is passed back to user */ + char UWbuf[1024 * 5]; /* warning message buffer; strdup of it is passed back to user */ + if (emsg) + *emsg = NULL; + if (wmsg) + *wmsg = NULL; + UEbuf[0] = 0; + UWbuf[0] = 0; + + // supply temporary result buffers as needed + Hwcentry tmp_entry_table[MAX_PICS]; + Hwcentry * tmp_entries[MAX_PICS]; + Hwcentry **entries; + if (caller_entries) + entries = caller_entries; + else + { + // user doesn't care about results; provide temporary storage for results + for (ii = 0; ii < MAX_PICS; ii++) + tmp_entries[ii] = &tmp_entry_table[ii]; + entries = tmp_entries; + maxctrs = MAX_PICS; + } + Tprintf (DBG_LT1, "hwctable: hwc_lookup(%s)\n", + instring ? instring : "NULL"); + + /* clear <entries> first - prevent seg faults in hwc_lookup_wrapup */ + for (ii = 0; ii < maxctrs; ii++) + *entries[ii] = empty_ctr; + if (!instring) + { + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("No HW counters were specified.")); + rc = -1; + goto hwc_lookup_wrapup; + } + + /* make sure tables are initialized */ + setup_cpc_skip_hwctest (); + if (cpcx_npics == 0) + { + if (cpcx_cpuver < 0) + { + char buf[1024]; + *buf = 0; + char *pch = hwcfuncs_errmsg_get (buf, sizeof (buf), 0); /* get first err msg, disable capture */ + if (*pch) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("HW counter profiling is not supported on this system: %s%s"), + pch, pch[strlen (pch) - 1] == '\n' ? "" : "\n"); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("HW counter profiling is not supported on this system\n")); + } + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("HW counter profiling is not supported on '%s'\n"), + cpcx_cciname); + rc = -1; + goto hwc_lookup_wrapup; + } + ss = instr_copy = strdup (instring); + while (*ss != 0 && (*ss == ' ' || *ss == '\t')) + ss++; + tokenptr[numtokens++] = ss; + do + { + /* find end of previous token, replace w/ NULL, skip whitespace, set <tokenptr>, repeat */ + for (; *ss; ss++) + { + if (*ss == ',' || *ss == ' ' || *ss == '\t') + { + /* end of previous token found */ + *ss = 0; /* terminate the previous token */ + ss++; + while (*ss != 0 && (*ss == ' ' || *ss == '\t')) + ss++; + if (*ss) + tokenptr[numtokens++] = ss; + break; // from for loop + } + } + } + while (*ss && numtokens < (MAX_PICS * 2)); + + if (*ss) + { + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("The number of HW counters specified exceeds internal resources\n")); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Run \"%s -h\" with no other arguments for more information on HW counters on this system.\n"), + (IS_KERNEL (forKernel) ? "er_kernel" : "collect")); + rc = -1; + goto hwc_lookup_wrapup; + } + Tprintf (DBG_LT3, "hwctable: hwc_lookup(): numtokens=%d\n", numtokens); + + /* look up individual counters */ + { + int fail = 0; + for (ii = 0; ii < numtokens && numctrs < maxctrs; ii += 2) + { + const char *counter; + const char *value; + Hwcentry *pret_ctr = entries[numctrs]; + + /* assign the tokens to ctrnames, timeoutValues. */ + counter = tokenptr[ii]; + if (ii + 1 < numtokens) + value = tokenptr[ii + 1]; + else + value = 0; + if (process_ctr_def (forKernel, global_min_time_nsec, counter, value, pret_ctr, + UWbuf + strlen (UWbuf), + sizeof (UWbuf) - strlen (UWbuf), + UEbuf + strlen (UEbuf), + sizeof (UEbuf) - strlen (UEbuf))) + { + /* could choose to set fail=1 and continue here, + but errmsgs would be aggregated (messy) */ + rc = -1; + goto hwc_lookup_wrapup; + } + numctrs++; + } + if (fail) + { + rc = -1; + goto hwc_lookup_wrapup; + } + } + + if (!numctrs) + { + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("No HW counters were specified.\n")); + rc = -1; + goto hwc_lookup_wrapup; + } + if (numctrs > cpcx_max_concurrent[forKernel]) + { + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("The HW counter configuration could not be loaded: More than %d counters were specified\n"), cpcx_max_concurrent[forKernel]); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Run \"%s -h\" with no other arguments for more information on HW counters on this system.\n"), + (IS_KERNEL (forKernel) ? "er_kernel" : "collect")); + rc = -1; + goto hwc_lookup_wrapup; + } + +hwc_lookup_wrapup: + free (instr_copy); + if (wmsg && strlen (UWbuf)) + *wmsg = strdup (UWbuf); + if (emsg && strlen (UEbuf)) + *emsg = strdup (UEbuf); + if (rc == 0) + rc = numctrs; + return rc; +} + +extern char * +hwc_validate_ctrs (int forKernel, Hwcentry *entries[], unsigned numctrs) +{ + char UEbuf[1024 * 5]; + UEbuf[0] = 0; + + /* search for obvious duplicates*/ + unsigned ii; + for (ii = 0; ii < numctrs; ii++) + { + regno_t reg_a = entries[ii]->reg_num; + if (reg_a != REGNO_ANY) + { + unsigned jj; + for (jj = ii + 1; jj < numctrs; jj++) + { + int reg_b = entries[jj]->reg_num; + if (reg_a == reg_b) + { + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Only one HW counter is allowed per register. The following counters use register %d: \n"), + reg_a); + for (jj = 0; jj < numctrs; jj++) + { + char buf[256]; + int reg_b = entries[jj]->reg_num; + if (reg_a == reg_b) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT (" %d. %s\n"), jj + 1, + hwc_hwcentry_specd_string (buf, sizeof (buf), + entries[jj])); + } + return strdup (UEbuf); + } + } + } + } + + /* test counters */ + hwcfuncs_errmsg_get (NULL, 0, 1); /* enable errmsg capture */ + int hwc_rc = hwcfuncs_assign_regnos (entries, numctrs); + if (!hwc_rc) + hwc_rc = test_hwcs ((const Hwcentry**) entries, numctrs); + if (hwc_rc) + { + if (cpcx_cpuver == CPC_PENTIUM_4_HT || cpcx_cpuver == CPC_PENTIUM_4) + { + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("HW counter profiling is disabled unless only one logical CPU per HyperThreaded processor is online (see psradm)\n")); + return strdup (UEbuf); + } + char buf[1024]; + *buf = 0; + char * pch = hwcfuncs_errmsg_get (buf, sizeof (buf), 0); /* get first err msg, disable capture */ + if (*pch) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("The HW counter configuration could not be loaded: %s%s"), + pch, pch[strlen (pch) - 1] == '\n' ? "" : "\n"); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("The HW counter configuration could not be loaded\n")); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Run \"%s -h\" with no other arguments for more information on HW counters on this system.\n"), + (IS_KERNEL (forKernel) ? "er_kernel" : "collect")); + return strdup (UEbuf); + } + return NULL; +} + +extern Hwcentry * +hwc_post_lookup (Hwcentry * pret_ctr, char *counter, char * int_name, int cpuver) +{ + const Hwcentry *pfound; + regno_t regno; + char *nameOnly = NULL; + char *attrs = NULL; + + /* fields in pret_ctr (name and int_name) should already be free */ + hwcfuncs_parse_ctr (counter, NULL, &nameOnly, &attrs, NULL, ®no); + + /* look for it in the canonical list */ + pfound = static_table_find (stdlist_get_table (cpuver), + nameOnly, int_name, 0, REGNO_ANY); + if (!pfound) /* try the generic list */ + pfound = static_table_find (papi_generic_list, + nameOnly, int_name, 0, REGNO_ANY); + if (pfound) + { + /* in standard list */ + *pret_ctr = *pfound; /* shallow copy */ + if (pret_ctr->int_name) + { + // aliased counter + pret_ctr->int_name = strdup (pret_ctr->int_name); + if (pret_ctr->short_desc == NULL) + { + // look for short_desc of corresponding raw counter + const Hwcentry *praw = static_table_find (stdlist_get_table (cpuver), + pret_ctr->int_name, NULL, 0, REGNO_ANY); + if (praw && praw->short_desc) + pret_ctr->short_desc = strdup (praw->short_desc); + } + } + else + pret_ctr->int_name = strdup (counter); + if (pret_ctr->reg_num == REGNO_ANY) + pret_ctr->reg_num = regno; /* table's regno is a wildcard */ + } + else + { + /* not a standard counter */ + *pret_ctr = empty_ctr; + pret_ctr->int_name = strdup (counter); + pret_ctr->reg_num = regno; + } + + /* update the name */ + if (attrs) + { + pret_ctr->name = canonical_name (counter); + if (pret_ctr->metric) + { + // metric text is supplied from a table. (User supplied HWC alias) + // Append user-supplied attributes to metric name: + size_t len = strlen (pret_ctr->metric) + strlen (attrs) + 4; + char *pch = calloc (len, 1); + if (pch) + snprintf (pch, len, "%s (%s)", pret_ctr->metric, attrs); + pret_ctr->metric = pch; // leaks + } + } + else + pret_ctr->name = strdup (nameOnly); + + if (pfound) + hwcentry_print (DBG_LT2, "hwctable: hwc_post_lookup: found: ", pret_ctr); + else + hwcentry_print (DBG_LT2, "hwctable: hwc_post_lookup: default: ", pret_ctr); + free (attrs); + free (nameOnly); + return pret_ctr; +} + +static const char * +hwc_on_lo_hi (const Hwcentry *pctr) +{ + char* rate; + { + switch (pctr->min_time) + { + case (HWCTIME_LO): + rate = NTXT ("lo"); + break; + case (HWCTIME_ON): + rate = NTXT ("on"); + break; + case (HWCTIME_HI): + rate = NTXT ("hi"); + break; + case (0): + rate = NULL; // null => use interval count + break; + default: + case (HWCTIME_TBD): + rate = NTXT ("on"); + break; + } + } + return rate; //strdup( rate ); +} + +extern char * +hwc_rate_string (const Hwcentry *pctr, int force_numeric) +{ + const char * rateString = hwc_on_lo_hi (pctr); + char buf[128]; + if (!rateString || force_numeric) + { + snprintf (buf, sizeof (buf), NTXT ("%d"), pctr->val); + rateString = buf; + } + return strdup (rateString); +} + +static char metricbuf[2048]; + +extern char * +hwc_i18n_metric (const Hwcentry *pctr) +{ + if (pctr->metric != NULL) + snprintf (metricbuf, sizeof (metricbuf), NTXT ("%s"), PTXT (pctr->metric)); + else if (pctr->name != NULL) + snprintf (metricbuf, sizeof (metricbuf), GTXT ("%s Events"), pctr->name); + else if (pctr->int_name != NULL) + snprintf (metricbuf, sizeof (metricbuf), GTXT ("%s Events"), pctr->int_name); + else + snprintf (metricbuf, sizeof (metricbuf), GTXT ("Undefined Events")); + return metricbuf; +} + +/* return cpu version, should only be called when about to generate an experiment, + not when reading back an experiment */ +#if 0 /* called by ... */ +. / perfan / collect / src / collect.cc : start : 245 : cpuver = hwc_get_cpc_cpuver (); +. / ccr_components / Collector_Interface / collctrl.cc : constructor : 202 : cpcx_cpuver = hwc_get_cpc_cpuver (); +. / perfan / dbe / src / Dbe.cc : 3041 : JApplication::cpuver = hwc_get_cpc_cpuver (); +. / perfan / dbe / src / Dbe.cc : 3164 : JApplication::cpuver = hwc_get_cpc_cpuver (); + +note: +cpc_getcpuver () : only papi, ostest, this and hwprofile.c call it +#endif +int +hwc_get_cpc_cpuver () +{ + setup_cpcx (); + return cpcx_cpuver; +} + +extern char* +hwc_get_cpuname (char *buf, size_t buflen) +{ + setup_cpcx (); + if (!buf || !buflen) + return buf; + buf[0] = 0; + if (cpcx_cciname) + { + strncpy (buf, cpcx_cciname, buflen - 1); + buf[buflen - 1] = 0; + } + return buf; +} + +extern char* +hwc_get_docref (char *buf, size_t buflen) +{ + setup_cpcx (); + if (!buf || !buflen) + return buf; + buf[0] = 0; + if (cpcx_docref) + { + strncpy (buf, cpcx_docref, buflen - 1); + buf[buflen - 1] = 0; + } + return buf; +} + +//TBR: + +extern char* +hwc_get_default_cntrs () +{ + setup_cpcx (); + if (cpcx_default_hwcs[0] != NULL) + return strdup (cpcx_default_hwcs[0]); // TBR deprecate this + return NULL; +} + +extern char* +hwc_get_default_cntrs2 (int forKernel, int style) +{ + setup_cpcx (); + if (!VALID_FOR_KERNEL (forKernel)) + return NULL; + char *cpcx_default = cpcx_default_hwcs[forKernel]; + if (cpcx_default == NULL || cpcx_npics == 0) + return NULL; + if (style == 1) + return strdup (cpcx_default); + + // style == 2 + // we will replace "," delimiters with " -h " (an extra 3 chars per HWC) + char *s = (char *) malloc (strlen (cpcx_default) + 3 * cpcx_npics); + if (s == NULL) return s; + char *p = s; + char *q = cpcx_default; + int i; + for (i = 0; i < cpcx_npics; i++) + { + int qlen = strlen (q); + if (qlen == 0) + { + p[0] = '\0'; + break; + } + // add " -h " if not the first HWC + if (i != 0) + { + p[0] = ' '; + p[1] = '-'; + p[2] = 'h'; + p[3] = ' '; + p += 4; + } + + // find second comma + char *r = strchr (q, ','); + if (r) + r = strchr (r + 1, ','); + + // we didn't find one, so the rest of the string is the last HWC + if (r == NULL) + { + // EUGENE could check i==cpcx_npicx-1, but what if it isn't??? + strcpy (p, q); + if (p[qlen - 1] == ',') + qlen--; + p[qlen] = '\0'; + break; + } + + // copy the HWC, trim trailing comma, add null char + qlen = r - q - 1; + strcpy (p, q); + if (p[qlen - 1] == ',') + qlen--; + p += qlen; + p[0] = '\0'; + q = r + 1; + } + return s; +} + +extern char* +hwc_get_orig_default_cntrs (int forKernel) +{ + setup_cpcx (); + if (!VALID_FOR_KERNEL (forKernel)) + return NULL; + if (cpcx_orig_default_hwcs[forKernel] != NULL) + return strdup (cpcx_orig_default_hwcs[forKernel]); + return NULL; +} + +extern const char * +hwc_memop_string (ABST_type memop) +{ + const char * s; + switch (memop) + { + case ABST_NONE: + s = ""; + break; + case ABST_LOAD: + s = GTXT ("load "); + break; + case ABST_STORE: + s = GTXT ("store "); + break; + case ABST_LDST: + case ABST_US_DTLBM: + case ABST_LDST_SPARC64: + s = GTXT ("load-store "); + break; + case ABST_EXACT_PEBS_PLUS1: + case ABST_EXACT: + s = GTXT ("memoryspace "); + break; + case ABST_COUNT: + s = GTXT ("count "); + break; + case ABST_NOPC: + s = GTXT ("not-program-related "); + break; + default: + s = ""; // was "ABST_UNK", but that's meaningless to users + break; + } + return s; +} + +static const char * +timecvt_string (int timecvt) +{ + if (timecvt > 0) + return GTXT ("CPU-cycles"); + if (timecvt < 0) + return GTXT ("ref-cycles"); + return GTXT ("events"); +} + +int show_regs = 0; // The register setting is available on Solaris only + +/* + * print the specified strings in aligned columns + */ +static void +format_columns (char *buf, int bufsiz, char *s1, char *s2, const char *s3, + const char *s4, char *s5, const char *s6) +{ + // NULL strings are blanks + char *blank = NTXT (""); + if (s2 == NULL) + s2 = blank; + if (s3 == NULL) + s3 = blank; + if (s6 == NULL) + s6 = blank; + + // get the lengths and target widths + // (s6 can be as wide as it likes) + int l1 = strlen (s1), n1 = 10, l2 = strlen (s2), n2 = 13; + int l3 = strlen (s3), n3 = 20, l4 = strlen (s4), n4 = 10, n5; + char divide = ' '; + + // adjust widths, stealing from one column to help a neighbor + // There's a ragged boundary between s2 and s3. + // So push this boundary to the right. + n2 += n3 - l3; + n3 -= n3 - l3; + + // If s3 is empty, push the boundary over to s4. + if (l3 == 0) + { + n2 += n4 - l4; + n4 -= n4 - l4; + } + + // If there's enough room to fit s1 and s2, do so. + if (n1 + n2 >= l1 + l2) + { + if (n1 < l1) + { + n2 -= l1 - n1; + n1 += l1 - n1; + } + if (n2 < l2) + { + n1 -= l2 - n2; + n2 += l2 - n2; + } + } + else + { + // not enough room, so we need to divide the line + n3 += 4 // 4-blank margin + + n1 // 1st column + + 1 // space between 1st and 2nd columns + + n2 // 2nd column + + 1; // space between 2nd and 3th columns + divide = '\n'; + + // make 1st column large enough + if (n1 < l1) + n1 = l1; + + // width of 2nd column no longer matters since we divided the line + n2 = 0; + } + + if (show_regs) + { + // fifth column should be wide enough for regnolist + // see function get_regnolist() + if (cpcx_npics < 10) + n5 = cpcx_npics; // one char per regno + else + n5 = 16 + 3 * (cpcx_npics - 9); // spaces between regnos and some regnos are 2-char wide + // ... and be wide enough for header "regs" + if (n5 < 4) + n5 = 4; + + // print to buffer + // (don't need a space before s4 since historical precedent to have a trailing space in s3) + snprintf (buf, bufsiz, "%-*s %-*s%c%*s%*s %-*s %s", + n1, s1, n2, s2, divide, n3, s3, n4, s4, n5, s5, s6); + } + else + snprintf (buf, bufsiz, "%-*s %-*s%c%*s%*s %s", + n1, s1, n2, s2, divide, n3, s3, n4, s4, s6); + for (int i = strlen (buf); i > 0; i--) + if (buf[i] == ' ' || buf[i] == '\t') + buf[i] = 0; + else + break; +} + +/* routine to return HW counter string formatted and i18n'd */ +static char * +hwc_hwcentry_string_internal (char *buf, size_t buflen, const Hwcentry *ctr, + int show_short_desc) +{ + char stderrbuf[1024]; + char regnolist[256]; + if (!buf || !buflen) + return buf; + buf[0] = 0; + if (ctr == NULL) + { + snprintf (stderrbuf, sizeof (stderrbuf), GTXT ("HW counter not available")); + goto hwc_hwcentry_string_done; + } + char *desc = NULL; + if (show_short_desc) + desc = ctr->short_desc; + if (desc == NULL) + desc = ctr->metric ? hwc_i18n_metric (ctr) : NULL; + format_columns (stderrbuf, sizeof (stderrbuf), ctr->name, ctr->int_name, + hwc_memop_string (ctr->memop), timecvt_string (ctr->timecvt), + get_regnolist (regnolist, sizeof (regnolist), ctr->reg_list, 2), + desc); + +hwc_hwcentry_string_done: + strncpy (buf, stderrbuf, buflen - 1); + buf[buflen - 1] = 0; + return buf; +} + +/* routine to return HW counter string formatted and i18n'd */ +extern char * +hwc_hwcentry_string (char *buf, size_t buflen, const Hwcentry *ctr) +{ + return hwc_hwcentry_string_internal (buf, buflen, ctr, 0); +} + +/* routine to return HW counter string formatted and i18n'd */ +extern char * +hwc_hwcentry_specd_string (char *buf, size_t buflen, const Hwcentry *ctr) +{ + char stderrbuf[1024]; + const char *memop, *timecvt; + char descstr[1024]; + if (!buf || !buflen) + return buf; + buf[0] = 0; + if (ctr == NULL) + { + snprintf (stderrbuf, sizeof (stderrbuf), GTXT ("HW counter not available")); + goto hwc_hwcentry_specd_string_done; + } + timecvt = timecvt_string (ctr->timecvt); + if (ctr->memop) + memop = hwc_memop_string (ctr->memop); + else + memop = ""; + if (ctr->metric != NULL) /* a standard counter for a specific register */ + snprintf (descstr, sizeof (descstr), GTXT (" (`%s'; %s%s)"), + hwc_i18n_metric (ctr), memop, timecvt); + else /* raw counter */ + snprintf (descstr, sizeof (descstr), GTXT (" (%s%s)"), memop, timecvt); + + char *rateString = hwc_rate_string (ctr, 1); + snprintf (stderrbuf, sizeof (stderrbuf), NTXT ("%s,%s%s"), ctr->name, + rateString ? rateString : "", descstr); + free (rateString); + +hwc_hwcentry_specd_string_done: + strncpy (buf, stderrbuf, buflen - 1); + buf[buflen - 1] = 0; + return buf; +} + +unsigned +hwc_get_max_regs () +{ + setup_cpcx (); + return cpcx_npics; +} + +unsigned +hwc_get_max_concurrent (int forKernel) +{ + setup_cpcx (); + if (!VALID_FOR_KERNEL (forKernel)) + return 0; + return cpcx_max_concurrent[forKernel]; +} + +char** +hwc_get_attrs (int forKernel) +{ + setup_cpcx (); + if (!VALID_FOR_KERNEL (forKernel)) + return NULL; + return cpcx_attrs[forKernel]; +} + +Hwcentry ** +hwc_get_std_ctrs (int forKernel) +{ + setup_cpcx (); + if (!VALID_FOR_KERNEL (forKernel)) + return NULL; + return cpcx_std[forKernel]; +} + +Hwcentry ** +hwc_get_raw_ctrs (int forKernel) +{ + setup_cpcx (); + if (!VALID_FOR_KERNEL (forKernel)) + return NULL; + return cpcx_raw[forKernel]; +} + +/* Call an action function for each attribute supported */ +unsigned +hwc_scan_attrs (void (*action)(const char *attr, const char *desc)) +{ + setup_cpcx (); + int cnt = 0; + for (int ii = 0; cpcx_attrs[0] && cpcx_attrs[0][ii]; ii++, cnt++) + { + if (action) + action (cpcx_attrs[0][ii], NULL); + } + if (!cnt && action) + action (NULL, NULL); + return cnt; +} + +unsigned +hwc_scan_std_ctrs (void (*action)(const Hwcentry *)) +{ + setup_cpcx (); + Tprintf (DBG_LT1, "hwctable: hwc_scan_standard_ctrs()...\n"); + int cnt = 0; + for (int ii = 0; cpcx_std[0] && cpcx_std[0][ii]; ii++, cnt++) + if (action) + action (cpcx_std[0][ii]); + if (!cnt && action) + action (NULL); + return cnt; +} + +/* Call an action function for each counter supported */ +/* action is called with NULL when all counters have been seen */ +unsigned +hwc_scan_raw_ctrs (void (*action)(const Hwcentry *)) +{ + setup_cpcx (); + Tprintf (DBG_LT1, "hwctable: hwc_scan_raw_ctrs()...\n"); + int cnt = 0; + for (int ii = 0; cpcx_raw[0] && cpcx_raw[0][ii]; ii++, cnt++) + if (action) + action (cpcx_raw[0][ii]); + if (!cnt && action) + action (NULL); + return cnt; +} + +static void +hwc_usage_raw_overview_sparc (FILE *f_usage, int cpuver) +{ + /* All these cpuver's use cputabs[]==sparc_t5_m6 anyhow. */ + if ((cpuver == CPC_SPARC_M5) || (cpuver == CPC_SPARC_M6) + || (cpuver == CPC_SPARC_T5) || (cpuver == CPC_SPARC_T6)) + cpuver = CPC_SPARC_M4; // M4 was renamed to M5 + + /* While there are small differences between + * cputabs[]== sparc_t4 + * cputabs[]== sparc_t5_m6 + * they are in HWCs we don't discuss in the overview anyhow. + * So just lump them in with T4. + */ + if (cpuver == CPC_SPARC_M4) + cpuver = CPC_SPARC_T4; + + /* Check for the cases we support. */ + if (cpuver != CPC_SPARC_T4 && cpuver != CPC_SPARC_M7 && cpuver != CPC_SPARC_M8) + return; + fprintf (f_usage, GTXT (" While the above aliases represent the most useful hardware counters\n" + " for this processor, a full list of raw (unaliased) counter names appears\n" + " below. First is an overview of some of these names.\n\n")); + fprintf (f_usage, GTXT (" == Cycles.\n" + " Count active cycles with\n" + " Cycles_user\n" + " Set attributes to choose user, system, and/or hyperprivileged cycles.\n\n")); + fprintf (f_usage, GTXT (" == Instructions.\n" + " Count instructions when they are committed with:\n")); + fprintf (f_usage, NTXT (" Instr_all\n")); + if (cpuver != CPC_SPARC_M8) + fprintf (f_usage, GTXT (" It is the total of these counters:\n")); + else + fprintf (f_usage, GTXT (" Some subsets of instructions can be counted separately:\n")); + fprintf (f_usage, NTXT (" Branches %s\n"), GTXT ("branches")); + fprintf (f_usage, NTXT (" Instr_FGU_crypto %s\n"), GTXT ("Floating Point and Graphics Unit")); + fprintf (f_usage, NTXT (" Instr_ld %s\n"), GTXT ("loads")); + fprintf (f_usage, NTXT (" Instr_st %s\n"), GTXT ("stores")); + fprintf (f_usage, NTXT (" %-19s %s\n"), + cpuver == CPC_SPARC_M7 ? NTXT ("Instr_SPR_ring_ops") + : NTXT ("SPR_ring_ops"), + GTXT ("internal use of SPR ring")); + fprintf (f_usage, NTXT (" Instr_other %s\n"), GTXT ("basic arithmetic and logical instructions")); + if (cpuver != CPC_SPARC_M8) + fprintf (f_usage, GTXT (" Some subsets of these instructions can be counted separately:\n")); + fprintf (f_usage, NTXT (" Br_taken %s\n"), GTXT ("Branches that are taken")); + fprintf (f_usage, NTXT (" %-19s %s\n"), + cpuver == CPC_SPARC_M7 ? NTXT ("Instr_block_ld_st") + : NTXT ("Block_ld_st"), + GTXT ("block load/store")); + fprintf (f_usage, NTXT (" %-19s %s\n"), + cpuver == CPC_SPARC_M7 ? NTXT ("Instr_atomic") + : NTXT ("Atomics"), + GTXT ("atomic instructions")); + fprintf (f_usage, NTXT (" %-19s %s\n"), + cpuver == CPC_SPARC_M7 ? NTXT ("Instr_SW_prefetch") + : NTXT ("SW_prefetch"), + GTXT ("prefetches")); + fprintf (f_usage, NTXT (" %-19s %s\n"), + cpuver == CPC_SPARC_M7 ? NTXT ("Instr_SW_count") + : NTXT ("Sw_count_intr"), + GTXT ("SW Count instructions (counts special no-op assembler instructions)")); + fprintf (f_usage, NTXT ("\n")); + +#ifdef TMPLEN + compilation error : we're trying to use a macro that's already defined +#endif +#define TMPLEN 32 + char s0[TMPLEN], s1[TMPLEN], s2[TMPLEN], s3[TMPLEN]; + if (cpuver == CPC_SPARC_M7) + { + snprintf (s0, TMPLEN, "Commit_0_cyc"); + snprintf (s1, TMPLEN, "Commit_1_cyc"); + snprintf (s2, TMPLEN, "Commit_2_cyc"); + snprintf (s3, TMPLEN, "Commit_1_or_2_cyc"); + } + else + { + snprintf (s0, TMPLEN, "Commit_0"); + snprintf (s1, TMPLEN, "Commit_1"); + snprintf (s2, TMPLEN, "Commit_2"); + snprintf (s3, TMPLEN, "Commit_1_or_2"); + } +#undef TMPLEN + fprintf (f_usage, GTXT (" == Commit.\n" + " Instructions may be launched speculatively, executed out of order, etc.\n")); + if (cpuver != CPC_SPARC_M8) + { + fprintf (f_usage, GTXT (" We can count the number of cycles during which 0, 1, or 2 instructions are\n" + " actually completed and their results committed:\n")); + fprintf (f_usage, GTXT (" %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s is a useful way of identifying parts of your application with\n" + " high-latency instructions.\n\n"), + s0, s1, s2, s3, s0); + } + else + { + fprintf (f_usage, GTXT (" We can count the number of cycles during which no instructions were\n" + " able to commit results using:\n")); + fprintf (f_usage, GTXT (" %s\n" + " %s is a useful way of identifying parts of your application with\n" + " high-latency instructions.\n\n"), + s0, s0); + } + + fprintf (f_usage, GTXT (" == Cache/memory hierarchy.\n")); + if (cpuver == CPC_SPARC_M7) + { + fprintf (f_usage, GTXT (" In the cache hierarchy:\n" + " * Each socket has memory and multiple SPARC core clusters (scc).\n" + " * Each scc has an L3 cache and multiple L2 and L1 caches.\n")); + fprintf (f_usage, GTXT (" Loads can be counted by where they hit on socket:\n")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_hit"), GTXT ("hit own L1 data cache")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_L2_hit"), GTXT ("hit own L2")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_L3_hit"), GTXT ("hit own L3")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_nbr_L2_hit"), GTXT ("hit neighbor L2 (same scc)")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_nbr_scc_hit"), GTXT ("hit neighbor scc (same socket)")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_nbr_scc_miss"), GTXT ("miss all caches (same socket)")); + fprintf (f_usage, GTXT (" These loads can also be grouped:\n")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss"), GTXT ("all - DC_hit")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_L2_miss"), GTXT ("all - DC_hit - DC_miss_L2_hit")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_L3_miss"), GTXT ("DC_miss_nbr_scc_hit + DC_miss_nbr_scc_miss")); + fprintf (f_usage, GTXT (" Loads that miss all caches on this socket can be counted:\n")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_remote_scc_hit"), GTXT ("hit cache on different socket")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_local_mem_hit"), GTXT ("hit local memory (same socket)")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_remote_mem_hit"), GTXT ("hit remote memory (off socket)")); + fprintf (f_usage, GTXT (" These events are for speculative loads, launched in anticipation\n" + " of helping performance but whose results might not be committed.\n")); +#if 0 // was: #if defined(linux). See 22236226 - sparc-Linux: Support basic Memoryspace and Dataspace profiling (capture VADDR) + /* 21869427 should not look like memoryspace profiling is supported on Linux */ + /* 21869424 desire memoryspace profiling on Linux */ + fprintf (f_usage, GTXT (" To count only data-cache misses that commit, use:\n")); + fprintf (f_usage, NTXT (" DC_miss_commit\n")); +#else + fprintf (f_usage, GTXT (" To count only data-cache misses that commit, or for memoryspace profiling,\n" + " use the 'memoryspace' counter:\n")); + fprintf (f_usage, NTXT (" DC_miss_commit\n")); +#endif + fprintf (f_usage, NTXT ("\n")); + } + else if (cpuver == CPC_SPARC_M8) + { + fprintf (f_usage, GTXT (" In the cache hierarchy:\n" + " * Each processor has 4 memory controllers and 2 quad core clusters (QCC).\n" + " * Each QCC contains 4 cache processor clusters (CPC).\n" + " * Each CPC contains 4 cores.\n" + " * Each core supports 8 hardware threads.\n" + " * The L3 consists of 2 partitions with 1 QCC per partition.\n" + )); + fprintf (f_usage, GTXT (" Loads can be counted by where they hit on socket:\n")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_L2_hit"), GTXT ("hit own L2")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_L3_hit"), GTXT ("hit own L3")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_L3_dirty_copyback"), GTXT ("hit own L3 but require copyback from L2D")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_nbr_L3_hit"), GTXT ("hit neighbor L3 (same socket)")); + fprintf (f_usage, GTXT (" Loads that miss all caches on this socket can be counted:\n")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_remote_L3_hit"), GTXT ("hit cache on different socket")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_local_mem_hit"), GTXT ("hit local memory (same socket)")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("DC_miss_remote_mem_hit"), GTXT ("hit remote memory (off socket)")); + fprintf (f_usage, GTXT (" These events are for speculative loads, launched in anticipation\n" + " of helping performance but whose results might not be committed.\n")); +#if 0 // was: #if defined(linux). See 22236226 - sparc-Linux: Support basic Memoryspace and Dataspace profiling (capture VADDR) + /* 21869427 should not look like memoryspace profiling is supported on Linux */ + /* 21869424 desire memoryspace profiling on Linux */ + fprintf (f_usage, GTXT (" To count only data-cache misses that commit, use:\n")); + fprintf (f_usage, NTXT (" DC_miss_commit\n")); +#else + fprintf (f_usage, GTXT (" To count only data-cache misses that commit, or for memoryspace profiling,\n" + " use the 'memoryspace' counter:\n")); + fprintf (f_usage, NTXT (" DC_miss_commit\n")); +#endif + fprintf (f_usage, NTXT ("\n")); + } + else + { + fprintf (f_usage, GTXT (" Total data-cache misses can be counted with:\n")); + fprintf (f_usage, NTXT (" DC_miss DC_miss_nospec\n")); + fprintf (f_usage, GTXT (" They are the totals of misses that hit in L2/L3 cache, local memory, or\n" + " remote memory:\n")); + fprintf (f_usage, NTXT (" DC_miss_L2_L3_hit DC_miss_L2_L3_hit_nospec\n")); + fprintf (f_usage, NTXT (" DC_miss_local_hit DC_miss_local_hit_nospec\n")); + fprintf (f_usage, NTXT (" DC_miss_remote_L3_hit DC_miss_remote_L3_hit_nospec\n")); + fprintf (f_usage, GTXT (" The events in the left column include speculative operations. Use the\n" + " right-hand _nospec events to count only data accesses that commit\n" + " or for memoryspace profiling.\n\n")); + } + + fprintf (f_usage, GTXT (" == TLB misses.\n" + " The Translation Lookaside Buffer (TLB) is a cache of virtual-to-physical\n" + " page translations.")); + fprintf (f_usage, GTXT (" If a virtual address (VA) is not represented in the\n" + " TLB, an expensive hardware table walk (HWTW) must be conducted.")); + fprintf (f_usage, GTXT (" If the\n" + " page is still not found, a trap results. There is a data TLB (DTLB) and\n" + " an instruction TLB (ITLB).\n\n")); + fprintf (f_usage, GTXT (" TLB misses can be counted by:\n")); + fprintf (f_usage, NTXT (" %s\n"), + cpuver == CPC_SPARC_M7 ? + NTXT ("DTLB_HWTW_search ITLB_HWTW_search") : + cpuver == CPC_SPARC_M8 ? + NTXT ("DTLB_HWTW ITLB_HWTW") : + NTXT ("DTLB_miss_asynch ITLB_miss_asynch")); + fprintf (f_usage, GTXT (" or broken down by page size:\n")); + fprintf (f_usage, NTXT (" %s"), + cpuver == CPC_SPARC_M7 ? + NTXT ("DTLB_HWTW_hit_8K ITLB_HWTW_hit_8K\n" + " DTLB_HWTW_hit_64K ITLB_HWTW_hit_64K\n" + " DTLB_HWTW_hit_4M ITLB_HWTW_hit_4M\n") : + NTXT ("DTLB_fill_8KB ITLB_fill_8KB\n" + " DTLB_fill_64KB ITLB_fill_64KB\n" + " DTLB_fill_4MB ITLB_fill_4MB\n")); + fprintf (f_usage, NTXT (" %s\n\n"), + cpuver == CPC_SPARC_M7 ? + NTXT ("DTLB_HWTW_hit_256M ITLB_HWTW_hit_256M\n" + " DTLB_HWTW_hit_2G_16G ITLB_HWTW_hit_2G_16G\n" + " DTLB_HWTW_miss_trap ITLB_HWTW_miss_trap") : + cpuver == CPC_SPARC_M8 ? + NTXT ("DTLB_HWTW_hit_256M ITLB_HWTW_hit_256M\n" + " DTLB_HWTW_hit_16G ITLB_HWTW_hit_16G\n" + " DTLB_HWTW_hit_1T ITLB_HWTW_hit_1T") : + NTXT ("DTLB_fill_256MB ITLB_fill_256MB\n" + " DTLB_fill_2GB ITLB_fill_2GB\n" + " DTLB_fill_trap ITLB_fill_trap")); + if (cpuver == CPC_SPARC_M8) + { + fprintf (f_usage, GTXT (" TLB traps, which can require hundreds of cycles, can be counted with:\n")); + fprintf (f_usage, NTXT (" %s\n\n"), + NTXT ("DTLB_fill_trap ITLB_fill_trap")); + } + + fprintf (f_usage, GTXT (" == Branch misprediction.\n" + " Count branch mispredictions with:\n" + " Br_mispred\n" + " It is the total of:\n" + " Br_dir_mispred direction was mispredicted\n" + " %s target was mispredicted\n" + "\n"), cpuver == CPC_SPARC_M7 ? NTXT ("Br_tgt_mispred") : NTXT ("Br_trg_mispred")); + + fprintf (f_usage, GTXT (" == RAW hazards.\n" + " A read-after-write (RAW) delay occurs when we attempt to read a datum\n" + " before an earlier write has had time to complete:\n")); + if (cpuver == CPC_SPARC_M8) + { + fprintf (f_usage, NTXT (" RAW_hit\n")); + fprintf (f_usage, GTXT (" RAW_hit events can be broken down into:\n")); + } + else + { + fprintf (f_usage, NTXT (" RAW_hit_st_q~emask=0xf\n")); + fprintf (f_usage, GTXT (" The mask 0xf counts the total of all types such as:\n")); + } + fprintf (f_usage, NTXT (" RAW_hit_st_buf write is still in store buffer\n" + " RAW_hit_st_q write is still in store queue\n" + "\n")); + if (cpuver == CPC_SPARC_M7) + { + fprintf (f_usage, GTXT (" == Flush.\n" + " One can count the number of times the pipeline must be flushed:\n")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("Flush_L3_miss"), GTXT ("load missed L3 and >1 strand is active on the core")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("Flush_br_mispred"), GTXT ("branch misprediction")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("Flush_arch_exception"), GTXT ("SPARC exceptions and trap entry/return")); + fprintf (f_usage, NTXT (" %-22s %s\n"), + NTXT ("Flush_other"), GTXT ("state change to/from halted/paused")); + fprintf (f_usage, NTXT ("\n")); + } +} + +static void +hwc_usage_internal (int forKernel, FILE *f_usage, const char *cmd, const char *dataspace_msg, int show_syntax, int show_short_desc) +{ + if (!VALID_FOR_KERNEL (forKernel)) + return; + char cpuname[128]; + hwc_get_cpuname (cpuname, 128); + Hwcentry** raw_ctrs = hwc_get_raw_ctrs (forKernel); + int has_raw_ctrs = (raw_ctrs && raw_ctrs[0]); + Hwcentry** std_ctrs = hwc_get_std_ctrs (forKernel); + int has_std_ctrs = (std_ctrs && std_ctrs[0]); + unsigned hwc_maxregs = hwc_get_max_concurrent (forKernel); + int cpuver = hwc_get_cpc_cpuver (); + if (hwc_maxregs != 0) + { + if (show_syntax) + { + fprintf (f_usage, GTXT ("\nSpecifying HW counters on `%s' (cpuver=%d):\n\n"), cpuname, cpuver); + fprintf (f_usage, GTXT (" -h {auto|lo|on|hi}\n")); + fprintf (f_usage, GTXT ("\tturn on default set of HW counters at the specified rate\n")); + if (hwc_maxregs == 1) + { + fprintf (f_usage, GTXT (" -h <ctr_def>\n")); + fprintf (f_usage, GTXT ("\tspecify HW counter profiling for one HW counter only\n")); + } + else + { + fprintf (f_usage, GTXT (" -h <ctr_def> [-h <ctr_def>]...\n")); + fprintf (f_usage, GTXT (" -h <ctr_def>[,<ctr_def>]...\n")); + fprintf (f_usage, GTXT ("\tspecify HW counter profiling for up to %u HW counters\n"), hwc_maxregs); + } + fprintf (f_usage, NTXT ("\n")); + } + else + { + fprintf (f_usage, GTXT ("\nSpecifying HW counters on `%s' (cpuver=%d)\n\n"), cpuname, cpuver); + if (hwc_maxregs == 1) + fprintf (f_usage, GTXT (" Hardware counter profiling is supported for only one counter.\n")); + else + fprintf (f_usage, GTXT (" Hardware counter profiling is supported for up to %u HW counters.\n"), hwc_maxregs); + } + } + else + { + if (!IS_KERNEL (forKernel)) + { // EUGENE I don't see why we don't also use this for er_kernel + char buf[1024]; + *buf = 0; + char *pch = hwcfuncs_errmsg_get (buf, sizeof (buf), 0); + if (*pch) + fprintf (f_usage, GTXT ("HW counter profiling is not supported on this system: %s%s"), + pch, pch[strlen (pch) - 1] == '\n' ? "" : "\n"); + else + fprintf (f_usage, GTXT ("HW counter profiling is not supported on this system\n")); + } + return; + } + + /* At this point, we know we have counters */ + char**hwc_attrs = hwc_get_attrs (forKernel); + int has_attrs = (hwc_attrs && hwc_attrs[0]); + if (show_syntax) + { + const char *reg_s = show_regs ? "[/<reg#>]" : ""; + const char *attr_s = has_attrs ? "[[~<attr>=<val>]...]" : ""; + fprintf (f_usage, GTXT (" <ctr_def> == <ctr>%s%s,[<rate>]\n"), attr_s, reg_s); + if (dataspace_msg) + fprintf (f_usage, NTXT ("%s"), dataspace_msg); + fprintf (f_usage, GTXT (" <ctr>\n")); + fprintf (f_usage, GTXT (" counter name, ")); + } + else + fprintf (f_usage, GTXT (" Counter name ")); + fprintf (f_usage, GTXT ("must be selected from the available counters\n" + " listed below. On most systems, if a counter is not listed\n" + " below, it may still be specified by its numeric value.\n")); + if (cpcx_has_precise[forKernel]) + { + if (!forKernel) + fprintf (f_usage, GTXT (" Counters labeled as 'memoryspace' in the list below will\n" + " collect memoryspace data by default.\n")); + } + fprintf (f_usage, GTXT ("\n")); + if (has_attrs) + { + if (show_syntax) + { + fprintf (f_usage, GTXT (" ~<attr>=<val>\n")); + fprintf (f_usage, GTXT (" optional attribute where <val> can be in decimal or hex\n" + " format, and <attr> can be one of: \n")); + } + else + fprintf (f_usage, GTXT (" Optional attribute where <val> can be in decimal or hex\n" + " format, and <attr> can be one of: \n")); + for (char **pattr = hwc_attrs; *pattr; pattr++) + fprintf (f_usage, NTXT (" `%s'\n"), *pattr); + if (show_syntax) + fprintf (f_usage, GTXT (" Multiple attributes may be specified, and each must be preceded by a ~.\n\n")); + else + fprintf (f_usage, GTXT (" Multiple attributes may be specified.\n\n")); + if (IS_KERNEL (forKernel)) + fprintf (f_usage, GTXT (" Other attributes may be supported by the chip, but are not supported by DTrace and will be ignored by er_kernel.\n\n")); + } + + if (show_syntax) + { + if (show_regs) + fprintf (f_usage, GTXT (" /<reg#>\n" + " forces use of a specific hardware register. (Solaris only)\n" + " If not specified, %s will attempt to place the counter into the first\n" + " available register and as a result may be unable to place\n" + " subsequent counters due to register conflicts.\n" + " The / in front of the register number is required if a register is specified.\n\n"), + cmd); + + fprintf (f_usage, GTXT (" <rate> == {auto|lo|on|hi}\n")); + fprintf (f_usage, GTXT (" `auto' (default) match the rate used by clock profiling.\n")); + fprintf (f_usage, GTXT (" If clock profiling is disabled, use `on'.\n")); + fprintf (f_usage, GTXT (" `lo' per-thread maximum rate of ~10 samples/second\n")); + fprintf (f_usage, GTXT (" `on' per-thread maximum rate of ~100 samples/second\n")); + fprintf (f_usage, GTXT (" `hi' per-thread maximum rate of ~1000 samples/second\n\n")); + fprintf (f_usage, GTXT (" <rate> == <interval>\n")); + fprintf (f_usage, GTXT (" event interval; see collect (1) for details\n\n")); + + fprintf (f_usage, GTXT (" A comma ',' followed immediately by white space may be omitted.\n\n")); + } + + /* default counters */ + fprintf (f_usage, GTXT ("Default set of HW counters:\n\n")); + char * defctrs = hwc_get_default_cntrs2 (forKernel, 1); + if (defctrs == NULL) + fprintf (f_usage, GTXT (" No default HW counter set defined for this system.\n")); + else if (strlen (defctrs) == 0) + { + char *s = hwc_get_orig_default_cntrs (forKernel); + fprintf (f_usage, GTXT (" The default HW counter set (%s) defined for %s cannot be loaded on this system.\n"), + s, cpuname); + free (s); + free (defctrs); + } + else + { + char *defctrs2 = hwc_get_default_cntrs2 (forKernel, 2); + fprintf (f_usage, GTXT (" -h %s\n"), defctrs); + free (defctrs2); + free (defctrs); + } + + /* long listings */ + char tmp[1024]; + if (has_std_ctrs) + { + fprintf (f_usage, GTXT ("\nAliases for most useful HW counters:\n\n")); + format_columns (tmp, 1024, "alias", "raw name", "type ", "units", "regs", "description"); + fprintf (f_usage, NTXT (" %s\n\n"), tmp); + for (Hwcentry **pctr = std_ctrs; *pctr; pctr++) + { + Hwcentry *ctr = *pctr; + hwc_hwcentry_string_internal (tmp, sizeof (tmp), ctr, 0); + fprintf (f_usage, NTXT (" %s\n"), tmp); + } + } + if (has_raw_ctrs) + { + fprintf (f_usage, GTXT ("\nRaw HW counters:\n\n")); + hwc_usage_raw_overview_sparc (f_usage, cpuver); + format_columns (tmp, 1024, "name", NULL, "type ", "units", "regs", "description"); + fprintf (f_usage, NTXT (" %s\n\n"), tmp); + for (Hwcentry **pctr = raw_ctrs; *pctr; pctr++) + { + Hwcentry *ctr = *pctr; + hwc_hwcentry_string_internal (tmp, sizeof (tmp), ctr, show_short_desc); + fprintf (f_usage, NTXT (" %s\n"), tmp); + } + } + + /* documentation notice */ + hwc_get_docref (tmp, 1024); + if (strlen (tmp)) + fprintf (f_usage, NTXT ("\n%s\n"), tmp); +} + +/* Print a description of "-h" usage, largely common to collect and er_kernel. */ +void +hwc_usage (int forKernel, const char *cmd, const char *dataspace_msg) +{ + hwc_usage_internal (forKernel, stdout, cmd, dataspace_msg, 1, 0); +} + +void +hwc_usage_f (int forKernel, FILE *f, const char *cmd, const char *dataspace_msg, int show_syntax, int show_short_desc) +{ + hwc_usage_internal (forKernel, f, cmd, dataspace_msg, show_syntax, show_short_desc); +} + +/*---------------------------------------------------------------------------*/ +/* init functions */ + +static char* supported_pebs_counters[] = { + "mem_inst_retired.latency_above_threshold", + "mem_trans_retired.load_latency", + "mem_trans_retired.precise_store", + NULL +}; + +/* callback, (see setup_cpc()) called for each valid regno/name combo */ + +/* builds rawlist,, creates and updates reg_list[] arrays in stdlist table */ +static void +hwc_cb (uint_t cpc_regno, const char *name) +{ + regno_t regno = cpc_regno; /* convert type */ + list_add (&unfiltered_raw, regno, name); +} + +/* input: + * forKernel: 1 - generate lists for er_kernel, 0 - generate lists for collect + * + * raw_orig: HWCs as generated by hwc_cb() + * output: + * pstd_out[], praw_out[]: malloc'd array of pointers to malloc'd hwcentry, or NULL + */ +static void +hwc_process_raw_ctrs (int forKernel, Hwcentry ***pstd_out, + Hwcentry ***praw_out, Hwcentry ***phidden_out, + Hwcentry**static_tables, Hwcentry **raw_unfiltered_in) +{ + // set up output buffers + ptr_list s_outbufs[3]; + ptr_list *std_out = &s_outbufs[0]; + ptr_list_init (std_out); + ptr_list *raw_out = &s_outbufs[1]; + ptr_list_init (raw_out); + ptr_list *hidden_out = &s_outbufs[2]; + ptr_list_init (hidden_out); + +#define NUM_TABLES 3 + ptr_list table_copy[NUM_TABLES]; // copy of data from static tables. [0]std, [1]generic, and [2]hidden + for (int tt = 0; tt < NUM_TABLES; tt++) + ptr_list_init (&table_copy[tt]); + + // copy records from std [0] and generic [1] static input tables into table_copy[0],[1],or[2] + for (int tt = 0; tt < 2; tt++) + for (Hwcentry *pctr = static_tables[tt]; pctr && pctr->name; pctr++) + if (is_hidden_alias (pctr)) + list_append_shallow_copy (&table_copy[2], pctr); // hidden list + else + list_append_shallow_copy (&table_copy[tt], pctr); + + // copy raw_unfiltered_in to raw_out + for (int ii = 0; raw_unfiltered_in && raw_unfiltered_in[ii]; ii++) + { + Hwcentry *pctr = raw_unfiltered_in[ii]; + // filter out raw counters that don't work correctly + +#ifdef WORKAROUND_6231196_NIAGARA1_NO_CTR_0 + if (cpcx_cpuver == CPC_ULTRA_T1) + if (!regno_is_valid (pctr, 1)) + continue; /* Niagara can not profile on register zero; skip this */ +#endif + // remove specific PEBs counters when back end doesn't support sampling + const char *name = pctr->name; + if ((cpcx_support_bitmask & HWCFUNCS_SUPPORT_PEBS_SAMPLING) == 0 || forKernel) + { + int skip = 0; + for (int ii = 0; supported_pebs_counters[ii]; ii++) + if (strcmp (supported_pebs_counters[ii], name) == 0) + { + skip = 1; + break; + } + if (skip) + continue; + } + + Hwcentry *pnew = list_append_shallow_copy (raw_out, pctr); +#ifdef WORKAROUND_6231196_NIAGARA1_NO_CTR_0 + if (cpcx_cpuver == CPC_ULTRA_T1) + { + free (pnew->reg_list); + pnew->reg_list = NULL; + regno_add (pnew, 1); // only allow register 1 + } +#endif + } // raw_unfiltered_in + + // Scan raw counters to populate Hwcentry fields from matching static_tables entries + // Also populate reg_list for aliases found in table_copy[] + for (int uu = 0; uu < raw_out->sz; uu++) + { + Hwcentry *praw = (Hwcentry*) raw_out->array[uu]; + Hwcentry *pstd = NULL; // set if non-alias entry from std table matches + char *name = praw->name; + /* in the standard counter and generic lists, + update reg_list for all matching items */ + for (int tt = 0; tt < NUM_TABLES; tt++) + { // std, generic, and hidden + if (table_copy[tt].sz == 0) + continue; + Hwcentry **array = (Hwcentry**) table_copy[tt].array; + for (int jj = 0; array[jj]; jj++) + { // all table counters + Hwcentry *pctr = array[jj]; + char *pname; + if (pctr->int_name) + pname = pctr->int_name; + else + pname = pctr->name; + if (!is_same (name, pname, '~')) + continue; + + /* truncated pname matches <name>... */ + // check to see if table entry applies only to specific register + int specific_reg_num_only = 0; + if (pctr->reg_num != REGNO_ANY) + { + // table entry applies only to specific register + if (!regno_is_valid (praw, pctr->reg_num)) + continue; + specific_reg_num_only = 1; + } + + // Match! + // Update cpu_table_copy's supported registers + if (specific_reg_num_only) + regno_add (pctr, pctr->reg_num); + else + pctr->reg_list = praw->reg_list; + + if (!is_visible_alias (pctr) && !is_hidden_alias (pctr)) + { + // Note: we could expand criteria to also allow aliases to set default rates for raw HWCs + /* This is an 'internal' raw counter */ + if (!pstd) + pstd = pctr; /* use info as a template when adding to raw list */ + else + hwcentry_print (DBG_LT0, "hwctable: hwc_cb: Warning: " + "counter %s appears in table more than once: ", + pstd); + } + }/* for table rows */ + }/* for std and generic tables */ + + if (pstd) + { + /* the main table had an entry that matched <name> exactly */ + /* Apply the main table entry as a template */ + *praw = *pstd; + } + }/* for (raw_out) */ + + // update std_out and hidden_out + for (int tt = 0; tt < NUM_TABLES; tt++) + { + if (tt == 1 /*skip std_raw*/ || table_copy[tt].sz == 0) + continue; + Hwcentry *pctr; + for (int ii = 0; (pctr = table_copy[tt].array[ii]); ii++) + { + // prune unsupported rows from std table + if (!is_visible_alias (pctr) && !is_hidden_alias (pctr)) + continue; // only aliases + if (REG_LIST_IS_EMPTY (pctr->reg_list)) + { + if (is_numeric_alias (pctr)) + { +#if 1 //22844570 DTrace cpc provider does not accept numeric counter names + if (forKernel) + continue; +#endif + regno_add (pctr, REGNO_ANY); // hwcs specified by number allowed on any register + } + else + continue; + } + + ptr_list *dest = (tt == 0) ? std_out : hidden_out; + Hwcentry *isInList; + if (pctr->short_desc == NULL) + { + isInList = ptrarray_find_by_name ((Hwcentry**) raw_out->array, pctr->int_name); + if (isInList) + pctr->short_desc = isInList->short_desc; // copy the raw counter's detailed description + } + isInList = ptrarray_find_by_name ((Hwcentry**) dest->array, pctr->name); + if (isInList) + hwcentry_print (DBG_LT0, "hwctable: hwc_cb: Warning: " + "counter %s appears in alias list more than once: ", + pctr); + else + list_append_shallow_copy (dest, pctr); + } + } + for (int tt = 0; tt < NUM_TABLES; tt++) + ptr_list_free (&table_copy[tt]); + + if (forKernel) + { + // for er_kernel, use baseline value of PRELOAD_DEF_ERKERNEL instead of PRELOAD_DEF + for (int tt = 0; tt < 3; tt++) + { // std_out-0, raw_out-1, hidden_out-2 + Hwcentry** hwcs = (Hwcentry**) (s_outbufs[tt].array); + for (int ii = 0; hwcs && hwcs[ii]; ii++) + { + Hwcentry *hwc = hwcs[ii]; + if (hwc->val == PRELOAD_DEF) + hwc->val = PRELOAD_DEF_ERKERNEL; + } + } + } + *pstd_out = (Hwcentry**) std_out->array; + *praw_out = (Hwcentry**) raw_out->array; + *phidden_out = (Hwcentry**) hidden_out->array; +} + +/* callback, (see setup_cpc()) called for each valid attribute */ +/* builds attrlist */ +static void +attrs_cb (const char *attr) +{ + Tprintf (DBG_LT3, "hwctable: attrs_cb(): %s\n", attr); + if (strcmp (attr, "picnum") == 0) + return; /* don't make this attribute available to users */ + ptr_list_add (&unfiltered_attrs, (void*) strdup (attr)); +} + +/* returns true if attribute is valid for this platform */ +static int +attr_is_valid (int forKernel, const char *attr) +{ + setup_cpcx (); + if (!VALID_FOR_KERNEL (forKernel) || !cpcx_attrs[forKernel]) + return 0; + for (int ii = 0; cpcx_attrs[forKernel][ii]; ii++) + if (strcmp (attr, cpcx_attrs[forKernel][ii]) == 0) + return 1; + return 0; +} diff --git a/gprofng/common/opteron_pcbe.c b/gprofng/common/opteron_pcbe.c new file mode 100644 index 0000000..d479945 --- /dev/null +++ b/gprofng/common/opteron_pcbe.c @@ -0,0 +1,448 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * This file contains preset event names from the Performance Application + * Programming Interface v3.5 which included the following notice: + * + * Copyright (c) 2005,6 + * Innovative Computing Labs + * Computer Science Department, + * University of Tennessee, + * Knoxville, TN. + * All Rights Reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of Tennessee nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + * This open source software license conforms to the BSD License template. + */ + +/* + * Performance Counter Back-End for AMD Opteron and AMD Athlon 64 processors. + */ + +#include <sys/types.h> +#include "hwcdrv.h" + +#define CPU /* used by cpuid_get*() functions */ + +typedef struct _amd_event +{ + char *name; + uint16_t emask; /* Event mask setting */ + uint8_t umask_valid; /* Mask of unreserved UNIT_MASK bits */ +} amd_event_t; + +typedef struct _amd_generic_event +{ + char *name; + char *event; + uint8_t umask; +} amd_generic_event_t; + +#define EV_END { NULL, 0, 0 } +#define GEN_EV_END { NULL, NULL, 0 } + +#define AMD_cmn_events \ + { "FP_dispatched_fpu_ops", 0x00, 0x3F }, \ + { "FP_cycles_no_fpu_ops_retired", 0x01, 0x0 }, \ + { "FP_dispatched_fpu_ops_ff", 0x02, 0x0 }, \ + { "LS_seg_reg_load", 0x20, 0x7F }, \ + { "LS_uarch_resync_self_modify", 0x21, 0x0 }, \ + { "LS_uarch_resync_snoop", 0x22, 0x0 }, \ + { "LS_buffer_2_full", 0x23, 0x0 }, \ + { "LS_retired_cflush", 0x26, 0x0 }, \ + { "LS_retired_cpuid", 0x27, 0x0 }, \ + { "DC_access", 0x40, 0x0 }, \ + { "DC_miss", 0x41, 0x0 }, \ + { "DC_refill_from_L2", 0x42, 0x1F }, \ + { "DC_refill_from_system", 0x43, 0x1F }, \ + { "DC_misaligned_data_ref", 0x47, 0x0 }, \ + { "DC_uarch_late_cancel_access", 0x48, 0x0 }, \ + { "DC_uarch_early_cancel_access", 0x49, 0x0 }, \ + { "DC_dispatched_prefetch_instr", 0x4B, 0x7 }, \ + { "DC_dcache_accesses_by_locks", 0x4C, 0x2 }, \ + { "BU_memory_requests", 0x65, 0x83}, \ + { "BU_data_prefetch", 0x67, 0x3 }, \ + { "BU_cpu_clk_unhalted", 0x76, 0x0 }, \ + { "IC_fetch", 0x80, 0x0 }, \ + { "IC_miss", 0x81, 0x0 }, \ + { "IC_refill_from_L2", 0x82, 0x0 }, \ + { "IC_refill_from_system", 0x83, 0x0 }, \ + { "IC_itlb_L1_miss_L2_hit", 0x84, 0x0 }, \ + { "IC_uarch_resync_snoop", 0x86, 0x0 }, \ + { "IC_instr_fetch_stall", 0x87, 0x0 }, \ + { "IC_return_stack_hit", 0x88, 0x0 }, \ + { "IC_return_stack_overflow", 0x89, 0x0 }, \ + { "FR_retired_x86_instr_w_excp_intr", 0xC0, 0x0 }, \ + { "FR_retired_uops", 0xC1, 0x0 }, \ + { "FR_retired_branches_w_excp_intr", 0xC2, 0x0 }, \ + { "FR_retired_branches_mispred", 0xC3, 0x0 }, \ + { "FR_retired_taken_branches", 0xC4, 0x0 }, \ + { "FR_retired_taken_branches_mispred", 0xC5, 0x0 }, \ + { "FR_retired_far_ctl_transfer", 0xC6, 0x0 }, \ + { "FR_retired_resyncs", 0xC7, 0x0 }, \ + { "FR_retired_near_rets", 0xC8, 0x0 }, \ + { "FR_retired_near_rets_mispred", 0xC9, 0x0 }, \ + { "FR_retired_taken_branches_mispred_addr_miscomp", 0xCA, 0x0 }, \ + { "FR_retired_fastpath_double_op_instr", 0xCC, 0x7 }, \ + { "FR_intr_masked_cycles", 0xCD, 0x0 }, \ + { "FR_intr_masked_while_pending_cycles", 0xCE, 0x0 }, \ + { "FR_taken_hardware_intrs", 0xCF, 0x0 }, \ + { "FR_nothing_to_dispatch", 0xD0, 0x0 }, \ + { "FR_dispatch_stalls", 0xD1, 0x0 }, \ + { "FR_dispatch_stall_branch_abort_to_retire", 0xD2, 0x0 }, \ + { "FR_dispatch_stall_serialization", 0xD3, 0x0 }, \ + { "FR_dispatch_stall_segment_load", 0xD4, 0x0 }, \ + { "FR_dispatch_stall_reorder_buffer_full", 0xD5, 0x0 }, \ + { "FR_dispatch_stall_resv_stations_full", 0xD6, 0x0 }, \ + { "FR_dispatch_stall_fpu_full", 0xD7, 0x0 }, \ + { "FR_dispatch_stall_ls_full", 0xD8, 0x0 }, \ + { "FR_dispatch_stall_waiting_all_quiet", 0xD9, 0x0 }, \ + { "FR_dispatch_stall_far_ctl_trsfr_resync_branch_pend", 0xDA, 0x0 },\ + { "FR_fpu_exception", 0xDB, 0xF }, \ + { "FR_num_brkpts_dr0", 0xDC, 0x0 }, \ + { "FR_num_brkpts_dr1", 0xDD, 0x0 }, \ + { "FR_num_brkpts_dr2", 0xDE, 0x0 }, \ + { "FR_num_brkpts_dr3", 0xDF, 0x0 }, \ + { "NB_mem_ctrlr_bypass_counter_saturation", 0xE4, 0xF } + +#define OPT_events \ + { "LS_locked_operation", 0x24, 0x7 }, \ + { "DC_copyback", 0x44, 0x1F }, \ + { "DC_dtlb_L1_miss_L2_hit", 0x45, 0x0 }, \ + { "DC_dtlb_L1_miss_L2_miss", 0x46, 0x0 }, \ + { "DC_1bit_ecc_error_found", 0x4A, 0x3 }, \ + { "BU_system_read_responses", 0x6C, 0x7 }, \ + { "BU_quadwords_written_to_system", 0x6D, 0x1 }, \ + { "BU_internal_L2_req", 0x7D, 0x1F }, \ + { "BU_fill_req_missed_L2", 0x7E, 0x7 }, \ + { "BU_fill_into_L2", 0x7F, 0x1 }, \ + { "IC_itlb_L1_miss_L2_miss", 0x85, 0x0 }, \ + { "FR_retired_fpu_instr", 0xCB, 0xF }, \ + { "NB_mem_ctrlr_page_access", 0xE0, 0x7 }, \ + { "NB_mem_ctrlr_page_table_overflow", 0xE1, 0x0 }, \ + { "NB_mem_ctrlr_turnaround", 0xE3, 0x7 }, \ + { "NB_ECC_errors", 0xE8, 0x80}, \ + { "NB_sized_commands", 0xEB, 0x7F }, \ + { "NB_probe_result", 0xEC, 0x7F}, \ + { "NB_gart_events", 0xEE, 0x7 }, \ + { "NB_ht_bus0_bandwidth", 0xF6, 0xF }, \ + { "NB_ht_bus1_bandwidth", 0xF7, 0xF }, \ + { "NB_ht_bus2_bandwidth", 0xF8, 0xF } + +#define OPT_RevD_events \ + { "NB_sized_blocks", 0xE5, 0x3C } + +#define OPT_RevE_events \ + { "NB_cpu_io_to_mem_io", 0xE9, 0xFF}, \ + { "NB_cache_block_commands", 0xEA, 0x3D} + +#define AMD_FAMILY_10h_cmn_events \ + { "FP_retired_sse_ops", 0x3, 0x7F}, \ + { "FP_retired_move_ops", 0x4, 0xF}, \ + { "FP_retired_serialize_ops", 0x5, 0xF}, \ + { "FP_serialize_ops_cycles", 0x6, 0x3}, \ + { "DC_copyback", 0x44, 0x7F }, \ + { "DC_dtlb_L1_miss_L2_hit", 0x45, 0x3 }, \ + { "DC_dtlb_L1_miss_L2_miss", 0x46, 0x7 }, \ + { "DC_1bit_ecc_error_found", 0x4A, 0xF }, \ + { "DC_dtlb_L1_hit", 0x4D, 0x7 }, \ + { "BU_system_read_responses", 0x6C, 0x17 }, \ + { "BU_octwords_written_to_system", 0x6D, 0x1 }, \ + { "BU_internal_L2_req", 0x7D, 0x3F }, \ + { "BU_fill_req_missed_L2", 0x7E, 0xF }, \ + { "BU_fill_into_L2", 0x7F, 0x3 }, \ + { "IC_itlb_L1_miss_L2_miss", 0x85, 0x3 }, \ + { "IC_eviction", 0x8B, 0x0 }, \ + { "IC_cache_lines_invalidate", 0x8C, 0xF }, \ + { "IC_itlb_reload", 0x99, 0x0 }, \ + { "IC_itlb_reload_aborted", 0x9A, 0x0 }, \ + { "FR_retired_mmx_sse_fp_instr", 0xCB, 0x7 }, \ + { "NB_mem_ctrlr_page_access", 0xE0, 0xFF }, \ + { "NB_mem_ctrlr_page_table_overflow", 0xE1, 0x3 }, \ + { "NB_mem_ctrlr_turnaround", 0xE3, 0x3F }, \ + { "NB_thermal_status", 0xE8, 0x7C}, \ + { "NB_sized_commands", 0xEB, 0x3F }, \ + { "NB_probe_results_upstream_req", 0xEC, 0xFF}, \ + { "NB_gart_events", 0xEE, 0xFF }, \ + { "NB_ht_bus0_bandwidth", 0xF6, 0xBF }, \ + { "NB_ht_bus1_bandwidth", 0xF7, 0xBF }, \ + { "NB_ht_bus2_bandwidth", 0xF8, 0xBF }, \ + { "NB_ht_bus3_bandwidth", 0x1F9, 0xBF }, \ + { "LS_locked_operation", 0x24, 0xF }, \ + { "LS_cancelled_store_to_load_fwd_ops", 0x2A, 0x7 }, \ + { "LS_smi_received", 0x2B, 0x0 }, \ + { "LS_ineffective_prefetch", 0x52, 0x9 }, \ + { "LS_global_tlb_flush", 0x54, 0x0 }, \ + { "NB_mem_ctrlr_dram_cmd_slots_missed", 0xE2, 0x3 }, \ + { "NB_mem_ctrlr_req", 0x1F0, 0xFF }, \ + { "CB_cpu_to_dram_req_to_target", 0x1E0, 0xFF }, \ + { "CB_io_to_dram_req_to_target", 0x1E1, 0xFF }, \ + { "CB_cpu_read_cmd_latency_to_target_0_to_3", 0x1E2, 0xFF }, \ + { "CB_cpu_read_cmd_req_to_target_0_to_3", 0x1E3, 0xFF }, \ + { "CB_cpu_read_cmd_latency_to_target_4_to_7", 0x1E4, 0xFF }, \ + { "CB_cpu_read_cmd_req_to_target_4_to_7", 0x1E5, 0xFF }, \ + { "CB_cpu_cmd_latency_to_target_0_to_7", 0x1E6, 0xFF }, \ + { "CB_cpu_req_to_target_0_to_7", 0x1E7, 0xFF }, \ + { "L3_read_req", 0x4E0, 0xF7 }, \ + { "L3_miss", 0x4E1, 0xF7 }, \ + { "L3_l2_eviction_l3_fill", 0x4E2, 0xFF }, \ + { "L3_eviction", 0x4E3, 0xF } + +#define AMD_cmn_generic_events \ + { "PAPI_br_ins", "FR_retired_branches_w_excp_intr", 0x0 },\ + { "PAPI_br_msp", "FR_retired_branches_mispred", 0x0 }, \ + { "PAPI_br_tkn", "FR_retired_taken_branches", 0x0 }, \ + { "PAPI_fp_ops", "FP_dispatched_fpu_ops", 0x3 }, \ + { "PAPI_fad_ins", "FP_dispatched_fpu_ops", 0x1 }, \ + { "PAPI_fml_ins", "FP_dispatched_fpu_ops", 0x2 }, \ + { "PAPI_fpu_idl", "FP_cycles_no_fpu_ops_retired", 0x0 }, \ + { "PAPI_tot_cyc", "BU_cpu_clk_unhalted", 0x0 }, \ + { "PAPI_tot_ins", "FR_retired_x86_instr_w_excp_intr", 0x0 }, \ + { "PAPI_l1_dca", "DC_access", 0x0 }, \ + { "PAPI_l1_dcm", "DC_miss", 0x0 }, \ + { "PAPI_l1_ldm", "DC_refill_from_L2", 0xe }, \ + { "PAPI_l1_stm", "DC_refill_from_L2", 0x10 }, \ + { "PAPI_l1_ica", "IC_fetch", 0x0 }, \ + { "PAPI_l1_icm", "IC_miss", 0x0 }, \ + { "PAPI_l1_icr", "IC_fetch", 0x0 }, \ + { "PAPI_l2_dch", "DC_refill_from_L2", 0x1e }, \ + { "PAPI_l2_dcm", "DC_refill_from_system", 0x1e }, \ + { "PAPI_l2_dcr", "DC_refill_from_L2", 0xe }, \ + { "PAPI_l2_dcw", "DC_refill_from_L2", 0x10 }, \ + { "PAPI_l2_ich", "IC_refill_from_L2", 0x0 }, \ + { "PAPI_l2_icm", "IC_refill_from_system", 0x0 }, \ + { "PAPI_l2_ldm", "DC_refill_from_system", 0xe }, \ + { "PAPI_l2_stm", "DC_refill_from_system", 0x10 }, \ + { "PAPI_res_stl", "FR_dispatch_stalls", 0x0 }, \ + { "PAPI_stl_icy", "FR_nothing_to_dispatch", 0x0 }, \ + { "PAPI_hw_int", "FR_taken_hardware_intrs", 0x0 } + +#define OPT_cmn_generic_events \ + { "PAPI_tlb_dm", "DC_dtlb_L1_miss_L2_miss", 0x0 }, \ + { "PAPI_tlb_im", "IC_itlb_L1_miss_L2_miss", 0x0 }, \ + { "PAPI_fp_ins", "FR_retired_fpu_instr", 0xd }, \ + { "PAPI_vec_ins", "FR_retired_fpu_instr", 0x4 } + +#define AMD_FAMILY_10h_generic_events \ + { "PAPI_tlb_dm", "DC_dtlb_L1_miss_L2_miss", 0x7 }, \ + { "PAPI_tlb_im", "IC_itlb_L1_miss_L2_miss", 0x3 }, \ + { "PAPI_l3_dcr", "L3_read_req", 0xf1 }, \ + { "PAPI_l3_icr", "L3_read_req", 0xf2 }, \ + { "PAPI_l3_tcr", "L3_read_req", 0xf7 }, \ + { "PAPI_l3_stm", "L3_miss", 0xf4 }, \ + { "PAPI_l3_ldm", "L3_miss", 0xf3 }, \ + { "PAPI_l3_tcm", "L3_miss", 0xf7 } + +static amd_event_t opt_events_rev_E[] = { + AMD_cmn_events, + OPT_events, + OPT_RevD_events, + OPT_RevE_events, + EV_END +}; + +static amd_event_t family_10h_events[] = { + AMD_cmn_events, + OPT_RevE_events, + AMD_FAMILY_10h_cmn_events, + EV_END +}; + +static amd_generic_event_t opt_generic_events[] = { + AMD_cmn_generic_events, + OPT_cmn_generic_events, + GEN_EV_END +}; + +static amd_generic_event_t family_10h_generic_events[] = { + AMD_cmn_generic_events, + AMD_FAMILY_10h_generic_events, + GEN_EV_END +}; + +static amd_event_t *amd_events = NULL; +static uint_t amd_family; +static amd_generic_event_t *amd_generic_events = NULL; + +#define BITS(v, u, l) (((v) >> (l)) & ((1 << (1 + (u) - (l))) - 1)) +#define OPTERON_FAMILY 0x0f +#define AMD_FAMILY_10H 0x10 + +static int +opt_pcbe_init (void) +{ + amd_family = cpuid_getfamily (); + /* + * Make sure this really _is_ an Opteron or Athlon 64 system. The kernel + * loads this module based on its name in the module directory, but it + * could have been renamed. + */ + if (cpuid_getvendor () != X86_VENDOR_AMD + || (amd_family != OPTERON_FAMILY && amd_family != AMD_FAMILY_10H)) + return (-1); + + /* + * Figure out processor revision here and assign appropriate + * event configuration. + */ + if (amd_family == OPTERON_FAMILY) + { + amd_events = opt_events_rev_E; + amd_generic_events = opt_generic_events; + } + else + { + amd_events = family_10h_events; + amd_generic_events = family_10h_generic_events; + } + return (0); +} + +static uint_t +opt_pcbe_ncounters (void) +{ + return (4); +} + +static const char * +opt_pcbe_impl_name (void) +{ + if (amd_family == OPTERON_FAMILY) + return ("AMD Opteron & Athlon64"); + else if (amd_family == AMD_FAMILY_10H) + return ("AMD Family 10h"); + else + return ("Unknown AMD processor"); +} + +static const char * +opt_pcbe_cpuref (void) +{ + if (amd_family == OPTERON_FAMILY) + return GTXT ("See Chapter 10 of the \"BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD Opteron Processors,\"\nAMD publication #26094"); + else if (amd_family == AMD_FAMILY_10H) + return GTXT ("See section 3.15 of the \"BIOS and Kernel Developer's Guide (BKDG) For AMD Family 10h Processors,\"\nAMD publication #31116"); + else + return GTXT ("Unknown AMD processor"); +} + +static int +opt_pcbe_get_events (hwcf_hwc_cb_t *hwc_cb) +{ + int count = 0; + for (uint_t kk = 0; amd_events && amd_events[kk].name; kk++) + for (uint_t jj = 0; jj < opt_pcbe_ncounters (); jj++) + { + hwc_cb (jj, amd_events[kk].name); + count++; + } + for (uint_t kk = 0; amd_generic_events && amd_generic_events[kk].name; kk++) + for (uint_t jj = 0; jj < opt_pcbe_ncounters (); jj++) + { + hwc_cb (jj, amd_generic_events[kk].name); + count++; + } + return count; +} + +static int +opt_pcbe_get_eventnum (const char *eventname, uint_t pmc, eventsel_t *eventsel, + eventsel_t *event_valid_umask, uint_t *pmc_sel) +{ + uint_t kk; + *pmc_sel = pmc; /* for AMD, pmc doesn't need to be adjusted */ + *eventsel = (eventsel_t) - 1; + *event_valid_umask = 0x0; + + /* search table */ + for (kk = 0; amd_events && amd_events[kk].name; kk++) + { + if (strcmp (eventname, amd_events[kk].name) == 0) + { + *eventsel = EXTENDED_EVNUM_2_EVSEL (amd_events[kk].emask); + *event_valid_umask = amd_events[kk].umask_valid; + return 0; + } + } + + /* search generic */ + int generic = 0; + eventsel_t tmp_umask = 0; + for (kk = 0; amd_generic_events && amd_generic_events[kk].name; kk++) + { + if (strcmp (eventname, amd_generic_events[kk].name) == 0) + { + generic = 1; + eventname = amd_generic_events[kk].event; + tmp_umask = amd_generic_events[kk].umask; + break; + } + } + if (!generic) + return -1; + + /* find real event # for generic event */ + for (kk = 0; amd_events && amd_events[kk].name; kk++) + { + if (strcmp (eventname, amd_events[kk].name) == 0) + { + *eventsel = EXTENDED_EVNUM_2_EVSEL (amd_events[kk].emask); + *eventsel |= (tmp_umask << PERFCTR_UMASK_SHIFT); + *event_valid_umask = 0; /* user umask not allowed w/generic events */ + return 0; + } + } + return -1; +} + +static hdrv_pcbe_api_t hdrv_pcbe_opteron_api = { + opt_pcbe_init, + opt_pcbe_ncounters, + opt_pcbe_impl_name, + opt_pcbe_cpuref, + opt_pcbe_get_events, + opt_pcbe_get_eventnum +}; diff --git a/gprofng/config/bison.m4 b/gprofng/config/bison.m4 new file mode 100644 index 0000000..0493587 --- /dev/null +++ b/gprofng/config/bison.m4 @@ -0,0 +1,92 @@ +# serial 10 + +# Copyright (C) 2002-2006, 2008-2021 Free Software Foundation, Inc. +# 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. + +# There are two types of parser skeletons: +# +# * Those that can be used with any Yacc implementation, including bison. +# For these, in the configure.ac, up to Autoconf 2.69, you could use +# AC_PROG_YACC +# In newer Autoconf versions, however, this macro is broken. See +# https://lists.gnu.org/archive/html/autoconf-patches/2013-03/msg00000.html +# https://lists.gnu.org/archive/html/bug-autoconf/2018-12/msg00001.html +# In the Makefile.am you could use +# $(SHELL) $(YLWRAP) $(srcdir)/foo.y \ +# y.tab.c foo.c \ +# y.tab.h foo.h \ +# y.output foo.output \ +# -- $(YACC) $(YFLAGS) $(AM_YFLAGS) +# or similar. +# +# * Those that make use of Bison extensions. For example, +# - %define api.pure requires bison 2.7 or newer, +# - %precedence requires bison 3.0 or newer. +# For these, in the configure.ac you will need an invocation of +# gl_PROG_BISON([VARIABLE], [MIN_BISON_VERSION]) +# Example: +# gl_PROG_BISON([PARSE_DATETIME_BISON], [2.4]) +# With this preparation, in the Makefile.am there are two ways to formulate +# the invocation. Both are direct, without use of 'ylwrap'. +# (a) You can invoke +# $(VARIABLE) -d $(SOME_BISON_OPTIONS) --output foo.c $(srcdir)/foo.y +# or similar. +# (b) If you want the invocation to honor an YFLAGS=... parameter passed to +# 'configure' or an YFLAGS environment variable present at 'configure' +# time, add an invocation of gl_BISON to the configure.ac, and write +# $(VARIABLE) -d $(YFLAGS) $(AM_YFLAGS) $(srcdir)/foo.y +# or similar. + +# This macro defines the autoconf variable VARIABLE to 'bison' if the specified +# minimum version of bison is found in $PATH, or to ':' otherwise. +AC_DEFUN([gl_PROG_BISON], +[ + AC_CHECK_PROGS([$1], [bison]) + if test -z "$[$1]"; then + ac_verc_fail=yes + else + cat >conftest.y <<_ACEOF +%require "$2" +%% +exp: +_ACEOF + AC_MSG_CHECKING([for bison $2 or newer]) + ac_prog_version=`$$1 --version 2>&1 | sed -n 's/^.*GNU Bison.* \([[0-9]]*\.[[0-9.]]*\).*$/\1/p'` + : ${ac_prog_version:='v. ?.??'} + if $$1 conftest.y -o conftest.c 2>/dev/null; then + ac_prog_version="$ac_prog_version, ok" + ac_verc_fail=no + else + ac_prog_version="$ac_prog_version, bad" + ac_verc_fail=yes + fi + rm -f conftest.y conftest.c + AC_MSG_RESULT([$ac_prog_version]) + fi + if test $ac_verc_fail = yes; then + [$1]=: + fi + AC_SUBST([$1]) +]) + +# This macro sets the autoconf variables YACC (for old-style yacc Makefile +# rules) and YFLAGS (to allow options to be passed as 'configure' time). +AC_DEFUN([gl_BISON], +[ + : ${YACC='bison -o y.tab.c'} +dnl +dnl Declaring YACC & YFLAGS precious will not be necessary after GNULIB +dnl requires an Autoconf greater than 2.59c, but it will probably still be +dnl useful to override the description of YACC in the --help output, re +dnl parse-datetime.y assuming 'bison -o y.tab.c'. + AC_ARG_VAR([YACC], +[The "Yet Another C Compiler" implementation to use. Defaults to +'bison -o y.tab.c'. Values other than 'bison -o y.tab.c' will most likely +break on most systems.])dnl + AC_ARG_VAR([YFLAGS], +[YFLAGS contains the list arguments that will be passed by default to Bison. +This script will default YFLAGS to the empty string to avoid a default value of +'-d' given by some make applications.])dnl +]) diff --git a/gprofng/configure b/gprofng/configure new file mode 100755 index 0000000..3cf4dc7 --- /dev/null +++ b/gprofng/configure @@ -0,0 +1,19350 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for gprofng 2.38.50. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + 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 + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1 + + 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\" ) || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 </dev/null +exec 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='gprofng' +PACKAGE_TARNAME='gprofng' +PACKAGE_VERSION='2.38.50' +PACKAGE_STRING='gprofng 2.38.50' +PACKAGE_BUGREPORT='' +PACKAGE_URL='' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +enable_option_checking=no +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +BUILD_SUBDIRS +GPROFNG_LIBDIR +GPROFNG_CPPFLAGS +GPROFNG_CFLAGS +LD_NO_AS_NEEDED +BUILD_MAN_FALSE +BUILD_MAN_TRUE +HELP2MAN +TCL_TRY_FALSE +TCL_TRY_TRUE +EXPECT +jdk_inc +JAVA +JAVAC +PTHREAD_CFLAGS +PTHREAD_LIBS +PTHREAD_CC +ax_pthread_config +RUN_TESTS_FALSE +RUN_TESTS_TRUE +subdirs +BUILD_SRC_FALSE +BUILD_SRC_TRUE +BUILD_COLLECTOR_FALSE +BUILD_COLLECTOR_TRUE +gprofng_cflags +WERROR +GPROFNG_LIBADD +CXXCPP +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +OBJDUMP +LN_S +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +SED +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +LIBTOOL +ac_ct_AR +AR +RANLIB +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +ac_ct_CXX +CXXFLAGS +CXX +EGREP +GREP +CPP +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +MAINT +MAINTAINER_MODE_FALSE +MAINTAINER_MODE_TRUE +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_maintainer_mode +enable_dependency_tracking +enable_shared +enable_static +with_pic +enable_fast_install +with_gnu_ld +enable_libtool_lock +enable_werror_always +enable_gprofng_tools +with_jdk +enable_gprofng_debug +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +CXX +CXXFLAGS +CCC +CXXCPP' +ac_subdirs_all='libcollector' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures gprofng 2.38.50 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/gprofng] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of gprofng 2.38.50:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-shared[=PKGS] build shared libraries [default=no] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + --enable-werror-always enable -Werror despite compiler version + --disable-gprofng-tools do not build gprofng/src directory + --enable-gprofng-debug Enable debugging output [default=no] + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-pic try to use only PIC/non-PIC objects [default=use + both] + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-jdk=PATH specify prefix directory for installed JDK. + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +gprofng configure 2.38.50 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case <limits.h> declares $2. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link + +# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES +# --------------------------------------------- +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. +ac_fn_c_check_decl () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + as_decl_name=`echo $2|sed 's/ *(.*//'` + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_decl +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by gprofng $as_me 2.38.50, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +am__api_version='1.15' + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='gprofng' + VERSION='2.38.50' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542> + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: <http://www.gnu.org/software/coreutils/>. + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } + # Check whether --enable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then : + enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 +$as_echo "$USE_MAINTAINER_MODE" >&6; } + if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdio.h> +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = xyes; then : + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if ${ac_cv_safe_to_define___extensions__+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar lib "link -lib" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar lib "link -lib" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 +$as_echo_n "checking the archiver ($AR) interface... " >&6; } +if ${am_cv_ar_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + am_cv_ar_interface=ar + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int some_variable = 0; +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=ar + else + am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=lib + else + am_cv_ar_interface=unknown + fi + fi + rm -f conftest.lib libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 +$as_echo "$am_cv_ar_interface" >&6; } + +case $am_cv_ar_interface in +ar) + ;; +lib) + # Microsoft lib, so override with the ar-lib wrapper script. + # FIXME: It is wrong to rewrite AR. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__AR in this case, + # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something + # similar. + AR="$am_aux_dir/ar-lib $AR" + ;; +unknown) + as_fn_error $? "could not determine $AR interface" "$LINENO" 5 + ;; +esac + + +# Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; 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 +else + enable_shared=no +fi + + + + + + + + + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.2.7a' +macro_revision='1.3134' + + + + + + + + + + + + + +ltmain="$ac_aux_dir/ltmain.sh" + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +# 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' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# 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*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 </dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break + ;; + *) + test "$with_gnu_ld" != yes && break + ;; + esac + fi + done + IFS="$lt_save_ifs" +else + lt_cv_path_LD="$LD" # Let the user override the test with a path. +fi +fi + +LD="$lt_cv_path_LD" +if test -n "$LD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 </dev/null` in +*GNU* | *'with BFD'*) + lt_cv_prog_gnu_ld=yes + ;; +*) + lt_cv_prog_gnu_ld=no + ;; +esac +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the nm to test. + lt_nm_to_check="$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 + 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=. + case "$lt_tmp_nm" in + */*|*\\*) tmp_nm="$lt_tmp_nm";; + *) tmp_nm="$ac_dir/$lt_tmp_nm";; + esac + 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 + case `"$tmp_nm" -B "$tmp_nm" 2>&1 | grep -v '^ *$' | sed '1q'` in + *$tmp_nm*) lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p "$tmp_nm" 2>&1 | grep -v '^ *$' | sed '1q'` in + *$tmp_nm*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test "$lt_cv_path_NM" != "no"; 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 + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # 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 + ;; + + 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"; 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"`func_fallback_echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 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 + +fi + +if test -n $lt_cv_sys_max_cmd_len ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 +$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 +$as_echo "$xsi_shell" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 +$as_echo_n "checking whether the shell understands \"+=\"... " >&6; } +lt_shell_append=no +( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 +$as_echo "$lt_shell_append" >&6; } + + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# 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 + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + darwin*) + if test "$GCC" = yes; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 +# which 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. + # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. + if ( test "$lt_cv_nm_interface" = "BSD nm" && 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 + lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' + 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 + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +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 Linux ELF. +linux* | k*bsd*-gnu | kopensolaris*-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*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; 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 + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } +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 + + + + + + + + + + + + +plugin_option= +plugin_names="liblto_plugin.so liblto_plugin-0.dll cyglto_plugin-0.dll" +for plugin in $plugin_names; do + plugin_so=`${CC} ${CFLAGS} --print-prog-name $plugin` + if test x$plugin_so = x$plugin; then + plugin_so=`${CC} ${CFLAGS} --print-file-name $plugin` + fi + if test x$plugin_so != x$plugin; then + plugin_option="--plugin $plugin_so" + break + fi +done + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +else + AR="$ac_cv_prog_AR" +fi + +test -z "$AR" && AR=ar +if test -n "$plugin_option"; then + if $AR --help 2>&1 | grep -q "\--plugin"; then + touch conftest.c + $AR $plugin_option rc conftest.a conftest.c + if test "$?" != 0; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Failed: $AR $plugin_option rc" >&5 +$as_echo "$as_me: WARNING: Failed: $AR $plugin_option rc" >&2;} + else + AR="$AR $plugin_option" + fi + rm -f conftest.* + fi +fi +test -z "$AR_FLAGS" && AR_FLAGS=cru + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: +if test -n "$plugin_option" && test "$RANLIB" != ":"; then + if $RANLIB --help 2>&1 | grep -q "\--plugin"; then + RANLIB="$RANLIB $plugin_option" + fi +fi + + + + + + +# 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 + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# 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 + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# 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 "$host_cpu" = ia64; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BCDRT]' + ;; +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 + +# 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 -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$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 -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/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 + # and D for any global 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};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print 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 + + # 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 { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 + (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && 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 +#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. */ +const struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$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_save_LIBS="$LIBS" + lt_save_CFLAGS="$CFLAGS" + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS="$lt_save_LIBS" + CFLAGS="$lt_save_CFLAGS" + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + + + + + + + + + + + + + + + + + + + + + + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test "x$enable_libtool_lock" != xno && 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 which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; 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 which ABI we are using. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test "$lt_cv_prog_gnu_ld" = yes; 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* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; 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" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +sparc*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) LD="${LD-ld} -m elf64_sparc" ;; + *) + 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" + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -f conftest && test ! -s conftest.err && test $_lt_result = 0 && $GREP forced_load conftest 2>&1 >/dev/null; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + 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 "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; 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 "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + + + + +# Set options + + + + enable_dlopen=no + + + enable_win32_dll=no + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; 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 +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; pic_mode="$withval" +else + pic_mode=default +fi + + +test -z "$pic_mode" && pic_mode=default + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; 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 +else + enable_fast_install=yes +fi + + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +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 "X${COLLECT_NAMES+set}" != Xset; 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 + +for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + +# 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/${ac_tool_prefix}file; then + lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" + 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 +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/file; then + lt_cv_path_MAGIC_CMD="$ac_dir/file" + 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 +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC="$CC" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$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);}' + + + + + + + +# 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 + +# 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 +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* + +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* + + +## 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_prog_compiler_no_builtin_flag= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # 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\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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 + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } + + if test "$GCC" = yes; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + lt_prog_compiler_pic='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-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_prog_compiler_pic='-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 + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + 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_prog_compiler_pic='-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_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + lt_prog_compiler_pic='-Xcompiler -fPIC' + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + 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). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-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_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ F* | *Sun*Fortran*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic" >&5 +$as_echo "$lt_prog_compiler_pic" >&6; } + + + + + + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" + # 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\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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 + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works" = xyes; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + 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>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test x"$lt_cv_prog_compiler_static_works" = xyes; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=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\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $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* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=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\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $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* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_flag_spec_ld= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # 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'. + exclude_expsyms='_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. + 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 "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + esac + + ld_shlibs=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 "$with_gnu_ld" = yes; 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 "$lt_use_gnu_ld_interface" = yes; 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 + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec='${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 + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v 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 "$host_cpu" != ia64; then + ld_shlibs=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 + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$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)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach <jrb3@best.com> says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='${wl}--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$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 (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; 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 + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${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. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='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 "$host_os" = linux-dietlibc; 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 "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='${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 + whole_archive_flag_spec='${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 + whole_archive_flag_spec= + tmp_sharedflag='--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 + whole_archive_flag_spec='${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' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='${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' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='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 + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec= + hardcode_libdir_flag_spec_ld='-rpath $libdir' + archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='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 $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $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 + ld_shlibs=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 + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** 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 + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test "$ld_shlibs" = no; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$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. + hardcode_minus_L=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test "$host_cpu" = ia64; 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 AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | 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 + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + 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. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='${wl}-f,' + + if test "$GCC" = yes; 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 + hardcode_direct=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 + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; 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 "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec='${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. + always_export_symbols=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`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 "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$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. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`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 "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec='${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. + no_undefined_flag=' ${wl}-bernotok' + allow_undefined_flag=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$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)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-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. + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=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. + archive_cmds='$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. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + fix_srcfile_path='`cygpath -w "$srcfile"`' + enable_shared_with_static_runtimes=yes + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds="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}" + module_expsym_cmds="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}" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=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*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_flag_spec_ld='+b $libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # 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) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -b" + 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>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test x"$lt_cv_prog_compiler__b" = xyes; then + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds='$CC -shared $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. + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo(void) {} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + archive_expsym_cmds='$CC -shared $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 +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + else + archive_cmds='$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' + archive_expsym_cmds='$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 + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$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 + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$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 + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_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' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$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' + archive_expsym_cmds='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 + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared ${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='' + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='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}' + archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='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 + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=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 "$GCC" = yes; then + whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='${wl}-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT 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. + no_undefined_flag='${wl}-z,text' + allow_undefined_flag='${wl}-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='${wl}-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test "$ld_shlibs" = no && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds 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. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test "$GCC" = yes; 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` + 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" + else + 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 + +case $host_os in +aix3*) + version_type=linux + 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 + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; 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 + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib<name>.so + # instead of lib<name>.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}' + else + # 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' + fi + 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%'\''`; test $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 + 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,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) + 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}' + + 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 + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # 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`' + + 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 + 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} $libname${shared_ext}' + 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 + 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=yes + 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 "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + 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 + 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 "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + 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 + ;; + +# This must be Linux ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux + 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 + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + 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 + + # Append ld.so.conf contents 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="/lib /usr/lib $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 + 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*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + 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 + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +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 + 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 "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux + 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 + 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=freebsd-elf + 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 "$with_gnu_ld" = yes; 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 + 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 + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test "X$hardcode_automatic" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct" != no && + # 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 "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test "$hardcode_action" = relink || + test "$inherit_rpath" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test "x$enable_dlopen" != xyes; 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && 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" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line 12092 "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include <stdio.h> + +#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 -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +void fnord () __attribute__((visibility("default"))); +#endif + +void fnord () { int i=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 { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line 12198 "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include <stdio.h> + +#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 -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +void fnord () __attribute__((visibility("default"))); +#endif + +void fnord () { int i=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 { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + 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 + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +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" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +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" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report which library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test "$can_build_shared" = "no" && 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 "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +else + _lt_caught_CXX_error=yes +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_flag_spec_ld_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +reload_flag_CXX=$reload_flag +reload_cmds_CXX=$reload_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$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 "$_lt_caught_CXX_error" != yes; 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. + + + + + + +# 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 + + + # save warnings/boilerplate of simple test code + 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* + + 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* + + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + 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++"} + compiler=$CC + compiler_CXX=$CC + for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 </dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break + ;; + *) + test "$with_gnu_ld" != yes && break + ;; + esac + fi + done + IFS="$lt_save_ifs" +else + lt_cv_path_LD="$LD" # Let the user override the test with a path. +fi +fi + +LD="$lt_cv_path_LD" +if test -n "$LD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 </dev/null` in +*GNU* | *'with BFD'*) + lt_cv_prog_gnu_ld=yes + ;; +*) + lt_cv_prog_gnu_ld=no + ;; +esac +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_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 "$with_gnu_ld" = yes; then + archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$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' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${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 + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + 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. + archive_cmds_CXX='$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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; 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 + # need to do runtime linking. + 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 + ;; + 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. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='${wl}-f,' + + if test "$GXX" = yes; 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 + hardcode_direct_CXX=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 + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; 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 "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec_CXX='${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. + always_export_symbols_CXX=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_CXX='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`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 "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$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. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`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 "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_CXX='${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. + no_undefined_flag_CXX=' ${wl}-bernotok' + allow_undefined_flag_CXX=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + fi + archive_cmds_need_lc_CXX=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach <jrb3@best.com> says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-all-symbols' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$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 (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; 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 + ld_shlibs_CXX=no + fi + ;; + darwin* | rhapsody*) + + + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + else + whole_archive_flag_spec_CXX='' + fi + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds_CXX="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}" + module_expsym_cmds_CXX="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}" + if test "$lt_cv_apple_cc_single_mod" != "yes"; then + archive_cmds_CXX="\$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}" + archive_expsym_cmds_CXX="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 + ld_shlibs_CXX=no + fi + + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; + + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + + gnu*) + ;; + + haiku*) + archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs_CXX=yes + ;; + + hpux9*) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='${wl}-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=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 + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $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 "$GXX" = yes; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=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 + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$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 "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${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 + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${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. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='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++ + archive_cmds_CXX='$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. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + archive_cmds_CXX='$CC -shared -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 + archive_cmds_CXX='$CC -shared -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 + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-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. + archive_cmds_CXX='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' + archive_expsym_cmds_CXX='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"' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$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."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$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 + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5].* | *pgcpp\ [1-5].*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' + old_archive_cmds_CXX='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 | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='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 | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='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 | $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 + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='$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 + + hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${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++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$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 + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # 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 + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds_CXX='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 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='${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' + compiler_needs_object_CXX=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. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=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*) + ld_shlibs_CXX=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + ld_shlibs_CXX=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='${wl}-E' + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + ld_shlibs_CXX=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. + archive_cmds_CXX='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' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$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' + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$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' + archive_expsym_cmds_CXX='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' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac + + hardcode_libdir_separator_CXX=: + + # 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 "$GXX" = yes && test "$with_gnu_ld" = no; then + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + archive_cmds_CXX='$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' + ;; + *) + archive_cmds_CXX='$CC -shared -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 + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # 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 + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='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' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=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?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=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. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$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. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + no_undefined_flag_CXX=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared -nostdlib ${wl}-M $wl$lib.exp -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. + archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='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 -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 + + hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='${wl}-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT 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. + no_undefined_flag_CXX='${wl}-z,text' + allow_undefined_flag_CXX='${wl}-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ + '"$old_archive_cmds_CXX" + reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ + '"$reload_cmds_CXX" + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$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 + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test "$ld_shlibs_CXX" = no && can_build_shared=no + + GCC_CXX="$GXX" + LD_CXX="$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... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF + +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; 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 $p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + else + prev= + fi + + if test "$pre_test_object_deps_done" = no; then + case $p 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 "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX="${prev}${p}" + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${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 "$postdeps_CXX"; then + postdeps_CXX="${prev}${p}" + else + postdeps_CXX="${postdeps_CXX} ${prev}${p}" + fi + fi + ;; + + *.$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 "$pre_test_object_deps_done" = no; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX="$p" + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX="$p" + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext + +# PORTME: override above test on systems where it is broken +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. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac + + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } + + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + lt_prog_compiler_pic_CXX='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-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_prog_compiler_pic_CXX='-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 + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static_CXX= + ;; + 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_prog_compiler_pic_CXX=-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_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix[4-9]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--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 + ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+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_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-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_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd*) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_prog_compiler_pic_CXX" >&6; } + + + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" + # 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\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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 + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + 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>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then + : +else + lt_prog_compiler_static_CXX= +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=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\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $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* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=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\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $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* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + 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 AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;/^.*[ ]__nm__/s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test "$ld_shlibs_CXX" = no && can_build_shared=no + +with_gnu_ld_CXX=$with_gnu_ld + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds_CXX 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. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc_CXX=no + else + lt_cv_archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } + archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +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 + +case $host_os in +aix3*) + version_type=linux + 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 + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; 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 + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib<name>.so + # instead of lib<name>.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}' + else + # 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' + fi + 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%'\''`; test $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 + 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,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) + 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}' + + ;; + 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 + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # 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`' + + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + 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} $libname${shared_ext}' + 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 + 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=yes + 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 "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + 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 + 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 "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + 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 + ;; + +# This must be Linux ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux + 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 + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + 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 + + # Append ld.so.conf contents 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="/lib /usr/lib $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 + 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*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + 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 + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +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 + 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 "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux + 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 + 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=freebsd-elf + 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 "$with_gnu_ld" = yes; 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 + 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 + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test "X$hardcode_automatic_CXX" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct_CXX" != no && + # 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 "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && + test "$hardcode_minus_L_CXX" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } + +if test "$hardcode_action_CXX" = relink || + test "$inherit_rpath_CXX" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + + fi # test -n "$compiler" + + CC=$lt_save_CC + 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 "$_lt_caught_CXX_error" != yes + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + + +GPROFNG_LIBADD="-L../../libiberty -liberty" +if test "$enable_shared" = "yes"; then + GPROFNG_LIBADD="-L../../libiberty/pic -liberty" +fi + + +# Figure out what compiler warnings we can enable. +# See config/warnings.m4 for details. + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +WERROR= +# Check whether --enable-werror-always was given. +if test "${enable_werror_always+set}" = set; then : + enableval=$enable_werror_always; +else + enable_werror_always=no +fi + +if test $enable_werror_always = yes; then : + WERROR="$WERROR${WERROR:+ }-Werror" +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +gprofng_cflags= +save_CFLAGS="$CFLAGS" +for real_option in -Wall; do + # Do the check with the no- prefix removed since gcc silently + # accepts any -Wno-* option on purpose + case $real_option in + -Wno-*) option=-W`expr x$real_option : 'x-Wno-\(.*\)'` ;; + *) option=$real_option ;; + esac + as_acx_Woption=`$as_echo "acx_cv_prog_cc_warning_$option" | $as_tr_sh` + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $option" >&5 +$as_echo_n "checking whether $CC supports $option... " >&6; } +if eval \${$as_acx_Woption+:} false; then : + $as_echo_n "(cached) " >&6 +else + CFLAGS="$option" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$as_acx_Woption=yes" +else + eval "$as_acx_Woption=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +eval ac_res=\$$as_acx_Woption + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + if test `eval 'as_val=${'$as_acx_Woption'};$as_echo "$as_val"'` = yes; then : + gprofng_cflags="$gprofng_cflags${gprofng_cflags:+ }$real_option" +fi + done +CFLAGS="$save_CFLAGS" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +gprofng_cppflags="-U_ASM" +build_collector= +build_src= + +# This is annoying: it means we have to pass --enable-shared explicitly to +# get gprofng, while the configure default is supposed to be that shared libs +# are on by default. But as long as libiberty has code like this, so must +# we... + + case "${target}" in + x86_64-*-linux*) + build_src=true + build_collector=true + ;; + i?86-*-linux*) + build_collector=true + build_collector=true + ;; + aarch64-*-linux*) + build_src=true + build_collector=true + ;; + esac + # Check whether --enable-gprofng-tools was given. +if test "${enable_gprofng_tools+set}" = set; then : + enableval=$enable_gprofng_tools; build_src=$enableval +fi + + + if test x$build_collector = xtrue; then + BUILD_COLLECTOR_TRUE= + BUILD_COLLECTOR_FALSE='#' +else + BUILD_COLLECTOR_TRUE='#' + BUILD_COLLECTOR_FALSE= +fi + + if test x$build_src = xtrue; then + BUILD_SRC_TRUE= + BUILD_SRC_FALSE='#' +else + BUILD_SRC_TRUE='#' + BUILD_SRC_FALSE= +fi + + +run_tests=false +if test x$build_collector = xtrue; then + + +subdirs="$subdirs libcollector" + + if test x${host} = x${target}; then + run_tests=true + fi +fi + if test x$run_tests = xtrue; then + RUN_TESTS_TRUE= + RUN_TESTS_FALSE='#' +else + RUN_TESTS_TRUE='#' + RUN_TESTS_FALSE= +fi + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on Tru64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + if test "x$PTHREAD_CC" != "x"; then : + CC="$PTHREAD_CC" +fi + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS" >&5 +$as_echo_n "checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_join (); +int +main () +{ +return pthread_join (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "AX_PTHREAD_ZOS_MISSING" >/dev/null 2>&1; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&5 +$as_echo "$as_me: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&2;} +fi +rm -f conftest* + + ;; + + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" + ;; +esac + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +if test "x$GCC" = "xyes"; then : + ax_pthread_flags="-pthread -pthreads $ax_pthread_flags" +fi + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled + +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; + + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; +esac +if test "x$ax_pthread_check_macro" = "x--"; then : + ax_pthread_check_cond=0 +else + ax_pthread_check_cond="!defined($ax_pthread_check_macro)" +fi + +# Are we compiling with Clang? + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC is Clang" >&5 +$as_echo_n "checking whether $CC is Clang... " >&6; } +if ${ax_cv_PTHREAD_CLANG+:} false; then : + $as_echo_n "(cached) " >&6 +else + ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "AX_PTHREAD_CC_IS_CLANG" >/dev/null 2>&1; then : + ax_cv_PTHREAD_CLANG=yes +fi +rm -f conftest* + + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG" >&5 +$as_echo "$ax_cv_PTHREAD_CLANG" >&6; } +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + +ax_pthread_clang_warning=no + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way + +if test "x$ax_pthread_clang" = "xyes"; then + + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + PTHREAD_CFLAGS="-pthread" + PTHREAD_LIBS= + + ax_pthread_ok=yes + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread" >&5 +$as_echo_n "checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread... " >&6; } +if ${ax_cv_PTHREAD_CLANG_NO_WARN_FLAG+:} false; then : + $as_echo_n "(cached) " >&6 +else + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + if test "x$ax_pthread_try" = "xunknown"; then : + break +fi + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void){return 0;} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_link="$ax_pthread_2step_ac_link" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void){return 0;} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + if test "x$ax_pthread_try" = "x"; then : + ax_pthread_try=no +fi + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&5 +$as_echo "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&6; } + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 +$as_echo_n "checking whether pthreads work without any flags... " >&6; } + ;; + + -mt,pthread) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with -mt -lpthread" >&5 +$as_echo_n "checking whether pthreads work with -mt -lpthread... " >&6; } + PTHREAD_CFLAGS="-mt" + PTHREAD_LIBS="-lpthread" + ;; + + -*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $ax_pthread_try_flag" >&5 +$as_echo_n "checking whether pthreads work with $ax_pthread_try_flag... " >&6; } + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + # Extract the first word of "pthread-config", so it can be a program name with args. +set dummy pthread-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ax_pthread_config+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ax_pthread_config"; then + ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ax_pthread_config="yes" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" +fi +fi +ax_pthread_config=$ac_cv_prog_ax_pthread_config +if test -n "$ax_pthread_config"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 +$as_echo "$ax_pthread_config" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "x$ax_pthread_config" = "xno"; then : + continue +fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$ax_pthread_try_flag" >&5 +$as_echo_n "checking for the pthreads library -l$ax_pthread_try_flag... " >&6; } + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <pthread.h> +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; } +int +main () +{ +pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test "x$ax_pthread_ok" = "xyes"; then : + break +fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 +$as_echo_n "checking for joinable pthread attribute... " >&6; } +if ${ax_cv_PTHREAD_JOINABLE_ATTR+:} false; then : + $as_echo_n "(cached) " >&6 +else + ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <pthread.h> +int +main () +{ +int attr = $ax_pthread_attr; return attr /* ; */ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_JOINABLE_ATTR" >&5 +$as_echo "$ax_cv_PTHREAD_JOINABLE_ATTR" >&6; } + if test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"; then : + +cat >>confdefs.h <<_ACEOF +#define PTHREAD_CREATE_JOINABLE $ax_cv_PTHREAD_JOINABLE_ATTR +_ACEOF + + ax_pthread_joinable_attr_defined=yes + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether more special flags are required for pthreads" >&5 +$as_echo_n "checking whether more special flags are required for pthreads... " >&6; } +if ${ax_cv_PTHREAD_SPECIAL_FLAGS+:} false; then : + $as_echo_n "(cached) " >&6 +else + ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_SPECIAL_FLAGS" >&5 +$as_echo "$ax_cv_PTHREAD_SPECIAL_FLAGS" >&6; } + if test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"; then : + PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 +$as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; } +if ${ax_cv_PTHREAD_PRIO_INHERIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <pthread.h> +int +main () +{ +int i = PTHREAD_PRIO_INHERIT; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_cv_PTHREAD_PRIO_INHERIT=yes +else + ax_cv_PTHREAD_PRIO_INHERIT=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 +$as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } + if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"; then : + +$as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h + + ax_pthread_prio_inherit_defined=yes + +fi + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + case "x/$CC" in #( + x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) : + #handle absolute path differently from PATH based program lookup + case "x$CC" in #( + x/*) : + if as_fn_executable_p ${CC}_r; then : + PTHREAD_CC="${CC}_r" +fi ;; #( + *) : + for ac_prog in ${CC}_r +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PTHREAD_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_PTHREAD_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 +$as_echo "$PTHREAD_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PTHREAD_CC" && break +done +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + ;; +esac ;; #( + *) : + ;; +esac + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + + + + + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "x$ax_pthread_ok" = "xyes"; then + +$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h + + : +else + ax_pthread_ok=no + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +# Specify a location for JDK +enable_gprofng_jp= +jdk_inc= + +# Check whether --with-jdk was given. +if test "${with_jdk+set}" = set; then : + withval=$with_jdk; +fi + + +if test "x$with_jdk" != x; then + jdk_inc="-I$with_jdk/include -I$with_jdk/include/linux" + enable_gprofng_jp=yes +else + # Extract the first word of "javac", so it can be a program name with args. +set dummy javac; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_JAVAC+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $JAVAC in + [\\/]* | ?:[\\/]*) + ac_cv_path_JAVAC="$JAVAC" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_JAVAC="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_JAVAC" && ac_cv_path_JAVAC="javac" + ;; +esac +fi +JAVAC=$ac_cv_path_JAVAC +if test -n "$JAVAC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $JAVAC" >&5 +$as_echo "$JAVAC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -f $JAVAC; then + x=`readlink -f $JAVAC` + x=`dirname $x` + x=`dirname $x` + if ! test -f $x/include/jni.h; then + x=`dirname $x` + fi + if test -f $x/include/jni.h; then + jdk_inc="-I$x/include -I$x/include/linux" + enable_gprofng_jp=yes + fi + fi +fi +if test "x$enable_gprofng_jp" = x; then + # Extract the first word of "java", so it can be a program name with args. +set dummy java; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_JAVA+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $JAVA in + [\\/]* | ?:[\\/]*) + ac_cv_path_JAVA="$JAVA" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_JAVA="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_JAVA" && ac_cv_path_JAVA="java" + ;; +esac +fi +JAVA=$ac_cv_path_JAVA +if test -n "$JAVA"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $JAVA" >&5 +$as_echo "$JAVA" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -f $JAVA; then + x=`readlink -f $JAVA` + x=`dirname $x` + x=`dirname $x` + if ! test -f $x/include/jni.h; then + x=`dirname $x` + fi + if test -f $x/include/jni.h; then + jdk_inc="-I$x/include -I$x/include/linux" + enable_gprofng_jp=yes + fi + fi +fi +if test "x$enable_gprofng_jp" = x; then + ac_fn_c_check_header_compile "$LINENO" "jni.h" "ac_cv_header_jni_h" " +" +if test "x$ac_cv_header_jni_h" = xyes; then : + enable_gprofng_jp=yes +fi + + +fi +if test "x$enable_gprofng_jp" = x; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find the JDK include directory. + gprofng will be build without support for profiling Java applications. + Use --with-jdk=PATH to specify directory for the installed JDK" >&5 +$as_echo "$as_me: WARNING: Cannot find the JDK include directory. + gprofng will be build without support for profiling Java applications. + Use --with-jdk=PATH to specify directory for the installed JDK" >&2;} +else + +$as_echo "#define GPROFNG_JAVA_PROFILING 1" >>confdefs.h + +fi + + +DEBUG= + # Check whether --enable-gprofng-debug was given. +if test "${enable_gprofng_debug+set}" = set; then : + enableval=$enable_gprofng_debug; + case "$enableval" in + yes|no) ;; + *) as_fn_error $? "Argument to enable/disable gprofng-debug must be yes or no" "$LINENO" 5 ;; + esac + +else + enable_gprofng_debug=no +fi + + +if test "${enable_gprofng_debug}" = yes; then + +$as_echo "#define DEBUG 1" >>confdefs.h + +fi + +# Check if linker supports --as-needed and --no-as-needed options. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking linker --as-needed support" >&5 +$as_echo_n "checking linker --as-needed support... " >&6; } +if ${bfd_cv_ld_as_needed+:} false; then : + $as_echo_n "(cached) " >&6 +else + bfd_cv_ld_as_needed=no + if $LD --help 2>/dev/null | grep as-needed > /dev/null; then + bfd_cv_ld_as_needed=yes + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $bfd_cv_ld_as_needed" >&5 +$as_echo "$bfd_cv_ld_as_needed" >&6; } + +no_as_needed= +if test x"$bfd_cv_ld_as_needed" = xyes; then + no_as_needed='-Wl,--no-as-needed' +fi + +# Extract the first word of "expect", so it can be a program name with args. +set dummy expect; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_EXPECT+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $EXPECT in + [\\/]* | ?:[\\/]*) + ac_cv_path_EXPECT="$EXPECT" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_EXPECT="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +EXPECT=$ac_cv_path_EXPECT +if test -n "$EXPECT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $EXPECT" >&5 +$as_echo "$EXPECT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl supporting try/catch" >&5 +$as_echo_n "checking for Tcl supporting try/catch... " >&6; } +if ${ac_cv_libctf_tcl_try+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_libctf_tcl_try=`if test -z $EXPECT; then echo no; else $EXPECT << EOF +if [llength [info commands try]] then { puts yes } else { puts no } +EOF +fi` + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libctf_tcl_try" >&5 +$as_echo "$ac_cv_libctf_tcl_try" >&6; } + if test "${ac_cv_libctf_tcl_try}" = yes; then + TCL_TRY_TRUE= + TCL_TRY_FALSE='#' +else + TCL_TRY_TRUE='#' + TCL_TRY_FALSE= +fi + + + +# Generate manpages, if possible. +if test $cross_compiling = no; then + +HELP2MAN=${HELP2MAN-"${am_missing_run}help2man"} + + build_man=true +else + build_man=false +fi + if test x$build_man = xtrue; then + BUILD_MAN_TRUE= + BUILD_MAN_FALSE='#' +else + BUILD_MAN_TRUE='#' + BUILD_MAN_FALSE= +fi + + +LD_NO_AS_NEEDED=${no_as_needed} + +GPROFNG_CFLAGS=${gprofng_cflags} + +GPROFNG_CPPFLAGS=${gprofng_cppflags} + +GPROFNG_LIBDIR=${libdir} + + +ac_fn_c_check_decl "$LINENO" "basename" "ac_cv_have_decl_basename" "$ac_includes_default" +if test "x$ac_cv_have_decl_basename" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_BASENAME $ac_have_decl +_ACEOF + +for ac_func in strsignal +do : + ac_fn_c_check_func "$LINENO" "strsignal" "ac_cv_func_strsignal" +if test "x$ac_cv_func_strsignal" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STRSIGNAL 1 +_ACEOF + +fi +done + + + + +ac_config_files="$ac_config_files Makefile src/Makefile gp-display-html/Makefile doc/Makefile" + +ac_config_headers="$ac_config_headers config.h:common/config.h.in" + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_COLLECTOR_TRUE}" && test -z "${BUILD_COLLECTOR_FALSE}"; then + as_fn_error $? "conditional \"BUILD_COLLECTOR\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_SRC_TRUE}" && test -z "${BUILD_SRC_FALSE}"; then + as_fn_error $? "conditional \"BUILD_SRC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${RUN_TESTS_TRUE}" && test -z "${RUN_TESTS_FALSE}"; then + as_fn_error $? "conditional \"RUN_TESTS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${TCL_TRY_TRUE}" && test -z "${TCL_TRY_FALSE}"; then + as_fn_error $? "conditional \"TCL_TRY\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_MAN_TRUE}" && test -z "${BUILD_MAN_FALSE}"; then + as_fn_error $? "conditional \"BUILD_MAN\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + 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 + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by gprofng $as_me 2.38.50, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +gprofng config.status 2.38.50 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + + +# 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' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_ld='`$ECHO "$hardcode_libdir_flag_spec_ld" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +fix_srcfile_path='`$ECHO "$fix_srcfile_path" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' +predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' +predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' +postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' +reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' +reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_ld_CXX='`$ECHO "$hardcode_libdir_flag_spec_ld_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' +fix_srcfile_path_CXX='`$ECHO "$fix_srcfile_path_CXX" | $SED "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' + +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 SHELL \ +ECHO \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +AR \ +AR_FLAGS \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_wl \ +lt_prog_compiler_pic \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_flag_spec_ld \ +hardcode_libdir_separator \ +fix_srcfile_path \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +reload_flag_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_flag_spec_ld_CXX \ +hardcode_libdir_separator_CXX \ +fix_srcfile_path_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +sys_lib_dlsearch_path_spec \ +reload_cmds_CXX \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' +xsi_shell='$xsi_shell' +lt_shell_append='$lt_shell_append' + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile' + + + + + + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "gp-display-html/Makefile") CONFIG_FILES="$CONFIG_FILES gp-display-html/Makefile" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h:common/config.h.in" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' <confdefs.h | sed ' +s/'"$ac_delim"'/"\\\ +"/g' >>$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "libtool":C) + + # See if we are running on zsh, and set the options which 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 + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $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. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# 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 +# 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="CXX " + +# ### BEGIN LIBTOOL CONFIG + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method == "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# 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 +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# If ld is used when linking, flag to hardcode \$libdir into a binary +# during linking. This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# 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. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path=$lt_fix_srcfile_path + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_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 "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain="$ac_aux_dir/ltmain.sh" + + + # 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 '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + case $xsi_shell in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac +} + +# func_basename file +func_basename () +{ + func_basename_result="${1##*/}" +} + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}" +} + +# func_stripname 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). +func_stripname () +{ + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"} +} + +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=${1%%=*} + func_opt_split_arg=${1#*=} +} + +# func_lo2o object +func_lo2o () +{ + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac +} + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=${1%.*}.lo +} + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=$(( $* )) +} + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=${#1} +} + +_LT_EOF + ;; + *) # Bourne compatible functions. + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "${1}" | $SED "$basename"` +} + + +# func_stripname 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). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} + +# sed scripts: +my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q' +my_sed_long_arg='1s/^-[^=]*=//' + +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=`$ECHO "${1}" | $SED "$my_sed_long_opt"` + func_opt_split_arg=`$ECHO "${1}" | $SED "$my_sed_long_arg"` +} + +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` +} + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` +} + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "$@"` +} + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` +} + +_LT_EOF +esac + +case $lt_shell_append in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$1+=\$2" +} +_LT_EOF + ;; + *) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$1=\$$1\$2" +} + +_LT_EOF + ;; + esac + + + sed -n '/^# Generated shell functions inserted here/,$p' "$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" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# If ld is used when linking, flag to hardcode \$libdir into a binary +# during linking. This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# 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. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path=$lt_fix_srcfile_path_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi + +# +# CONFIG_SUBDIRS section. +# +if test "$no_recursion" != yes; then + + # Remove --cache-file, --srcdir, and --disable-option-checking arguments + # so they do not pile up. + ac_sub_configure_args= + ac_prev= + eval "set x $ac_configure_args" + shift + for ac_arg + do + if test -n "$ac_prev"; then + ac_prev= + continue + fi + case $ac_arg in + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \ + | --c=*) + ;; + --config-cache | -C) + ;; + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + ;; + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + ;; + --disable-option-checking) + ;; + *) + case $ac_arg in + *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append ac_sub_configure_args " '$ac_arg'" ;; + esac + done + + # Always prepend --prefix to ensure using the same prefix + # in subdir configurations. + ac_arg="--prefix=$prefix" + case $ac_arg in + *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + ac_sub_configure_args="'$ac_arg' $ac_sub_configure_args" + + # Pass --silent + if test "$silent" = yes; then + ac_sub_configure_args="--silent $ac_sub_configure_args" + fi + + # Always prepend --disable-option-checking to silence warnings, since + # different subdirs can have different --enable and --with options. + ac_sub_configure_args="--disable-option-checking $ac_sub_configure_args" + + ac_popdir=`pwd` + for ac_dir in : $subdirs; do test "x$ac_dir" = x: && continue + + # Do not complain, so a configure script can configure whichever + # parts of a large source tree are present. + test -d "$srcdir/$ac_dir" || continue + + ac_msg="=== configuring in $ac_dir (`pwd`/$ac_dir)" + $as_echo "$as_me:${as_lineno-$LINENO}: $ac_msg" >&5 + $as_echo "$ac_msg" >&6 + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + cd "$ac_dir" + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f "$ac_srcdir/configure.gnu"; then + ac_sub_configure=$ac_srcdir/configure.gnu + elif test -f "$ac_srcdir/configure"; then + ac_sub_configure=$ac_srcdir/configure + elif test -f "$ac_srcdir/configure.in"; then + # This should be Cygnus configure. + ac_sub_configure=$ac_aux_dir/configure + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no configuration information is in $ac_dir" >&5 +$as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2;} + ac_sub_configure= + fi + + # The recursion is here. + if test -n "$ac_sub_configure"; then + # Make the cache file name correct relative to the subdirectory. + case $cache_file in + [\\/]* | ?:[\\/]* ) ac_sub_cache_file=$cache_file ;; + *) # Relative name. + ac_sub_cache_file=$ac_top_build_prefix$cache_file ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&5 +$as_echo "$as_me: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&6;} + # The eval makes quoting arguments work. + eval "\$SHELL \"\$ac_sub_configure\" $ac_sub_configure_args \ + --cache-file=\"\$ac_sub_cache_file\" --srcdir=\"\$ac_srcdir\"" || + as_fn_error $? "$ac_sub_configure failed for $ac_dir" "$LINENO" 5 + fi + + cd "$ac_popdir" + done +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + diff --git a/gprofng/configure.ac b/gprofng/configure.ac new file mode 100644 index 0000000..8977e8b --- /dev/null +++ b/gprofng/configure.ac @@ -0,0 +1,189 @@ +dnl Process this file with autoconf to produce a configure script. +dnl +dnl Copyright (C) 2021 Free Software Foundation, Inc. +dnl +dnl This file is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 3 of the License, or +dnl (at your option) any later version. +dnl +dnl This program 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 +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; see the file COPYING3. If not see +dnl <http://www.gnu.org/licenses/>. + +m4_include([../bfd/version.m4]) +AC_INIT([gprofng], BFD_VERSION) +AM_INIT_AUTOMAKE([subdir-objects]) +AM_MAINTAINER_MODE + +AC_USE_SYSTEM_EXTENSIONS +AC_PROG_CC +AC_PROG_CXX +AC_PROG_INSTALL +AC_PROG_RANLIB +AM_PROG_AR + +AC_DISABLE_SHARED +LT_INIT + +GPROFNG_LIBADD="-L../../libiberty -liberty" +if test "$enable_shared" = "yes"; then + GPROFNG_LIBADD="-L../../libiberty/pic -liberty" +fi +AC_SUBST(GPROFNG_LIBADD) + +# Figure out what compiler warnings we can enable. +# See config/warnings.m4 for details. + +ACX_PROG_CC_WARNINGS_ARE_ERRORS([manual]) +ACX_PROG_CC_WARNING_OPTS([-Wall], [gprofng_cflags]) +gprofng_cppflags="-U_ASM" +build_collector= +build_src= + +# This is annoying: it means we have to pass --enable-shared explicitly to +# get gprofng, while the configure default is supposed to be that shared libs +# are on by default. But as long as libiberty has code like this, so must +# we... + + case "${target}" in + x86_64-*-linux*) + build_src=true + build_collector=true + ;; + i?86-*-linux*) + build_collector=true + build_collector=true + ;; + aarch64-*-linux*) + build_src=true + build_collector=true + ;; + esac + AC_ARG_ENABLE(gprofng-tools, + AS_HELP_STRING([--disable-gprofng-tools], [do not build gprofng/src directory]), + build_src=$enableval) + +AM_CONDITIONAL([BUILD_COLLECTOR], [test x$build_collector = xtrue]) +AM_CONDITIONAL([BUILD_SRC], [test x$build_src = xtrue]) + +run_tests=false +if test x$build_collector = xtrue; then + AC_CONFIG_SUBDIRS([libcollector]) + if test x${host} = x${target}; then + run_tests=true + fi +fi +AM_CONDITIONAL([RUN_TESTS], [test x$run_tests = xtrue]) +AX_PTHREAD + +# Specify a location for JDK +enable_gprofng_jp= +jdk_inc= +AC_ARG_WITH(jdk, +[AS_HELP_STRING([--with-jdk=PATH], + [specify prefix directory for installed JDK.])]) + +if test "x$with_jdk" != x; then + jdk_inc="-I$with_jdk/include -I$with_jdk/include/linux" + enable_gprofng_jp=yes +else + AC_PATH_PROG([JAVAC], [javac], [javac]) + if test -f $JAVAC; then + x=`readlink -f $JAVAC` + x=`dirname $x` + x=`dirname $x` + if ! test -f $x/include/jni.h; then + x=`dirname $x` + fi + if test -f $x/include/jni.h; then + jdk_inc="-I$x/include -I$x/include/linux" + enable_gprofng_jp=yes + fi + fi +fi +if test "x$enable_gprofng_jp" = x; then + AC_PATH_PROG([JAVA], [java], [java]) + if test -f $JAVA; then + x=`readlink -f $JAVA` + x=`dirname $x` + x=`dirname $x` + if ! test -f $x/include/jni.h; then + x=`dirname $x` + fi + if test -f $x/include/jni.h; then + jdk_inc="-I$x/include -I$x/include/linux" + enable_gprofng_jp=yes + fi + fi +fi +if test "x$enable_gprofng_jp" = x; then + AC_CHECK_HEADER([jni.h], [ enable_gprofng_jp=yes ], [], [] ) +fi +if test "x$enable_gprofng_jp" = x; then + AC_MSG_WARN([ Cannot find the JDK include directory. + gprofng will be build without support for profiling Java applications. + Use --with-jdk=PATH to specify directory for the installed JDK]) +else + AC_DEFINE(GPROFNG_JAVA_PROFILING, 1, [Enable java profiling]) +fi +AC_SUBST(jdk_inc) + +DEBUG= +GCC_ENABLE([gprofng-debug], [no], [], [Enable debugging output]) +if test "${enable_gprofng_debug}" = yes; then + AC_DEFINE(DEBUG, 1, [Enable debugging output.]) +fi + +# Check if linker supports --as-needed and --no-as-needed options. +AC_CACHE_CHECK(linker --as-needed support, bfd_cv_ld_as_needed, + [bfd_cv_ld_as_needed=no + if $LD --help 2>/dev/null | grep as-needed > /dev/null; then + bfd_cv_ld_as_needed=yes + fi + ]) + +no_as_needed= +if test x"$bfd_cv_ld_as_needed" = xyes; then + no_as_needed='-Wl,--no-as-needed' +fi + +AC_PATH_PROG([EXPECT], [expect]) +AC_CACHE_CHECK([for Tcl supporting try/catch], [ac_cv_libctf_tcl_try], + [ac_cv_libctf_tcl_try=`if test -z $EXPECT; then echo no; else $EXPECT << EOF +if @<:@llength @<:@info commands try@:>@@:>@ then { puts yes } else { puts no } +EOF +fi` +]) +AM_CONDITIONAL(TCL_TRY, test "${ac_cv_libctf_tcl_try}" = yes) + + +# Generate manpages, if possible. +if test $cross_compiling = no; then + AM_MISSING_PROG(HELP2MAN, help2man) + build_man=true +else + build_man=false +fi +AM_CONDITIONAL([BUILD_MAN], [test x$build_man = xtrue]) + +AC_SUBST(LD_NO_AS_NEEDED, [${no_as_needed}]) +AC_SUBST(GPROFNG_CFLAGS, [${gprofng_cflags}]) +AC_SUBST(GPROFNG_CPPFLAGS, [${gprofng_cppflags}]) +AC_SUBST(GPROFNG_LIBDIR, [${libdir}]) + +AC_CHECK_DECLS([basename]) +AC_CHECK_FUNCS([strsignal]) + +AC_SUBST(BUILD_SUBDIRS) + +AC_CONFIG_FILES([Makefile src/Makefile gp-display-html/Makefile doc/Makefile]) +AC_CONFIG_HEADERS([config.h:common/config.h.in]) + +AC_OUTPUT + diff --git a/gprofng/doc/Makefile.am b/gprofng/doc/Makefile.am new file mode 100644 index 0000000..3dc2cac --- /dev/null +++ b/gprofng/doc/Makefile.am @@ -0,0 +1,37 @@ +## Process this file with automake to generate Makefile.in +# +# Copyright (C) 2012-2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. +# + +AUTOMAKE_OPTIONS = foreign no-texinfo.tex + +info_TEXINFOS = gprofng.texi +gprofng_TEXINFOS = fdl.texi +BUILT_SOURCES = version.texi +CLEANFILES = version.texi +TEXINFO_TEX = . +MAKEINFOHTML = $(MAKEINFO) --html --no-split + +version.texi: + @echo "@set EDITION 1.0" > $@ + @echo "@set VERSION 1.0" >> $@ + @echo "@set UPDATED 22 February 2022" >> $@ + @echo "@set UPDATED-MONTH February 2022" >> $@ +# @echo "@set UPDATED `date +"%-d %B %Y"`" >> $@ +# @echo "@set UPDATED-MONTH `date +"%B %Y"`" >> $@ + +MAINTAINERCLEANFILES = gprofng.info diff --git a/gprofng/doc/Makefile.in b/gprofng/doc/Makefile.in new file mode 100644 index 0000000..31e298c --- /dev/null +++ b/gprofng/doc/Makefile.in @@ -0,0 +1,834 @@ +# Makefile.in generated by automake 1.15.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2017 Free Software Foundation, Inc. + +# This Makefile.in 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. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# Copyright (C) 2012-2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. +# +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = doc +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/../libtool.m4 \ + $(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \ + $(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/../config/warnings.m4 \ + $(top_srcdir)/../config/enable.m4 \ + $(top_srcdir)/../config/ax_pthread.m4 \ + $(top_srcdir)/config/bison.m4 $(top_srcdir)/../bfd/version.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/version.texi \ + $(srcdir)/stamp-vti $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +AM_V_DVIPS = $(am__v_DVIPS_@AM_V@) +am__v_DVIPS_ = $(am__v_DVIPS_@AM_DEFAULT_V@) +am__v_DVIPS_0 = @echo " DVIPS " $@; +am__v_DVIPS_1 = +AM_V_MAKEINFO = $(am__v_MAKEINFO_@AM_V@) +am__v_MAKEINFO_ = $(am__v_MAKEINFO_@AM_DEFAULT_V@) +am__v_MAKEINFO_0 = @echo " MAKEINFO" $@; +am__v_MAKEINFO_1 = +AM_V_INFOHTML = $(am__v_INFOHTML_@AM_V@) +am__v_INFOHTML_ = $(am__v_INFOHTML_@AM_DEFAULT_V@) +am__v_INFOHTML_0 = @echo " INFOHTML" $@; +am__v_INFOHTML_1 = +AM_V_TEXI2DVI = $(am__v_TEXI2DVI_@AM_V@) +am__v_TEXI2DVI_ = $(am__v_TEXI2DVI_@AM_DEFAULT_V@) +am__v_TEXI2DVI_0 = @echo " TEXI2DVI" $@; +am__v_TEXI2DVI_1 = +AM_V_TEXI2PDF = $(am__v_TEXI2PDF_@AM_V@) +am__v_TEXI2PDF_ = $(am__v_TEXI2PDF_@AM_DEFAULT_V@) +am__v_TEXI2PDF_0 = @echo " TEXI2PDF" $@; +am__v_TEXI2PDF_1 = +AM_V_texinfo = $(am__v_texinfo_@AM_V@) +am__v_texinfo_ = $(am__v_texinfo_@AM_DEFAULT_V@) +am__v_texinfo_0 = -q +am__v_texinfo_1 = +AM_V_texidevnull = $(am__v_texidevnull_@AM_V@) +am__v_texidevnull_ = $(am__v_texidevnull_@AM_DEFAULT_V@) +am__v_texidevnull_0 = > /dev/null +am__v_texidevnull_1 = +INFO_DEPS = $(srcdir)/gprofng.info +am__TEXINFO_TEX_DIR = $(srcdir)/. +DVIS = gprofng.dvi +PDFS = gprofng.pdf +PSS = gprofng.ps +HTMLS = gprofng.html +TEXINFOS = gprofng.texi +TEXI2DVI = texi2dvi +TEXI2PDF = $(TEXI2DVI) --pdf --batch +AM_MAKEINFOHTMLFLAGS = $(AM_MAKEINFOFLAGS) +DVIPS = dvips +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__installdirs = "$(DESTDIR)$(infodir)" +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(gprofng_TEXINFOS) $(srcdir)/Makefile.in \ + $(top_srcdir)/../mkinstalldirs mdate-sh texinfo.tex +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_SUBDIRS = @BUILD_SUBDIRS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPECT = @EXPECT@ +FGREP = @FGREP@ +GPROFNG_CFLAGS = @GPROFNG_CFLAGS@ +GPROFNG_CPPFLAGS = @GPROFNG_CPPFLAGS@ +GPROFNG_LIBADD = @GPROFNG_LIBADD@ +GPROFNG_LIBDIR = @GPROFNG_LIBDIR@ +GREP = @GREP@ +HELP2MAN = @HELP2MAN@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JAVA = @JAVA@ +JAVAC = @JAVAC@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LD_NO_AS_NEEDED = @LD_NO_AS_NEEDED@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WERROR = @WERROR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +gprofng_cflags = @gprofng_cflags@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +jdk_inc = @jdk_inc@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = foreign no-texinfo.tex +info_TEXINFOS = gprofng.texi +gprofng_TEXINFOS = fdl.texi +BUILT_SOURCES = version.texi +CLEANFILES = version.texi +TEXINFO_TEX = . +MAKEINFOHTML = $(MAKEINFO) --html --no-split +# @echo "@set UPDATED `date +"%-d %B %Y"`" >> $@ +# @echo "@set UPDATED-MONTH `date +"%B %Y"`" >> $@ +MAINTAINERCLEANFILES = gprofng.info +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .dvi .html .info .pdf .ps .texi +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign doc/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +.texi.info: + $(AM_V_MAKEINFO)restore=: && backupdir="$(am__leading_dot)am$$$$" && \ + am__cwd=`pwd` && $(am__cd) $(srcdir) && \ + rm -rf $$backupdir && mkdir $$backupdir && \ + if ($(MAKEINFO) --version) >/dev/null 2>&1; then \ + for f in $@ $@-[0-9] $@-[0-9][0-9] $(@:.info=).i[0-9] $(@:.info=).i[0-9][0-9]; do \ + if test -f $$f; then mv $$f $$backupdir; restore=mv; else :; fi; \ + done; \ + else :; fi && \ + cd "$$am__cwd"; \ + if $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ + -o $@ $<; \ + then \ + rc=0; \ + $(am__cd) $(srcdir); \ + else \ + rc=$$?; \ + $(am__cd) $(srcdir) && \ + $$restore $$backupdir/* `echo "./$@" | sed 's|[^/]*$$||'`; \ + fi; \ + rm -rf $$backupdir; exit $$rc + +.texi.dvi: + $(AM_V_TEXI2DVI)TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ + $(TEXI2DVI) $(AM_V_texinfo) --build-dir=$(@:.dvi=.t2d) -o $@ $(AM_V_texidevnull) \ + $< + +.texi.pdf: + $(AM_V_TEXI2PDF)TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ + $(TEXI2PDF) $(AM_V_texinfo) --build-dir=$(@:.pdf=.t2p) -o $@ $(AM_V_texidevnull) \ + $< + +.texi.html: + $(AM_V_MAKEINFO)rm -rf $(@:.html=.htp) + $(AM_V_at)if $(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ + -o $(@:.html=.htp) $<; \ + then \ + rm -rf $@ && mv $(@:.html=.htp) $@; \ + else \ + rm -rf $(@:.html=.htp); exit 1; \ + fi +$(srcdir)/gprofng.info: gprofng.texi $(srcdir)/version.texi $(gprofng_TEXINFOS) +gprofng.dvi: gprofng.texi $(srcdir)/version.texi $(gprofng_TEXINFOS) +gprofng.pdf: gprofng.texi $(srcdir)/version.texi $(gprofng_TEXINFOS) +gprofng.html: gprofng.texi $(srcdir)/version.texi $(gprofng_TEXINFOS) +$(srcdir)/version.texi: @MAINTAINER_MODE_TRUE@ $(srcdir)/stamp-vti +$(srcdir)/stamp-vti: gprofng.texi $(top_srcdir)/configure + @(dir=.; test -f ./gprofng.texi || dir=$(srcdir); \ + set `$(SHELL) $(srcdir)/mdate-sh $$dir/gprofng.texi`; \ + echo "@set UPDATED $$1 $$2 $$3"; \ + echo "@set UPDATED-MONTH $$2 $$3"; \ + echo "@set EDITION $(VERSION)"; \ + echo "@set VERSION $(VERSION)") > vti.tmp$$$$ && \ + (cmp -s vti.tmp$$$$ $(srcdir)/version.texi \ + || (echo "Updating $(srcdir)/version.texi" && \ + cp vti.tmp$$$$ $(srcdir)/version.texi.tmp$$$$ && \ + mv $(srcdir)/version.texi.tmp$$$$ $(srcdir)/version.texi)) && \ + rm -f vti.tmp$$$$ $(srcdir)/version.texi.$$$$ + @cp $(srcdir)/version.texi $@ + +mostlyclean-vti: + -rm -f vti.tmp* $(srcdir)/version.texi.tmp* + +maintainer-clean-vti: +@MAINTAINER_MODE_TRUE@ -rm -f $(srcdir)/stamp-vti $(srcdir)/version.texi +.dvi.ps: + $(AM_V_DVIPS)TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + $(DVIPS) $(AM_V_texinfo) -o $@ $< + +uninstall-dvi-am: + @$(NORMAL_UNINSTALL) + @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(dvidir)/$$f'"; \ + rm -f "$(DESTDIR)$(dvidir)/$$f"; \ + done + +uninstall-html-am: + @$(NORMAL_UNINSTALL) + @list='$(HTMLS)'; test -n "$(htmldir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -rf '$(DESTDIR)$(htmldir)/$$f'"; \ + rm -rf "$(DESTDIR)$(htmldir)/$$f"; \ + done + +uninstall-info-am: + @$(PRE_UNINSTALL) + @if test -d '$(DESTDIR)$(infodir)' && $(am__can_run_installinfo); then \ + list='$(INFO_DEPS)'; \ + for file in $$list; do \ + relfile=`echo "$$file" | sed 's|^.*/||'`; \ + echo " install-info --info-dir='$(DESTDIR)$(infodir)' --remove '$(DESTDIR)$(infodir)/$$relfile'"; \ + if install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$$relfile"; \ + then :; else test ! -f "$(DESTDIR)$(infodir)/$$relfile" || exit 1; fi; \ + done; \ + else :; fi + @$(NORMAL_UNINSTALL) + @list='$(INFO_DEPS)'; \ + for file in $$list; do \ + relfile=`echo "$$file" | sed 's|^.*/||'`; \ + relfile_i=`echo "$$relfile" | sed 's|\.info$$||;s|$$|.i|'`; \ + (if test -d "$(DESTDIR)$(infodir)" && cd "$(DESTDIR)$(infodir)"; then \ + echo " cd '$(DESTDIR)$(infodir)' && rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]"; \ + rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]; \ + else :; fi); \ + done + +uninstall-pdf-am: + @$(NORMAL_UNINSTALL) + @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(pdfdir)/$$f'"; \ + rm -f "$(DESTDIR)$(pdfdir)/$$f"; \ + done + +uninstall-ps-am: + @$(NORMAL_UNINSTALL) + @list='$(PSS)'; test -n "$(psdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(psdir)/$$f'"; \ + rm -f "$(DESTDIR)$(psdir)/$$f"; \ + done + +dist-info: $(INFO_DEPS) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + list='$(INFO_DEPS)'; \ + for base in $$list; do \ + case $$base in \ + $(srcdir)/*) base=`echo "$$base" | sed "s|^$$srcdirstrip/||"`;; \ + esac; \ + if test -f $$base; then d=.; else d=$(srcdir); fi; \ + base_i=`echo "$$base" | sed 's|\.info$$||;s|$$|.i|'`; \ + for file in $$d/$$base $$d/$$base-[0-9] $$d/$$base-[0-9][0-9] $$d/$$base_i[0-9] $$d/$$base_i[0-9][0-9]; do \ + if test -f $$file; then \ + relfile=`expr "$$file" : "$$d/\(.*\)"`; \ + test -f "$(distdir)/$$relfile" || \ + cp -p $$file "$(distdir)/$$relfile"; \ + else :; fi; \ + done; \ + done + +mostlyclean-aminfo: + -rm -rf gprofng.t2d gprofng.t2p + +clean-aminfo: + -test -z "gprofng.dvi gprofng.pdf gprofng.ps gprofng.html" \ + || rm -rf gprofng.dvi gprofng.pdf gprofng.ps gprofng.html + +maintainer-clean-aminfo: + @list='$(INFO_DEPS)'; for i in $$list; do \ + i_i=`echo "$$i" | sed 's|\.info$$||;s|$$|.i|'`; \ + echo " rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]"; \ + rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]; \ + done +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-info +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(INFO_DEPS) +installdirs: + for dir in "$(DESTDIR)$(infodir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-aminfo clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: $(DVIS) + +html: html-am + +html-am: $(HTMLS) + +info: info-am + +info-am: $(INFO_DEPS) + +install-data-am: install-info-am + +install-dvi: install-dvi-am + +install-dvi-am: $(DVIS) + @$(NORMAL_INSTALL) + @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(dvidir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dvidir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dvidir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(dvidir)" || exit $$?; \ + done +install-exec-am: + +install-html: install-html-am + +install-html-am: $(HTMLS) + @$(NORMAL_INSTALL) + @list='$(HTMLS)'; list2=; test -n "$(htmldir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(htmldir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p" || test -d "$$p"; then d=; else d="$(srcdir)/"; fi; \ + $(am__strip_dir) \ + d2=$$d$$p; \ + if test -d "$$d2"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)/$$f'"; \ + $(MKDIR_P) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \ + echo " $(INSTALL_DATA) '$$d2'/* '$(DESTDIR)$(htmldir)/$$f'"; \ + $(INSTALL_DATA) "$$d2"/* "$(DESTDIR)$(htmldir)/$$f" || exit $$?; \ + else \ + list2="$$list2 $$d2"; \ + fi; \ + done; \ + test -z "$$list2" || { echo "$$list2" | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \ + done; } +install-info: install-info-am + +install-info-am: $(INFO_DEPS) + @$(NORMAL_INSTALL) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(infodir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(infodir)" || exit 1; \ + fi; \ + for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + esac; \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + file_i=`echo "$$file" | sed 's|\.info$$||;s|$$|.i|'`; \ + for ifile in $$d/$$file $$d/$$file-[0-9] $$d/$$file-[0-9][0-9] \ + $$d/$$file_i[0-9] $$d/$$file_i[0-9][0-9] ; do \ + if test -f $$ifile; then \ + echo "$$ifile"; \ + else : ; fi; \ + done; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(infodir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(infodir)" || exit $$?; done + @$(POST_INSTALL) + @if $(am__can_run_installinfo); then \ + list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \ + for file in $$list; do \ + relfile=`echo "$$file" | sed 's|^.*/||'`; \ + echo " install-info --info-dir='$(DESTDIR)$(infodir)' '$(DESTDIR)$(infodir)/$$relfile'";\ + install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$$relfile" || :;\ + done; \ + else : ; fi +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: $(PDFS) + @$(NORMAL_INSTALL) + @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pdfdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pdfdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pdfdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pdfdir)" || exit $$?; done +install-ps: install-ps-am + +install-ps-am: $(PSS) + @$(NORMAL_INSTALL) + @list='$(PSS)'; test -n "$(psdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(psdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(psdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(psdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(psdir)" || exit $$?; done +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-aminfo \ + maintainer-clean-generic maintainer-clean-vti + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-aminfo mostlyclean-generic \ + mostlyclean-libtool mostlyclean-vti + +pdf: pdf-am + +pdf-am: $(PDFS) + +ps: ps-am + +ps-am: $(PSS) + +uninstall-am: uninstall-dvi-am uninstall-html-am uninstall-info-am \ + uninstall-pdf-am uninstall-ps-am + +.MAKE: all check install install-am install-strip + +.PHONY: all all-am check check-am clean clean-aminfo clean-generic \ + clean-libtool cscopelist-am ctags-am dist-info distclean \ + distclean-generic distclean-libtool distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-aminfo maintainer-clean-generic \ + maintainer-clean-vti mostlyclean mostlyclean-aminfo \ + mostlyclean-generic mostlyclean-libtool mostlyclean-vti pdf \ + pdf-am ps ps-am tags-am uninstall uninstall-am \ + uninstall-dvi-am uninstall-html-am uninstall-info-am \ + uninstall-pdf-am uninstall-ps-am + +.PRECIOUS: Makefile + + +version.texi: + @echo "@set EDITION 1.0" > $@ + @echo "@set VERSION 1.0" >> $@ + @echo "@set UPDATED 22 February 2022" >> $@ + @echo "@set UPDATED-MONTH February 2022" >> $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gprofng/doc/fdl.texi b/gprofng/doc/fdl.texi new file mode 100644 index 0000000..8805f1a --- /dev/null +++ b/gprofng/doc/fdl.texi @@ -0,0 +1,506 @@ +@c The GNU Free Documentation License. +@center Version 1.3, 3 November 2008 + +@c This file is intended to be included within another document, +@c hence no sectioning command or @node. + +@display +Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. +@uref{http://fsf.org/} + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The ``publisher'' means any person or entity that distributes copies +of the Document to the public. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +@item +RELICENSING + +``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the +site means any set of copyrightable works thus published on the MMC +site. + +``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +``Incorporate'' means to publish or republish a Document, in whole or +in part, as part of another Document. + +An MMC is ``eligible for relicensing'' if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole +or in part into the MMC, (1) had no cover texts or invariant sections, +and (2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + +@end enumerate + +@page +@heading ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with@dots{}Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + diff --git a/gprofng/doc/gprofng.texi b/gprofng/doc/gprofng.texi new file mode 100644 index 0000000..3e75a6c --- /dev/null +++ b/gprofng/doc/gprofng.texi @@ -0,0 +1,3399 @@ +\input texinfo @c -*-texinfo-*- + +@c ---------------------------------------------------------------------------- +@c This is the Texinfo source file for the GPROFNG manual. +@c +@c Author: Ruud van der Pas +@c ---------------------------------------------------------------------------- + +@c %**start of header + +@setfilename gprofng.info +@settitle GNU gprofng + +@c -- Set the indent for the @example command to 1 space, not 5 --------------- +@exampleindent 1 + +@c %**end of header + +@c -- Start a new chapter on a new, odd numbered, page ------------------------ +@setchapternewpage odd + +@c -- Merge all index entries into the Concepts Index ------------------------- +@syncodeindex fn cp +@syncodeindex ky cp +@syncodeindex pg cp +@syncodeindex vr cp + +@c -- Macro definitions ------------------------------------------------------- +@c +@c Since only letters can be used, we use capitalization to distinguish +@c different words. +@c ---------------------------------------------------------------------------- +@macro CollectApp{} +@command{gprofng collect app} +@end macro + +@macro DisplayHTML{} +@command{gprofng display html} +@end macro + +@macro DisplayText{} +@command{gprofng display text} +@end macro + +@macro Driver{} +@command{gprofng} +@end macro + +@macro ProductName{} +gprofng +@end macro + +@macro ToolName{} +@command{gprofng} +@end macro + +@macro IndexSubentry{label, string} +@c -- @cindex \label\ @subentry \string\ +@cindex \label\, \string\ +@end macro + +@c -- Get the version information --------------------------------------------- +@include version.texi + +@c -- Entry for the Info dir structure ---------------------------------------- +@ifnottex +@dircategory Software development +@direntry +* gprofng: (gprofng). The next generation profiling tool for Linux +@end direntry +@end ifnottex + +@c -- Copyright stuff --------------------------------------------------------- +@copying +This document is the manual for @ProductName{}, last updated @value{UPDATED}. + +Copyright @copyright{} 2022 Free Software Foundation, Inc. + +@c -- @quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, +Version 1.3 or any later version published by the Free Software +Foundation; with no Invariant Sections, with no Front-Cover texts, +and with no Back-Cover Texts. A copy of the license is included in the +section entitled ``GNU Free Documentation License.'' + +@c -- @end quotation +@end copying + +@finalout +@smallbook + +@c -- Define the title page --------------------------------------------------- +@titlepage +@title GNU gprofng +@subtitle The next generation profiling tool for Linux +@subtitle version @value{VERSION} (last updated @value{UPDATED}) +@author Ruud van der Pas +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@c -- Generate the Table of Contents ------------------------------------------ +@contents + +@c -- The Top node ------------------------------------------------------------ +@c Should contain a short summary, copying permissions and a master menu. +@c ---------------------------------------------------------------------------- +@ifnottex +@node Top +@top GNU Gprofng + +@insertcopying +@end ifnottex + +@ifinfo +@c -- The menu entries -------------------------------------------------------- + +@menu +* Introduction:: About this manual. +* Overview:: A brief overview of @ProductName{}. +* A Mini Tutorial:: A short tutorial covering the key features. +* Terminology:: Various concepts and some terminology explained. +* Other Document Formats:: How to create this document in other formats. +* Index:: The index. + +@detailmenu + +--- The Detailed Node Listing --- + +Introduction + +Overview + +* Main Features:: A high level overview. +* Sampling versus Tracing:: The pros and cons of sampling versus tracing. +* Steps Needed to Create a Profile:: How to create a profile. + +A Mini Tutorial + +* Getting Started:: The basics of profiling with @ProductName(). +* Support for Multithreading:: Commands specific to multithreaded applications. +* Viewing Multiple Experiments:: Analyze multiple experiments. +* Profile Hardware Event Counters:: How to use hardware event counters. +* Java Profiling:: How to profile a Java application. + +Terminology + +* The Program Counter:: What is a Program Counter? +* Inclusive and Exclusive Metrics:: An explanation of inclusive and exclusive metrics. +* Metric Definitions:: Definitions associated with metrics. +* The Viewmode:: Select the way call stacks are presented. +* The Selection List:: How to define a selection. +* Load Objects and Functions:: The components in an application. +* The Concept of a CPU in @ProductName{}:: The definition of a CPU. +* Hardware Event Counters Explained:: What are event counters? +* apath:: Our generic definition of a path. + +@c -- Index + +@end detailmenu +@end menu +@end ifinfo + +@c -- A new node -------------------------------------------------------------- +@node Introduction +@chapter Introduction +@c ---------------------------------------------------------------------------- +The @ProductName{} tool is the next generation profiler for Linux. It consists +of various commands to generate and display profile information. + +This manual starts with a tutorial how to create and interpret a profile. This +part is highly practical and has the goal to get users up to speed as quickly +as possible. As soon as possible, we would like to show you how to get your +first profile on your screen. + +This is followed by more examples, covering many of the features. At the +end of this tutorial, you should feel confident enough to tackle the more +complex tasks. + +In a future update a more formal reference manual will be included as well. +Since even in this tutorial we use certain terminology, we have included a +chapter with descriptions at the end. In case you encounter unfamiliar +wordings or terminology, please check this chapter. + +One word of caution. In several cases we had to somewhat tweak the screen +output in order to make it fit. This is why the output may look somewhat +different when you try things yourself. + +For now, we wish you a smooth profiling experience with @ProductName{} and +good luck tackling performance bottlenecks. + +@c -- A new node -------------------------------------------------------------- +@c cccccc @node A Brief Overview of @ProductName{} +@node Overview +@chapter A Brief Overview of @ProductName{} +@c ---------------------------------------------------------------------------- + +@menu +* Main Features:: A high level overview. +* Sampling versus Tracing:: The pros and cons of sampling versus tracing. +* Steps Needed to Create a Profile:: How to create a profile. +@end menu + +Before we cover this tool in quite some detail, we start with a brief overview +of what it is, and the main features. Since we know that many of you would +like to get started rightaway, already in this first chapter we explain the +basics of profiling with @ToolName{}. + +@c ---------------------------------------------------------------------------- +@c TBD Review this text. Probably be more specific on the gcc releases and +@c processor specifics. +@c ---------------------------------------------------------------------------- + +@c -- A new node -------------------------------------------------------------- +@node Main Features +@section Main Features +@c ---------------------------------------------------------------------------- + +@noindent +These are the main features of the @ProductName{} tool: + +@itemize @bullet + +@item +Profiling is supported for an application written in C, C++, Java, or Scala. + +@c TBD Java: up to 1.8 full support, support other than for modules + +@item +Shared libraries are supported. The information is presented at the instruction +level. + +@item +The following multithreading programming models are supported: Pthreads, +OpenMP, and Java threads. + +@item +This tool works with unmodified production level executables. There is no need to +recompile the code, but if the @code{-g} option has been used when building +the application, source line level information is available. + +@item +The focus is on support for code generated with the @code{gcc} compiler, but +there is some limited support for the @code{icc} compiler as well. Future +improvements and enhancements will focus on @code{gcc} though. + +@item +Processors from Intel, AMD, and Arm are supported, but the level of support +depends on the architectural details. In particular, hardware event counters +may not be supported. + +@item +Several views into the data are supported. For example, a function overview +where the time is spent, but also a source line, disassembly, call tree and +a caller-callees overview are available. + +@item +Through filters, the user can zoom in on an area of interest. + +@item +Two or more profiles can be aggregated, or used in a comparison. This comparison +can be obtained at the function, source line, and disassembly level. + +@item +Through a scripting language, and customization of the metrics shown, +the generation and creation of a profile can be fully automated and provide +tailored output. + +@end itemize + +@c -- A new node -------------------------------------------------------------- +@node Sampling versus Tracing +@section Sampling versus Tracing +@c ---------------------------------------------------------------------------- + +A key difference with some other profiling tools is that the main data +collection command @CollectApp{} mostly uses +@cindex Program Counter sampling +@cindex PC sampling +Program Counter (PC) sampling +under the hood. + +With @emph{sampling}, the executable is stopped at regular intervals. Each time +it is halted, key information is gathered and stored. This includes the Program +Counter that keeps track of where the execution is. Hence the name. + +Together with operational +data, this information is stored in the experiment directory and can be +viewed in the second phase. + +For example, the PC information is used to derive where the program was when +it was halted. Since the sampling interval is known, it is relatively easy to +derive how much time was spent in the various parts of the program. + +The opposite technique is generally referred to as @emph{tracing}. With +tracing, the target is instrumented with specific calls that collect the +requested information. + +These are some of the pros and cons of PC sampling verus tracing: + +@itemize + +@item +Since there is no need to recompile, existing executables can be used +and the profile measures the behaviour of exactly the same executable that is +used in production runs. + +With sampling, one inherently profiles a different executable because +the calls to the instrumentation library may affect the compiler optimizations +and run time behaviour. + +@item +With sampling, there are very few restrictions on what can be profiled and even without +access to the source code, a basic profile can be made. + +@item +A downside of sampling is that, depending on the sampling frequency, small +functions may be missed or not captured accurately. Although this is rare, +this may happen and is the reason why the user has control over the sampling rate. + +@item +While tracing produces precise information, sampling is statistical in nature. +As a result, small variations may occur across seemingly identical runs. We +have not observed more than a few percent deviation though. Especially if +the target job executed for a sufficiently long time. + +@item +With sampling, it is not possible to get an accurate count how often +functions are called. + +@end itemize + +@c -- A new node -------------------------------------------------------------- +@node Steps Needed to Create a Profile +@section Steps Needed to Create a Profile +@c ---------------------------------------------------------------------------- + +Creating a profile takes two steps. First the profile data needs to be +generated. This is followed by a viewing step to create a report from the +information that has been gathered. + +Every @ProductName{} command starts with @ToolName{}, the name of the driver. This is followed +by a keyword to define the high level functionality. Depending on this +keyword, a third qualifier may be needed to further narrow down the request. +This combination is then followed by options that are specific to the functionality +desired. + +The command to gather, or ``collect'', the performance data is called +@CollectApp{}. Aside from numerous options, this command takes the name +of the target executable as an input parameter. + +Upon completion of the run, the performance data can be +found in the newly created +@cindex Experiment directory +experiment directory. + +Unless explicitly specified otherwise, a default +name for this directory is chosen. The name is @code{test.<n>.er} where +@code{n} is the first integer number not in use yet for such a name. + +For example, the first time @CollectApp{} is invoked, an experiment +directory with the name @code{test.1.er} is created. + +Upon a subsequent invocation of @CollectApp{} in the same directory, +an experiment directory with the name @code{test.2.er} will be created, +and so forth. + +Note that @CollectApp{} supports an option to explicitly name the experiment directory. +Outside of the restriction that the name of this directory has to end +with @code{.er}, any valid directory name can be used for this. + +Now that we have the performance data, the next step is to display it. + +@pindex @DisplayText{} +The most commonly used command to view the performance information is +@DisplayText{}. This is a very extensive and customizable tool that +produces the information in ASCII format. + +@pindex @DisplayHTML{} +Another option is to use @DisplayHTML{}. This tool generates a directory with +files in html format. These can be viewed in a browser, allowing for easy +navigation through the profile data. + +@c -- A new node -------------------------------------------------------------- +@node A Mini Tutorial +@chapter A Mini Tutorial +@c ---------------------------------------------------------------------------- + +In this chapter we present and discuss the main functionality of @ToolName{}. +This will be a practical approach, using an example code to generate profile +data and show how to get various performance reports. + +@menu +* Getting Started:: The basics of profiling with @ProductName(). +* Support for Multithreading:: Commands specific to multithreaded applications. +* Viewing Multiple Experiments:: Analyze multiple experiments. +* Profile Hardware Event Counters:: How to use hardware event counters. +* Java Profiling:: How to profile a Java application. +@end menu + +@c -- A new node -------------------------------------------------------------- +@node Getting Started +@section Getting Started +@c ---------------------------------------------------------------------------- + +The information presented here provides a good and common basis for many +profiling tasks, but there are more features that you may want to leverage. + +These are covered in subsequent sections in this chapter. + +@menu +* The Example Program:: A description of the example program used. +* A First Profile:: How to get the first profile. +* The Source Code View:: Display the metrics in the source code. +* The Disassembly View:: Display the metrics at the instruction level. +* Display and Define the Metrics:: An example how to customize the metrics. +* A First Customization of the Output:: An example how to customize the output. +* Name the Experiment Directory:: Change the name of the experiment directory. +* Control the Number of Lines in the Output:: Change the number of lines in the tables. +* Sorting the Performance Data:: How to set the metric to sort by. +* Scripting:: Use a script to execute the commands. +* A More Elaborate Example:: An example of customization. +* The Call Tree:: Display the dynamic call tree. +* More Information on the Experiment:: How to get additional statistics. +* Control the Sampling Frequency:: How to control the sampling granularity. +* Information on Load Objects:: How to get more information on load objects. +@end menu + +@c -- A new node -------------------------------------------------------------- +@node The Example Program +@subsection The Example Program +@c ---------------------------------------------------------------------------- + +Throughout this guide we use the same example C code that implements the +multiplication of a vector of length @math{n} by an @math{m} by @math{n} +matrix. The result is stored in a vector of length @math{m}. +@cindex Pthreads +@cindex Posix Threads +The algorithm has been parallelized using Posix Threads, or Pthreads for short. + +The code was built using the @code{gcc} compiler and the name of the executable +is +@cindex mxv-pthreads.exe +mxv-pthreads.exe. + +The matrix sizes can be set through the @code{-m} and @code{-n} options. The +number of threads is set with the @code{-t} option. To increase the duration +of the run, the multiplication is executed repeatedly. + +This is an example that multiplies a @math{3000} by @math{2000} matrix with +a vector of length @math{2000} using @math{2} threads: + +@smallexample +@verbatim +$ ./mxv-pthreads.exe -m 3000 -n 2000 -t 2 +mxv: error check passed - rows = 3000 columns = 2000 threads = 2 +$ +@end verbatim +@end smallexample + +The program performs an internal check to verify the results are correct. +The result of this check is printed, followed by the matrix sizes and the +number of threads used. + +@c -- A new node -------------------------------------------------------------- +@node A First Profile +@subsection A First Profile +@c ---------------------------------------------------------------------------- + +The first step is to collect the performance data. It is important to remember +that much more information is gathered than may be shown by default. Often a +single data collection run is sufficient to get a lot of insight. + +The @CollectApp{} command is used for the data collection. Nothing needs to be +changed in the way the application is executed. The only difference is that it +is now run under control of the tool, as shown below: + +@cartouche +@smallexample +$ gprofng collect app ./mxv.pthreads.exe -m 3000 -n 2000 -t 1 +@end smallexample +@end cartouche + +This command produces the following output: + +@smallexample +@verbatim +Creating experiment database test.1.er (Process ID: 2416504) ... +mxv: error check passed - rows = 3000 columns = 2000 threads = 1 +@end verbatim +@end smallexample + +We see the message that a directory with the name @code{test.1.er} +has been created. +The application then completes as usual and we have our first experiment +directory that can be analyzed. + +The tool we use for this is called @DisplayText{}. It takes the name of +the experiment directory as an argument. + +@cindex Interpreter mode +If invoked this way, the tool starts in the interactive @emph{interpreter} mode. +While in this environment, commands can be given and the tool responds. This is +illustrated below: + +@smallexample +@verbatim +$ gprofng display text test.1.er +Warning: History and command editing is not supported on this system. +(gp-display-text) quit +$ +@end verbatim +@end smallexample + +@cindex Command line mode +While useful in certain cases, we prefer to use this tool in command line mode, +by specifying the commands to be issued when invoking the tool. The way to do +this is to prepend the command with a hyphen (@code{-}) if used on the command +line. + +For example, +@IndexSubentry{Commands, @code{functions}} +with the @code{functions} command we request a list of the functions that +have been executed and their respective CPU times: + +@cartouche +@smallexample +$ gprofng display text -functions test.1.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +$ gprofng display text -functions test.1.er +Functions sorted by metric: Exclusive Total CPU Time + +Excl. Incl. Name +Total Total +CPU sec. CPU sec. +2.272 2.272 <Total> +2.160 2.160 mxv_core +0.047 0.103 init_data +0.030 0.043 erand48_r +0.013 0.013 __drand48_iterate +0.013 0.056 drand48 +0.008 0.010 _int_malloc +0.001 0.001 brk +0.001 0.002 sysmalloc +0. 0.001 __default_morecore +0. 0.113 __libc_start_main +0. 0.010 allocate_data +0. 2.160 collector_root +0. 2.160 driver_mxv +0. 0.113 main +0. 0.010 malloc +0. 0.001 sbrk +@end verbatim +@end smallexample + +As easy and simple as these steps are, we do have a first profile of our program! +There are three columns. The first two contain the +@cindex Total CPU time +@emph{Total CPU Time}, +which +is the sum of the user and system time. @xref{Inclusive and Exclusive Metrics} +for an explanation of ``exclusive'' and ``inclusive'' times. + +The first line echoes the metric that is used to sort the output. By default, this +is the exclusive CPU time, but the sort metric can be changed by the user. + +We then see three columns with the exclusive and inclusive CPU times, plus the +name of the function. + +@IndexSubentry{Miscellaneous, @code{<Total>}} +The function with the name @code{<Total>} is not a user function, but is introduced +by @ToolName{} and is used to display the accumulated metric values. In this case, +we see that the total CPU time of this job was @code{2.272} seconds. + +With @code{2.160} seconds, function @code{mxv_core} is the most time +consuming function. It is also a leaf function. + +The next function in the list is @code{init_data}. Although the CPU time spent in +this part is negligible, this is an interesting entry because the inclusive CPU +time of @code{0.103} seconds is higher than the exclusive CPU time of @code{0.047} +seconds. Clearly it is calling another function, +or even more than one function. +@xref{The Call Tree} for the details how to get more information on this. + +The function @code{collector_root} does not look familiar. It is one of the internal +functions used by @CollectApp{} and can be ignored. While the inclusive time is high, +the exclusive time is zero. This means it doesn't contribute to the performance. + +The question is how we know where this function originates from? There is a very useful +command to get more details on a function. @xref{Information on Load Objects}. + +@c -- A new node -------------------------------------------------------------- +@node The Source Code View +@subsection The Source Code View +@c ---------------------------------------------------------------------------- + +In general, you would like to focus the tuning efforts on the most time +consuming part(s) of the program. In this case that is easy, since 2.160 +seconds on a total of 2.272 seconds is spent in function @code{mxv_core}. +That is 95% of the total and it is time to dig deeper and look +@cindex Source level timings +at the time distribution at the source code level. + +@IndexSubentry{Commands, @code{source}} +The @code{source} command is used to accomplish this. It takes the name of the +function, not the source filename, as an argument. This is demonstrated +below, where the @DisplayText{} command is used to show the annotated +source listing of function @code{mxv_core}. + +Please note that the source code has to be compiled with the @code{-g} +option in order for the source code feature to work. Otherwise the +location can not be determined. + +@cartouche +@smallexample +$ gprofng display text -source mxv_core test.1.er +@end smallexample +@end cartouche + +The slightly modified output is as follows: + +@smallexample +@verbatim +Source file: <apath>/mxv.c +Object file: mxv-pthreads.exe (found as test.1.er/archives/...) +Load Object: mxv-pthreads.exe (found as test.1.er/archives/...) + + Excl. Incl. + Total Total + CPU sec. CPU sec. + + <lines deleted> + <Function: mxv_core> + 0. 0. 32. void __attribute__ ((noinline)) + mxv_core ( + uint64_t row_index_start, + uint64_t row_index_end, + uint64_t m, uint64_t n, + double **restrict A, + double *restrict b, + double *restrict c) + 0. 0. 33. { + 0. 0. 34. for (uint64_t i=row_index_start; + i<=row_index_end; i++) { + 0. 0. 35. double row_sum = 0.0; +## 1.687 1.687 36. for (int64_t j=0; j<n; j++) + 0.473 0.473 37. row_sum += A[i][j]*b[j]; + 0. 0. 38. c[i] = row_sum; + 39. } + 0. 0. 40. } +@end verbatim +@end smallexample + +The first three lines provide information on the location of the source file, +the object file and the load object (@xref{Load Objects and Functions}). + +Function @code{mxv_core} is part of a source file that has other functions +as well. These functions will be shown, but without timing information. They +have been removed in the output shown above. + +This is followed by the annotated source code listing. The selected metrics +are shown first, followed by a source line number, and the source code. +@IndexSubentry{Miscellaneous ,@code{##}} +The most time consuming line(s) are marked with the @code{##} symbol. In +this way they are easier to find. + +What we see is that all of the time is spent in lines 36-37. + +@IndexSubentry{Commands, @code{lines}} +A related command sometimes comes handy as well. It is called @code{lines} +and displays a list of the source lines and their metrics, ordered according +to the current sort metric (@xref{Sorting the Performance Data}). + +Below the command and the output. For lay-out reasons, only the top 10 is +shown here and the last part of the text on some lines has been replaced +by dots. + +@cartouche +@smallexample +$ gprofng display text -lines test.1.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +Lines sorted by metric: Exclusive Total CPU Time + +Excl. Incl. Name +Total Total +CPU sec. CPU sec. +2.272 2.272 <Total> +1.687 1.687 mxv_core, line 36 in "mxv.c" +0.473 0.473 mxv_core, line 37 in "mxv.c" +0.032 0.088 init_data, line 72 in "manage_data.c" +0.030 0.043 <Function: erand48_r, instructions without line numbers> +0.013 0.013 <Function: __drand48_iterate, instructions without ...> +0.013 0.056 <Function: drand48, instructions without line numbers> +0.012 0.012 init_data, line 77 in "manage_data.c" +0.008 0.010 <Function: _int_malloc, instructions without ...> +0.003 0.003 init_data, line 71 in "manage_data.c" +@end verbatim +@end smallexample + +What this overview immediately highlights is that the next most time consuming +source line takes 0.032 seconds only. With an inclusive time of 0.088 seconds, +it is also clear that this branch of the code does not impact the performance. + +@c -- A new node -------------------------------------------------------------- +@node The Disassembly View +@subsection The Disassembly View +@c ---------------------------------------------------------------------------- + +The source view is very useful to obtain more insight where the time is spent, +but sometimes this is not sufficient. This is when the disassembly view comes +in. It is activated with the +@IndexSubentry{Commands, @code{disasm}} +@code{disasm} +command and as with the source view, it displays an annotated listing. In this +@cindex Instruction level timings +case it shows the instructions with the metrics, interleaved with the +source lines. The +instructions have a reference in square brackets (@code{[} and @code{]}) +to the source line they correspond to. + +@noindent +This is what we get for our example: + +@cartouche +@smallexample +$ gprofng display text -disasm mxv_core test.1.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +Source file: <apath>/mxv.c +Object file: mxv-pthreads.exe (found as test.1.er/archives/...) +Load Object: mxv-pthreads.exe (found as test.1.er/archives/...) + + Excl. Incl. + Total Total + CPU sec. CPU sec. + + <lines deleted> + 32. void __attribute__ ((noinline)) + mxv_core ( + uint64_t row_index_start, + uint64_t row_index_end, + uint64_t m, uint64_t n, + double **restrict A, + double *restrict b, + double *restrict c) + 33. { + <Function: mxv_core> + 0. 0. [33] 4021ba: mov 0x8(%rsp),%r10 + 34. for (uint64_t i=row_index_start; + i<=row_index_end; i++) { + 0. 0. [34] 4021bf: cmp %rsi,%rdi + 0. 0. [34] 4021c2: jbe 0x37 + 0. 0. [34] 4021c4: ret + 35. double row_sum = 0.0; + 36. for (int64_t j=0; j<n; j++) + 37. row_sum += A[i][j]*b[j]; + 0. 0. [37] 4021c5: mov (%r8,%rdi,8),%rdx + 0. 0. [36] 4021c9: mov $0x0,%eax + 0. 0. [35] 4021ce: pxor %xmm1,%xmm1 + 0.002 0.002 [37] 4021d2: movsd (%rdx,%rax,8),%xmm0 + 0.096 0.096 [37] 4021d7: mulsd (%r9,%rax,8),%xmm0 + 0.375 0.375 [37] 4021dd: addsd %xmm0,%xmm1 +## 1.683 1.683 [36] 4021e1: add $0x1,%rax + 0.004 0.004 [36] 4021e5: cmp %rax,%rcx + 0. 0. [36] 4021e8: jne 0xffffffffffffffea + 38. c[i] = row_sum; + 0. 0. [38] 4021ea: movsd %xmm1,(%r10,%rdi,8) + 0. 0. [34] 4021f0: add $0x1,%rdi + 0. 0. [34] 4021f4: cmp %rdi,%rsi + 0. 0. [34] 4021f7: jb 0xd + 0. 0. [35] 4021f9: pxor %xmm1,%xmm1 + 0. 0. [36] 4021fd: test %rcx,%rcx + 0. 0. [36] 402200: jne 0xffffffffffffffc5 + 0. 0. [36] 402202: jmp 0xffffffffffffffe8 + 39. } + 40. } + 0. 0. [40] 402204: ret +@end verbatim +@end smallexample + +For each instruction, the timing values are given and we can exactly which ones +are the most expensive. As with the source level view, the most expensive +instructions are market with the @code{##} symbol. + +As illustrated below and similar to the @code{lines} command, we can get +an overview of the instructions executed by using the +@IndexSubentry{Commands, @code{pcs}} +@code{pcs} +command. + +@noindent +Below the command and the output, which again has been restricted +to 10 lines: + +@cartouche +@smallexample +$ gprofng display text -pcs test.1.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +PCs sorted by metric: Exclusive Total CPU Time + +Excl. Incl. Name +Total Total +CPU sec. CPU sec. +2.272 2.272 <Total> +1.683 1.683 mxv_core + 0x00000027, line 36 in "mxv.c" +0.375 0.375 mxv_core + 0x00000023, line 37 in "mxv.c" +0.096 0.096 mxv_core + 0x0000001D, line 37 in "mxv.c" +0.027 0.027 init_data + 0x000000BD, line 72 in "manage_data.c" +0.012 0.012 init_data + 0x00000117, line 77 in "manage_data.c" +0.008 0.008 _int_malloc + 0x00000A45 +0.007 0.007 erand48_r + 0x00000062 +0.006 0.006 drand48 + 0x00000000 +0.005 0.005 __drand48_iterate + 0x00000005 +@end verbatim +@end smallexample + +@c -- A new node -------------------------------------------------------------- +@node Display and Define the Metrics +@subsection Display and Define the Metrics +@c ---------------------------------------------------------------------------- + +The default metrics shown by @DisplayText{} are useful, but there is more +recorded than displayed. We can customize the values shown by defining the +metrics ourselves. + +@IndexSubentry{Commands, @code{metric_list}} +There are two commands related to changing the metrics shown: @code{metric_list} +and +@IndexSubentry{Commands, @code{metrics}} +@code{metrics}. + +The first command shows the metrics in use, plus all the metrics that have +been stored as part of the experiment. The second command may be used to +define the metric list. + +In our example we get the following values for the metrics: + +@IndexSubentry{Commands, @code{metric_list}} +@cartouche +@smallexample +$ gprofng display text -metric_list test.1.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +Current metrics: e.totalcpu:i.totalcpu:name +Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu ) +Available metrics: + Exclusive Total CPU Time: e.%totalcpu + Inclusive Total CPU Time: i.%totalcpu + Size: size + PC Address: address + Name: name +@end verbatim +@end smallexample + +This shows the metrics currently in use, the metric that is used to sort +the data and all the metrics that have been recorded, but are not necessarily +shown. + +@cindex Default metrics +In this case, the default metrics are set to the exclusive and inclusive +total CPU times, plus the name of the function, or load object. + +@IndexSubentry{Commands, @code{metrics}} +The @code{metrics} command is used to define the metrics that need to be +displayed. + +For example, to display the exclusive total CPU time, both as a number and a +percentage, use the following metric definition: @code{e.%totalcpu} + +Since the metrics can be tailored for different views, there is a way +to reset them to the default. This is done through the special keyword +@code{default}. + +@c -- A new node -------------------------------------------------------------- +@node A First Customization of the Output +@subsection A First Customization of the Output +@c ---------------------------------------------------------------------------- + +With the information just given, we can customize the function overview. +For sake of the example, we would like to display the name of the function +first, followed by the exclusive CPU time, given as an absolute number and +a percentage. + +Note that the commands are parsed in order of appearance. This is why we +need to define the metrics @emph{before} requesting the function overview: + +@cartouche +@smallexample +$ gprofng display text -metrics name:e.%totalcpu -functions test.1.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +Current metrics: name:e.%totalcpu +Current Sort Metric: Exclusive Total CPU Time ( e.%totalcpu ) +Functions sorted by metric: Exclusive Total CPU Time + +Name Excl. Total + CPU + sec. % + <Total> 2.272 100.00 + mxv_core 2.160 95.04 + init_data 0.047 2.06 + erand48_r 0.030 1.32 + __drand48_iterate 0.013 0.57 + drand48 0.013 0.57 + _int_malloc 0.008 0.35 + brk 0.001 0.04 + sysmalloc 0.001 0.04 + __default_morecore 0. 0. + __libc_start_main 0. 0. + allocate_data 0. 0. + collector_root 0. 0. + driver_mxv 0. 0. + main 0. 0. + malloc 0. 0. + sbrk 0. 0. +@end verbatim +@end smallexample + +This was a first and simple example how to customize the output. Note that we +did not rerun our profiling job and merely modified the display settings. +Below we will show other and also more advanced examples of customization. + + +@c -- A new node -------------------------------------------------------------- +@node Name the Experiment Directory +@subsection Name the Experiment Directory +@c ---------------------------------------------------------------------------- + +When using @CollectApp{}, the default names for experiments work fine, but +they are quite generic. It is often more convenient to select a more +descriptive name. For example, one that reflects conditions for the experiment +conducted. + +For this, the mutually exclusive @code{-o} and @code{-O} options come in handy. +Both may be used to provide a name for the experiment directory, but the +behaviour of @CollectApp{} is different. + +With the +@IndexSubentry{Options, @code{-o}} +@code{-o} +option, an existing experiment directory is not overwritten. You either +need to explicitly remove an existing directory first, or use a name that is not +in use yet. + +This is in contrast with the behaviour for the + @IndexSubentry{Options, @code{-O}} +@code{-O} +option. Any existing (experiment) directory with the same name is silently +overwritten. + +Be aware that the name of the experiment directory has to end with @code{.er}. + +@c -- A new node -------------------------------------------------------------- +@node Control the Number of Lines in the Output +@subsection Control the Number of Lines in the Output +@c ---------------------------------------------------------------------------- + +@IndexSubentry{Commands, @code{limit}} +The @code{limit <n>} command can be used to control the number of lines printed +in various overviews, including the function view, but it also takes effect +for other display commands, like @code{lines}. + +The argument @code{<n>} should be a positive integer number. It sets the number +of lines in the function view. A value of zero resets the limit to the default. + +Be aware that the pseudo-function @code{<Total>} counts as a regular function. +For example @code{limit 10} displays nine user level functions. + +@c -- A new node -------------------------------------------------------------- +@node Sorting the Performance Data +@subsection Sorting the Performance Data +@c ---------------------------------------------------------------------------- + +@IndexSubentry{Commands, @code{sort}} +The @code{sort <key>} command sets the key to be used when sorting the +performance data. + +The key is a valid metric definition, but the +@cindex Visibility field +visibility field +(@xref{Metric Definitions}) +in the metric +definition is ignored since this does not affect the outcome of the sorting +operation. +For example if we set the sort key to @code{e.totalcpu}, the values +will be sorted in descending order with respect to the exclusive total +CPU time. + +The data can be sorted in reverse order by prepending the metric definition +with a minus (@code{-}) sign. For example @code{sort -e.totalcpu}. + +A default metric for the sort operation has been defined and since this is +a persistent command, this default can be restored with @code{default} as +the key. + +@c -- A new node -------------------------------------------------------------- +@node Scripting +@subsection Scripting +@c ---------------------------------------------------------------------------- + +As is probably clear by now, the list with commands for @DisplayText{} can be +very long. This is tedious and also error prone. Luckily, there is an easier and +more elegant way to control the behaviour of this tool. + +@IndexSubentry{Commands, @code{script}} +Through the @code{script} command, the name of a file with commands can be +passed in. These commands are parsed and executed as if they appeared on +the command line in the same order as encountered in the file. The commands +in this script file can actually be mixed with commands on the command line. + +The difference between the commands in the script file and those used on the +command line is that the latter require a leading dash (@code{-}) symbol. + +Comment lines are supported. They need to start with the @code{#} symbol. + +@c -- A new node -------------------------------------------------------------- +@node A More Elaborate Example +@subsection A More Elaborate Example +@c ---------------------------------------------------------------------------- + +With the information presented so far, we can customize our data +gathering and display commands. + +As an example, to reflect the name of the algorithm and the number of threads +that were used in the experiment, we select @code{mxv.1.thr.er} +as the name of the experiment directory. +All we then need to +do is to add the + @IndexSubentry{Options, @code{-O}} +@code{-O} +option followed by this name on the command line when running @CollectApp{}: + +@cartouche +@smallexample +$ exe=mxv-pthreads.exe +$ m=3000 +$ n=2000 +$ gprofng collect app -O mxv.1.thr.er ./$exe -m $m -n $n -t 1 +@end smallexample +@end cartouche + +The commands to generate the profile are put into a file that we simply call +@code{my-script}: + +@smallexample +@verbatim +$ cat my-script +# This is my first gprofng script +# Set the metrics +metrics i.%totalcpu:e.%totalcpu:name +# Use the exclusive time to sort +sort e.totalcpu +# Limit the function list to 5 lines +limit 5 +# Show the function list +functions +@end verbatim +@end smallexample + +This script file is then specified as input to the @DisplayText{} command +that is used to display the performance information stored in +@code{mxv.1.thr.er}: + +@cartouche +@smallexample +$ gprofng display text -script my-script mxv.1.thr.er +@end smallexample +@end cartouche + +The command above produces the following output: + +@smallexample +@verbatim +# This is my first gprofng script +# Set the metrics +Current metrics: i.%totalcpu:e.%totalcpu:name +Current Sort Metric: Exclusive Total CPU Time ( e.%totalcpu ) +# Use the exclusive time to sort +Current Sort Metric: Exclusive Total CPU Time ( e.%totalcpu ) +# Limit the function list to 5 lines +Print limit set to 5 +# Show the function list +Functions sorted by metric: Exclusive Total CPU Time + +Incl. Total Excl. Total Name +CPU CPU + sec. % sec. % +2.272 100.00 2.272 100.00 <Total> +2.159 95.00 2.159 95.00 mxv_core +0.102 4.48 0.054 2.37 init_data +0.035 1.54 0.025 1.10 erand48_r +0.048 2.11 0.013 0.57 drand48 +@end verbatim +@end smallexample + +In the first part of the output, our comment lines in the script file are +shown. These are interleaved with an acknowledgement message for the commands. + +This is followed by a profile consisting of 5 lines only. For both metrics, +the percentages plus the timings are given. The numbers are sorted with respect +to the exclusive total CPU time. + +It is now immediately clear that function @code{mxv_core} is responsbile for +95% of the CPU time and @code{init_data} takes 4.5% only. + +This is also where we see sampling in action. Although this is exactly the +same job we profiled before, the timings are somewhat different, but the +differences are very small. + +@c -- A new node -------------------------------------------------------------- +@node The Call Tree +@subsection The Call Tree +@c ---------------------------------------------------------------------------- + +The call tree shows the dynamic hierarchy of the application by displaying the +functions executed and their parent. It helps to find the most expensive path +in the program. + +@IndexSubentry{Commands, @code{calltree}} +This feature is enabled through the @code{calltree} command. This is how to get +this tree for our current experiment: + +@cartouche +@smallexample +$ gprofng display text -calltree mxv.1.thr.er +@end smallexample +@end cartouche + +This displays the following structure: + +@smallexample +@verbatim +Functions Call Tree. Metric: Attributed Total CPU Time + +Attr. Name +Total +CPU sec. +2.272 +-<Total> +2.159 +-collector_root +2.159 | +-driver_mxv +2.159 | +-mxv_core +0.114 +-__libc_start_main +0.114 +-main +0.102 +-init_data +0.048 | +-drand48 +0.035 | +-erand48_r +0.010 | +-__drand48_iterate +0.011 +-allocate_data +0.011 | +-malloc +0.011 | +-_int_malloc +0.001 | +-sysmalloc +0.001 +-check_results +0.001 +-malloc +0.001 +-_int_malloc +@end verbatim +@end smallexample + +At first sight this may not be what you expected and some explanation is in +place. + +@c ---------------------------------------------------------------------------- +@c TBD: Revise this text when we have user and machine mode. +@c ---------------------------------------------------------------------------- +First of all, function @code{collector_root} is internal to @ToolName{} and +should be hidden to the user. This is part of a planned future enhancement. + +Recall that the @code{objects} and @code{fsingle} commands are very useful +to find out more about load objects in general, but also to help identify +an unknown entry in the function overview. @xref{Load Objects and Functions}. + +Another thing to note is that there are two main branches. The one under +@code{collector_root} and the second one under @code{__libc_start_main}. +This reflects the fact that we are executing a parallel program. Even though +we only used one thread for this run, this is still executed in a separate +path. + +The main, sequential part of the program is displayed under @code{main} and +shows the functions called and the time they took. + +There are two things worth noting for the call tree feature: + +@itemize + +@item +This is a dynamic tree and since sampling is used, it most likely looks +slighlty different across seemingly identical profile runs. In case the +run times are short, it is worth considering to use a high resolution +through the +@IndexSubentry{Options, @code{-p}} +@code{-p} +option. For example to use @code{-p hi} to increase the sampling rate. + +@item +In case hardware event counters have been enabled +(@xref{Profile Hardware Event Counters}), these values are also displayed +in the call tree view. + +@end itemize + +@c -- A new node -------------------------------------------------------------- +@node More Information on the Experiment +@subsection More Information on the Experiment +@c ---------------------------------------------------------------------------- + +The experiment directory not only contains performance related data. Several +system characteristics, the actually command executed, and some global +performance statistics can be displayed. + +@IndexSubentry{Commands, @code{header}} +The @code{header} command displays information about the experiment(s). +For example, this is the command to extract this data from for our experiment +directory: + +@cartouche +@smallexample +$ gprofng display text -header mxv.1.thr.er +@end smallexample +@end cartouche + +The above command prints the following information. Note that some of the +lay-out and the information has been modified. The textual changes are +marked with the @code{<} and @code{>} symbols. + +@smallexample +@verbatim +Experiment: mxv.1.thr.er +No errors +No warnings +Archive command `gp-archive -n -a on + --outfile <exp_dir>/archive.log <exp_dir>' + +Target command (64-bit): './mxv-pthreads.exe -m 3000 -n 2000 -t 1' +Process pid 30591, ppid 30589, pgrp 30551, sid 30468 +Current working directory: <cwd> +Collector version: `2.36.50'; experiment version 12.4 (64-bit) +Host `<hostname>', OS `Linux <version>', page size 4096, + architecture `x86_64' + 16 CPUs, clock speed 1995 MHz. + Memory: 30871514 pages @ 4096 = 120591 MB. +Data collection parameters: + Clock-profiling, interval = 997 microsecs. + Periodic sampling, 1 secs. + Follow descendant processes from: fork|exec|combo + +Experiment started <date and time> + +Experiment Ended: 2.293162658 +Data Collection Duration: 2.293162658 +@end verbatim +@end smallexample + +The output above may assist in troubleshooting, or to verify some of the +operational conditions and we recommand to include this command when +generating a profile. + +@IndexSubentry{Options, @code{-C}} +Related to this command there is a useful option to record your own comment(s) in +an experiment. +To this end, use the @code{-C} option on the @CollectApp{} tool to +specify a comment string. Up to ten comment lines can be included. +These comments are displayed with the @code{header} command on +the @DisplayText{} tool. + +@IndexSubentry{Commands, @code{overview}} +The @code{overview} command displays information on the experiment(s) and also +shows a summary of the values for the metric(s) used. This is an example how to +use it on our newly created experiment directory: + +@cartouche +@smallexample +$ gprofng display text -overview mxv.1.thr.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +Experiment(s): + +Experiment :mxv.1.thr.er + Target : './mxv-pthreads.exe -m 3000 -n 2000 -t 1' + Host : <hostname> (<ISA>, Linux <version>) + Start Time : <date and time> + Duration : 2.293 Seconds + +Metrics: + + Experiment Duration (Seconds): [2.293] + Clock Profiling + [X]Total CPU Time - totalcpu (Seconds): [*2.272] + +Notes: '*' indicates hot metrics, '[X]' indicates currently enabled + metrics. + The metrics command can be used to change selections. The + metric_list command lists all available metrics. +@end verbatim +@end smallexample + +This command provides a dashboard overview that helps to easily identify +where the time is spent and in case hardware event counters are used, it +shows their total values. + +@c -- A new node -------------------------------------------------------------- +@node Control the Sampling Frequency +@subsection Control the Sampling Frequency +@c ---------------------------------------------------------------------------- + +So far we did not talk about the frequency of the sampling process, but in +some cases it is useful to change the default of 10 milliseconds. + +The advantage of increasing the sampling frequency is that functions that +do not take much time per invocation are more accurately captured. The +downside is that more data is gathered. This has an impact on the overhead +of the collection process and more disk space is required. + +In general this is not an immediate concern, but with heavily threaded +applications that run for an extended period of time, increasing the +frequency may have a more noticeable impact. + +@IndexSubentry{Options, @code{-p}} +The @code{-p} option on the @CollectApp{} tool is used to enable or disable +clock based profiling, or to explicitly set the sampling rate. +@cindex Sampling interval +This option takes one of the following keywords: + +@table @code + +@item off +Disable clock based profiling. + +@item on +Enable clock based profiling with a per thread sampling interval of 10 ms. This is the default. + +@item lo +Enable clock based profiling with a per thread sampling interval of 100 ms. + +@item hi +Enable clock based profiling with a per thread sampling interval of 1 ms. + +@item <value> +Enable clock based profiling with a per thread sampling interval of <value>. + +@end table + +One may wonder why there is an option to disable clock based profiling. This +is because by default, it is enabled when conducting hardware event counter +experiments (@xref{Profile Hardware Event Counters}). +With the @code{-p off} option, this can be disabled. + +If an explicit value is set for the sampling, the number can be an integer or a +floating-point number. +A suffix of @code{u} for microseconds, or @code{m} for milliseconds is supported. +If no suffix is used, the value is assumed to be in milliseconds. + +If the value is smaller than the clock profiling minimum, a warning message is issued +and it is set to the minimum. +In case it is not a multiple of the clock profiling resolution, it is silently rounded +down to the nearest multiple of the clock resolution. + +If the value exceeds the clock profiling maximum, is negative, or zero, an error is +reported. + +@IndexSubentry{Commands, @code{header}} +Note that the @code{header} command echoes the sampling rate used. + +@c -- A new node -------------------------------------------------------------- +@node Information on Load Objects +@subsection Information on Load Objects +@c ---------------------------------------------------------------------------- + +It may happen that the function list contains a function that is not known to +the user. This can easily happen with library functions for example. +Luckily there are three commands that come in handy then. + +@IndexSubentry{Commands, @code{objects}} +@IndexSubentry{Commands, @code{fsingle}} +@IndexSubentry{Commands, @code{fsummary}} +These commands are @code{objects}, @code{fsingle}, and @code{fsummary}. +They provide details on +@cindex Load objects +load objects (@xref{Load Objects and Functions}). + +The @code{objects} command lists all load objects that have been referenced +during the performance experiment. +Below we show the command and the result for our profile job. Like before, +the (long) path names in the output have been shortened and replaced by the +@IndexSubentry{Miscellaneous, @code{<apath>}} +@code{<apath>} symbol that represents an absolute directory path. + +@cartouche +@smallexample +$ gprofng display text -objects mxv.1.thr.er +@end smallexample +@end cartouche + +The output includes the name and path of the target executable: + +@smallexample +@verbatim + <Unknown> (<Unknown>) + <mxv-pthreads.exe> (<apath>/mxv-pthreads.exe) + <librt-2.17.so> (/usr/lib64/librt-2.17.so) + <libdl-2.17.so> (/usr/lib64/libdl-2.17.so) + <libbfd-2.36.50.20210505.so> (<apath>/libbfd-2.36.50 <etc>) + <libopcodes-2.36.50.20210505.so> (<apath>/libopcodes-2. <etc>) + <libc-2.17.so> (/usr/lib64/libc-2.17.so) + <libpthread-2.17.so> (/usr/lib64/libpthread-2.17.so) + <libm-2.17.so> (/usr/lib64/libm-2.17.so) + <libgp-collector.so> (<apath>/libgp-collector.so) + <ld-2.17.so> (/usr/lib64/ld-2.17.so) + <DYNAMIC_FUNCTIONS> (DYNAMIC_FUNCTIONS) +@end verbatim +@end smallexample + +@IndexSubentry{Commands, @code{fsingle}} +The @code{fsingle} command may be used to get more details on a specific entry +in the function view, say. For example, the command below provides additional +information on the @code{collector_root} function shown in the function overview. + +@cartouche +@smallexample +$ gprofng display text -fsingle collector_root mxv.1.thr.er +@end smallexample +@end cartouche + +Below the output from this command. It has been somewhat modified to match the +display requirements. + +@smallexample +@verbatim +collector_root + Exclusive Total CPU Time: 0. ( 0. %) + Inclusive Total CPU Time: 2.159 ( 95.0%) + Size: 401 + PC Address: 10:0x0001db60 + Source File: <apath>/dispatcher.c + Object File: mxv.1.thr.er/archives/libgp-collector.so_HpzZ6wMR-3b + Load Object: <apath>/libgp-collector.so + Mangled Name: + Aliases: +@end verbatim +@end smallexample + +In this table we not only see how much time was spent in this function, we +also see where it originates from. In addition to this, the size and start +address are given as well. If the source code location is known it is also +shown here. + +@IndexSubentry{Commands, @code{fsummary}} +The related @code{fsummary} command displays the same information as +@code{fsingle}, but for all functions in the function overview, +including @code{<Total>}: + +@cartouche +@smallexample +$ gprofng display text -fsummary mxv.1.thr.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +Functions sorted by metric: Exclusive Total CPU Time + +<Total> + Exclusive Total CPU Time: 2.272 (100.0%) + Inclusive Total CPU Time: 2.272 (100.0%) + Size: 0 + PC Address: 1:0x00000000 + Source File: (unknown) + Object File: (unknown) + Load Object: <Total> + Mangled Name: + Aliases: + +mxv_core + Exclusive Total CPU Time: 2.159 ( 95.0%) + Inclusive Total CPU Time: 2.159 ( 95.0%) + Size: 75 + PC Address: 2:0x000021ba + Source File: <apath>/mxv.c + Object File: mxv.1.thr.er/archives/mxv-pthreads.exe_hRxWdccbJPc + Load Object: <apath>/mxv-pthreads.exe + Mangled Name: + Aliases: + + ... etc ... +@end verbatim +@end smallexample + +@c -- A new node -------------------------------------------------------------- +@node Support for Multithreading +@section Support for Multithreading +@c ---------------------------------------------------------------------------- + +In this chapter we introduce and discuss the support for multithreading. As +is shown below, nothing needs to be changed when collecting the performance +data. + +The difference is that additional commands are available to get more +information on the parallel environment, plus that several filters allow +the user to zoom in on specific threads. + +@c -- A new node -------------------------------------------------------------- +@node Creating a Multithreading Experiment +@subsection Creating a Multithreading Experiment +@c ---------------------------------------------------------------------------- + +We demonstrate the support for multithreading using the same code and settings +as before, but this time we use 2 threads: + +@cartouche +@smallexample +$ exe=mxv-pthreads.exe +$ m=3000 +$ n=2000 +$ gprofng collect app -O mxv.2.thr.er ./$exe -m $m -n $n -t 2 +@end smallexample +@end cartouche + +First of all, note that we did not change anything, other than setting the +number of threads to 2. Nothing special is needed to profile a multithreaded +job when using @ToolName{}. + +The same is true when displaying the performance results. The same commands +that we used before work unmodified. For example, this is all that is needed to +get a function overview: + +@cartouche +@smallexample +$ gpprofng display text -limit 10 -functions mxv.2.thr.er +@end smallexample +@end cartouche + +This produces the following familiar looking output: + +@smallexample +@verbatim +Print limit set to 10 +Functions sorted by metric: Exclusive Total CPU Time + +Excl. Incl. Name +Total Total +CPU sec. CPU sec. +2.268 2.268 <Total> +2.155 2.155 mxv_core +0.044 0.103 init_data +0.030 0.046 erand48_r +0.016 0.016 __drand48_iterate +0.013 0.059 drand48 +0.008 0.011 _int_malloc +0.003 0.003 brk +0. 0.003 __default_morecore +0. 0.114 __libc_start_main +@end verbatim +@end smallexample + +@c -- A new node -------------------------------------------------------------- +@node Commands Specific to Multithreading +@subsection Commands Specific to Multithreading +@c ---------------------------------------------------------------------------- + +The function overview shown above shows the results aggregated over all the +threads. The interesting new element is that we can also look at the +performance data for the individual threads. + +@IndexSubentry{Commands, @code{thread_list}} +The @code{thread_list} command displays how many threads have been used: + +@cartouche +@smallexample +$ gprofng display text -thread_list mxv.2.thr.er +@end smallexample +@end cartouche + +This produces the following output, showing that three threads have +been used: + +@smallexample +@verbatim +Exp Sel Total +=== === ===== + 1 all 3 +@end verbatim +@end smallexample + +The output confirms there is one experiment and that by default all +threads are selected. + +It may seem surprising to see three threads here, since we used the +@code{-t 2} option, but it is common for a Pthreads program to use one +additional thread. This is typically the thread that runs from start to +finish and handles the sequential portions of the code, as well as takes +care of managing the threads. + +It is no different in our example code. At some point, the main thread +creates and activates the two threads that perform the multiplication +of the matrix with the vector. Upon completion of this computation, +the main thread continues. + +@IndexSubentry{Commands, @code{threads}} +The @code{threads} command is simple, yet very powerful. It shows the +total value of the metrics for each thread. To make it easier to +interpret the data, we modify the metrics to include percentages: + +@cartouche +@smallexample +$ gprofng display text -metrics e.%totalcpu -threads mxv.2.thr.er +@end smallexample +@end cartouche + +The command above produces the following overview: + +@smallexample +@verbatim +Current metrics: e.%totalcpu:name +Current Sort Metric: Exclusive Total CPU Time ( e.%totalcpu ) +Objects sorted by metric: Exclusive Total CPU Time + +Excl. Total Name +CPU + sec. % +2.258 100.00 <Total> +1.075 47.59 Process 1, Thread 3 +1.070 47.37 Process 1, Thread 2 +0.114 5.03 Process 1, Thread 1 +@end verbatim +@end smallexample + +The first line gives the total CPU time accumulated over the threads +selected. This is followed by the metric value(s) for each thread. + +From this it is clear that the main thread is responsible for 5% of +the total CPU time, while the other two threads take 47% each. + +This view is ideally suited to verify if there any load balancing +issues and also to find the most time consuming thread(s). + +@IndexSubentry{Filters, Thread selection} +While useful, often more information than this is needed. This is +@IndexSubentry{Commands, @code{thread_select}} +where the thread selection filter comes in. Through the @code{thread_select} +command, one or more threads may be selected +(@xref{The Selection List} how to define the selection list). + +Since it is most common to use this command in a script, we do so as +well here. Below the script we are using: + +@cartouche +@smallexample +# Define the metrics +metrics e.%totalcpu +# Limit the output to 10 lines +limit 10 +# Get the function overview for thread 1 +thread_select 1 +functions +# Get the function overview for thread 2 +thread_select 2 +functions +# Get the function overview for thread 3 +thread_select 3 +functions +@end smallexample +@end cartouche + +The definition of the metrics and the output limiter has been shown and +explained before and will be ignored. The new command we focus on is +@IndexSubentry{Commands, @code{thread_select}} +@code{thread_select}. + +This command takes a list (@xref{The Selection List}) to select specific +threads. In this case we simply use the individual thread numbers that we +obtained with the @code{thread_list} command earlier. + +This restricts the output of the @code{functions} command to the thread +number(s) specified. This means that the script above shows which +function(s) each thread executes and how much CPU time they consumed. +Both the timings and their percentages are given. + +This is the relevant part of the output for the first thread: + +@smallexample +@verbatim +# Get the function overview for thread 1 +Exp Sel Total +=== === ===== + 1 1 3 +Functions sorted by metric: Exclusive Total CPU Time + +Excl. Total Name +CPU + sec. % +0.114 100.00 <Total> +0.051 44.74 init_data +0.028 24.56 erand48_r +0.017 14.91 __drand48_iterate +0.010 8.77 _int_malloc +0.008 7.02 drand48 +0. 0. __libc_start_main +0. 0. allocate_data +0. 0. main +0. 0. malloc +@end verbatim +@end smallexample + +As usual, the comment lines are echoed. This is followed by a confirmation +of our selection. We see that indeed thread 1 has been selected. What is +displayed next is the function overview for this particular thread. Due to +the @code{limit 10} command, there are ten entries in this list. + +Below are the overviews for threads 2 and 3 respectively. We see that all +of the CPU time is spent in function @code{mxv_core} and that this time +is approximately the same for both threads. + +@smallexample +@verbatim +# Get the function overview for thread 2 +Exp Sel Total +=== === ===== + 1 2 3 +Functions sorted by metric: Exclusive Total CPU Time + +Excl. Total Name +CPU + sec. % +1.072 100.00 <Total> +1.072 100.00 mxv_core +0. 0. collector_root +0. 0. driver_mxv + +# Get the function overview for thread 3 +Exp Sel Total +=== === ===== + 1 3 3 +Functions sorted by metric: Exclusive Total CPU Time + +Excl. Total Name +CPU + sec. % +1.076 100.00 <Total> +1.076 100.00 mxv_core +0. 0. collector_root +0. 0. driver_mxv +@end verbatim +@end smallexample + +When analyzing the performance of a multithreaded application, it is sometimes +useful to know whether threads have mostly executed on the same core, say, or +if they have wandered across multiple cores. This sort of stickiness is usually +referred to as +@cindex Thread affinity +@emph{thread affinity}. + +Similar to the commands for the threads, there are several commands related +to the usage of the cores, or @emph{CPUs} as they are called in @ToolName{} +(@xref{The Concept of a CPU in @ProductName{}}). + +In order to have some more interesting data to look at, we created a new +experiment, this time using 8 threads: + +@cartouche +@smallexample +$ exe=mxv-pthreads.exe +$ m=3000 +$ n=2000 +$ gprofng collect app -O mxv.8.thr.er ./$exe -m $m -n $n -t 8 +@end smallexample +@end cartouche + +@IndexSubentry{Commands, @code{cpu_list}} +Similar to the @code{thread_list} command, the @code{cpu_list} command +displays how many CPUs have been used. +@IndexSubentry{Commands, @code{cpus}} +The equivalent of the @code{threads} threads command, is the @code{cpus} +command, which shows the CPU numbers that were used and how much time was +spent on each of them. Both are demonstrated below. + +@cartouche +@smallexample +$ gprofng display text -metrics e.%totalcpu -cpu_list -cpus mxv.8.thr.er +@end smallexample +@end cartouche + +This command produces the following output: + +@smallexample +@verbatim +Current metrics: e.%totalcpu:name +Current Sort Metric: Exclusive Total CPU Time ( e.%totalcpu ) +Exp Sel Total +=== === ===== + 1 all 10 +Objects sorted by metric: Exclusive Total CPU Time + +Excl. Total Name +CPU + sec. % +2.310 100.00 <Total> +0.286 12.39 CPU 7 +0.284 12.30 CPU 13 +0.282 12.21 CPU 5 +0.280 12.13 CPU 14 +0.266 11.52 CPU 9 +0.265 11.48 CPU 2 +0.264 11.44 CPU 11 +0.194 8.42 CPU 0 +0.114 4.92 CPU 1 +0.074 3.19 CPU 15 +@end verbatim +@end smallexample + +@c ---------------------------------------------------------------------------- +@c TBD - Ruud +@c I'd like to improve this and have a way to see where a thread has executed. +@c ---------------------------------------------------------------------------- + +What we see in this table is that a total of 10 CPUs have been used. This is +followed by a list with all the CPU numbers that have been used during the +run. For each CPU it is shown how much time was spent on it. + +While the table with thread times shown earlier may point at a load imbalance +in the application, this overview has a different purpose. + +For example, we see that 10 CPUs have been used, but we know that the +application uses 9 threads only. +This means that at least one thread has executed on more than one CPU. In +itself this is not something to worry about, but warrants a deeper +investigation. + +Honesty dictates that next we performed a pre-analysis to find out +which thread(s) have been running on more than one CPU. We found this +to be thread 7. It has executed on CPUs 0 and 15. + +With this knowledge, we wrote the script shown below. It zooms in on +the behaviour of thread 7. + +@cartouche +@smallexample +# Define the metrics +metrics e.%totalcpu +# Limit the output to 10 lines +limit 10 +functions +# Get the function overview for CPU 0 +cpu_select 0 +functions +# Get the function overview for CPU 15 +cpu_select 15 +functions +@end smallexample +@end cartouche + +From the earlier shown threads overview, we know that thread 7 has +used @code{0.268} seconds of CPU time.. + +By selecting CPUs 0 and 15, respectively, we get the following +function overviews: + +@smallexample +@verbatim +# Get the function overview for CPU 0 +Exp Sel Total +=== === ===== + 1 0 10 +Functions sorted by metric: Exclusive Total CPU Time + +Excl. Total Name +CPU + sec. % +0.194 100.00 <Total> +0.194 100.00 mxv_core +0. 0. collector_root +0. 0. driver_mxv + +# Get the function overview for CPU 15 +Exp Sel Total +=== === ===== + 1 15 10 +Functions sorted by metric: Exclusive Total CPU Time + +Excl. Total Name +CPU + sec. % +0.074 100.00 <Total> +0.074 100.00 mxv_core +0. 0. collector_root +0. 0. driver_mxv +@end verbatim +@end smallexample + +This shows that thread 7 spent @code{0.194} seconds on CPU 0 and +@code{0.074} seconds on CPU 15. + +@c -- A new node -------------------------------------------------------------- +@node Viewing Multiple Experiments +@section Viewing Multiple Experiments +@c ---------------------------------------------------------------------------- + +One thing we did not cover sofar is that @ToolName{} fully supports the analysis +of multiple experiments. The @DisplayText{} tool accepts a list of experiments. +The data can either be aggregated across the experiments, or used in a +comparison. + +Mention @code{experiment_list} + +@c -- A new node -------------------------------------------------------------- +@node Aggregation of Experiments +@subsection Aggregation of Experiments +@c ---------------------------------------------------------------------------- + +By default, the data for multiple experiments is aggregrated and the display +commands shows these combined results. + +For example, we can aggregate the data for our single and dual thread +experiments. Below is the script we used for this: + +@cartouche +@smallexample +# Define the metrics +metrics e.%totalcpu +# Limit the output to 10 lines +limit 10 +# Get the list with experiments +experiment_list +# Get the function overview +functions +@end smallexample +@end cartouche + +@IndexSubentry{Commands, @code{experiment_list}} +With the exception of the @code{experiment_list} command, all commands +used have been discussed earlier. + +The @code{experiment_list} command provides a list of the experiments +that have been loaded. This is is used to verify we are looking at the +experiments we intend to aggregate. + +@cartouche +@smallexample +$ gprofng display text -script my-script-agg mxv.1.thr.er mxv.2.thr.er +@end smallexample +@end cartouche + +With the command above, we get the following output: + +@smallexample +@verbatim +# Define the metrics +Current metrics: e.%totalcpu:name +Current Sort Metric: Exclusive Total CPU Time ( e.%totalcpu ) +# Limit the output to 10 lines +Print limit set to 10 +# Get the list with experiments +ID Sel PID Experiment +== === ===== ============ + 1 yes 30591 mxv.1.thr.er + 2 yes 11629 mxv.2.thr.er +# Get the function overview +Functions sorted by metric: Exclusive Total CPU Time + +Excl. Total Name +CPU + sec. % +4.533 100.00 <Total> +4.306 94.99 mxv_core +0.105 2.31 init_data +0.053 1.17 erand48_r +0.027 0.59 __drand48_iterate +0.021 0.46 _int_malloc +0.021 0.46 drand48 +0.001 0.02 sysmalloc +0. 0. __libc_start_main +0. 0. allocate_data +@end verbatim +@end smallexample + +The first five lines should look familiar. The five lines following, echo +the comment line in the script and show the overview of the experiments. +This confirms two experiments have been loaded and that both are active. + +This is followed by the function overview. The timings have been summed +up and the percentages are adjusted accordingly. For example, the total +accumulated time is indeed 2.272 + 2.261 = 4.533 seconds. + +@c -- A new node -------------------------------------------------------------- +@node Comparison of Experiments +@subsection Comparison of Experiments +@c ---------------------------------------------------------------------------- + +The support for multiple experiments really shines in comparison mode. This +feature is enabled through the command +@IndexSubentry{Commands, @code{compare on/off}} +@code{compare on} +and is disabled +by setting +@code{compare off}. + +@cindex Compare experiments +In comparison mode, the data for the various experiments is shown side by +side, as illustrated below where we compare the results for the multithreaded +experiments using one and two threads respectively: + +@cartouche +@smallexample +$ gprofng display text -compare on -functions mxv.1.thr.er mxv.2.thr.er +@end smallexample +@end cartouche + +@noindent +This produces the following output: + +@smallexample +@verbatim +Functions sorted by metric: Exclusive Total CPU Time + +mxv.1.thr.er mxv.2.thr.er mxv.1.thr.er mxv.2.thr.er +Excl. Total Excl. Total Incl. Total Incl. Total Name +CPU CPU CPU CPU + sec. sec. sec. sec. +2.272 2.261 2.272 2.261 <Total> +2.159 2.148 2.159 2.148 mxv_core +0.054 0.051 0.102 0.104 init_data +0.025 0.028 0.035 0.045 erand48_r +0.013 0.008 0.048 0.053 drand48 +0.011 0.010 0.012 0.010 _int_malloc +0.010 0.017 0.010 0.017 __drand48_iterate +0.001 0. 0.001 0. sysmalloc +0. 0. 0.114 0.114 __libc_start_main +0. 0. 0.011 0.010 allocate_data +0. 0. 0.001 0. check_results +0. 0. 2.159 2.148 collector_root +0. 0. 2.159 2.148 driver_mxv +0. 0. 0.114 0.114 main +0. 0. 0.012 0.010 malloc +@end verbatim +@end smallexample + +This table is already helpful to more easily compare (two) profiles, but +there is more that we can do here. + +By default, in comparison mode, all measured values are shown. Often +profiling is about comparing performance data. It is therefore +more useful to look at differences, or ratios, using one experiment as +a reference. + +The values shown are relative to this difference. For example if a ratio +is below one, it means the reference value was higher. + +@IndexSubentry{Commands, @code{compare on/off}} +This feature is supported on the @code{compare} command. In addition to @code{on}, +or @code{off}, this command also supports +@IndexSubentry{Commands, @code{compare delta}} +@code{delta}, or +@IndexSubentry{Commands, @code{compare ratio}} +@code{ratio}. + +Usage of one of these two keywords enables the comparison feature and shows +either the difference, or the ratio, relative to the reference data. + +In the example below, we use the same two experiments used in the comparison +above, but as before, the number of lines is restricted to 10 and we focus on +the exclusive timings plus percentages. For the comparison part we are +interested in the differences. + +This is the script that produces such an overview: + +@cartouche +@smallexample +# Define the metrics +metrics e.%totalcpu +# Limit the output to 10 lines +limit 10 +# Set the comparison mode to differences +compare delta +# Get the function overview +functions +@end smallexample +@end cartouche + +Assuming this script file is called @code{my-script-comp}, this is how we +get the table displayed on our screen: + +@cartouche +@smallexample +$ gprofng display text -script my-script-comp mxv.1.thr.er mxv.2.thr.er +@end smallexample +@end cartouche + +Leaving out some of the lines printed, but we have seen before, we get +the following table: + +@smallexample +@verbatim +mxv.1.thr.er mxv.2.thr.er +Excl. Total Excl. Total Name +CPU CPU + sec. % delta % +2.272 100.00 -0.011 100.00 <Total> +2.159 95.00 -0.011 94.97 mxv_core +0.054 2.37 -0.003 2.25 init_data +0.025 1.10 +0.003 1.23 erand48_r +0.013 0.57 -0.005 0.35 drand48 +0.011 0.48 -0.001 0.44 _int_malloc +0.010 0.44 +0.007 0.75 __drand48_iterate +0.001 0.04 -0.001 0. sysmalloc +0. 0. +0. 0. __libc_start_main +0. 0. +0. 0. allocate_data +@end verbatim +@end smallexample + +It is now easy to see that the CPU times for the most time consuming +functions in this code are practically the same. + +While in this case we used the delta as a comparison, + +Note that the comparison feature is supported at the function, source, and +disassembly level. There is no practical limit on the number of experiments +that can be used in a comparison. + + + +@c -- A new node -------------------------------------------------------------- +@node Profile Hardware Event Counters +@section Profile Hardware Event Counters +@c ---------------------------------------------------------------------------- + +Many processors provide a set of hardware event counters and @ToolName{} +provides support for this feature. +@xref{Hardware Event Counters Explained} for those readers that are not +familiar with such counters and like to learn more. + +In this section we explain how to get the details on the event counter +support for the processor used in the experiment(s), and show several +examples. + +@c -- A new node -------------------------------------------------------------- +@node Getting Information on the Counters Supported +@subsection Getting Information on the Counters Supported +@c ---------------------------------------------------------------------------- + +The first step is to check if the processor used for the experiments is +supported by @ToolName{}. + +@IndexSubentry{Options, @code{-h}} +The @code{-h} option on @CollectApp{} will show the event counter +information: + +@cartouche +@smallexample +$ gprofng collect app -h +@end smallexample +@end cartouche + +In case the counters are supported, a list with the events is printed. +Otherwise, a warning message will be issued. + +For example, below we show this command and the output on an Intel Xeon +Platinum 8167M (aka ``Skylake'') processor. The output has been split +into several sections and each section is commented upon separately. + +@smallexample +@verbatim +Run "gprofng collect app --help" for a usage message. + +Specifying HW counters on `Intel Arch PerfMon v2 on Family 6 Model 85' +(cpuver=2499): + + -h {auto|lo|on|hi} + turn on default set of HW counters at the specified rate + -h <ctr_def> [-h <ctr_def>]... + -h <ctr_def>[,<ctr_def>]... + specify HW counter profiling for up to 4 HW counters +@end verbatim +@end smallexample + +The first line shows how to get a usage overview. This is followed by +some information on the target processor. + +The next five lines explain in what ways the @code{-h} option can be +used to define the events to be monitored. + +The first version shown above enables a default set of counters. This +default depends on the processor this command is executed on. The +keyword following the @code{-h} option defines the sampling rate: + +@table @code + +@item auto +Match the sample rate of used by clock profiling. If the latter is disabled, +Use a per thread sampling rate of approximately 100 samples per second. +This setting is the default and preferred. + +@item on +Use a per thread sampling rate of approximately 100 samples per second. + +@item lo +Use a per thread sampling rate of approximately 10 samples per second. + +@item hi +Use a per thread sampling rate of approximately 1000 samples per second. + +@end table + +The second and third variant define the events to be monitored. Note +that the number of simultaneous events supported is printed. In this +case we can monitor four events in a single profiling job. + +It is a matter of preference whether you like to use the @code{-h} +option for each event, or use it once, followed by a comma separated +list. + +There is one slight catch though. The counter definition below has +mandatory comma (@code{,}) between the event and the rate. While a +default can be used for the rate, the comma cannot be omitted. +This may result in a somewhat awkward counter definition in case +the default sampling rate is used. + +For example, the following two commands are equivalent. Note +the double comma in the second command. This is not a typo. + +@cartouche +@smallexample +$ gprofng collect app -h cycles -h insts ... +$ gprofng collect app -h cycles,,insts ... +@end smallexample +@end cartouche + +In the first command this comma is not needed, because a +comma (``@code{,}'') immediately followed by white space may +be omitted. + +This is why we prefer the this syntax and in the remainder will +use the first version of this command. + +@IndexSubentry{Hardware event counters, counter definition} +The counter definition takes an event name, plus optionally one or +more attributes, followed by a comma, and optionally the sampling rate. +The output section below shows the formal definition. + +@cartouche +@smallexample + <ctr_def> == <ctr>[[~<attr>=<val>]...],[<rate>] +@end smallexample +@end cartouche + +The printed help then explains this syntax. Below we have summarized +and expanded this output: + +@table @code + +@item <ctr> +The counter name must be selected from the available counters listed +as part of the output printed with the @code{-h} option. +On most systems, if a counter is not listed, it may still be specified +by its numeric value. + +@item ~<attr>=<val> +This is an optional attribute that depends on the processor. The list +of supported attributes is printed in the output. Examples of +attributes are ``user'', or ``system''. The value can given in decimal +or hexadecimal format. +Multiple attributes may be specified, and each must be preceded +by a ~. + +@item <rate> + +The sampling rate is one of the following: + +@table @code + +@item auto +This is the default and matches the rate used by clock profiling. +If clock profiling is disabled, use @code{on}. + +@item on +Set the per thread maximum sampling rate to ~100 samples/second + +@item lo +Set the per thread maximum sampling rate to ~10 samples/second + +@item hi +Set the per thread maximum sampling rate to ~1000 samples/second + +@item <interval> +Define the sampling interval. +@xref{Control the Sampling Frequency} how to define this. + +@end table + +@end table + +After the section with the formal definition of events and counters, a +processor specific list is displayed. This part starts with an overview +of the default set of counters and the aliased names supported +@emph{on this specific processor}. + +@smallexample +@verbatim +Default set of HW counters: + + -h cycles,,insts,,llm + +Aliases for most useful HW counters: + + alias raw name type units regs description + + cycles unhalted-core-cycles CPU-cycles 0123 CPU Cycles + insts instruction-retired events 0123 Instructions Executed + llm llc-misses events 0123 Last-Level Cache Misses + br_msp branch-misses-retired events 0123 Branch Mispredict + br_ins branch-instruction-retired events 0123 Branch Instructions +@end verbatim +@end smallexample + +The definitions given above may or may not be available on other processors, +but we try to maximize the overlap across alias sets. + +The table above shows the default set of counters defined for this processor, +and the aliases. For each alias the full ``raw'' name is given, plus the +unit of the number returned by the counter (CPU cycles, or a raw count), +the hardware counter the event is allowed to be mapped onto, and a short +description. + +The last part of the output contains all the events that can be monitored: + +@smallexample +@verbatim +Raw HW counters: + + name type units regs description + + unhalted-core-cycles CPU-cycles 0123 + unhalted-reference-cycles events 0123 + instruction-retired events 0123 + llc-reference events 0123 + llc-misses events 0123 + branch-instruction-retired events 0123 + branch-misses-retired events 0123 + ld_blocks.store_forward events 0123 + ld_blocks.no_sr events 0123 + ld_blocks_partial.address_alias events 0123 + dtlb_load_misses.miss_causes_a_walk events 0123 + dtlb_load_misses.walk_completed_4k events 0123 + + <many lines deleted> + + l2_lines_out.silent events 0123 + l2_lines_out.non_silent events 0123 + l2_lines_out.useless_hwpf events 0123 + sq_misc.split_lock events 0123 + +See Chapter 19 of the "Intel 64 and IA-32 Architectures Software +Developer's Manual Volume 3B: System Programming Guide" +@end verbatim +@end smallexample + +As can be seen, these names are not always easy to correlate to a specific +event of interest. The processor manual should provide more clarity on this. + +@c -- A new node -------------------------------------------------------------- +@node Examples Using Hardware Event Counters +@subsection Examples Using Hardware Event Counters +@c ---------------------------------------------------------------------------- + +The previous section may give the impression that these counters are hard to +use, but as we will show now, in practice it is quite simple. + +With the information from the @code{-h} option, we can easily set up our first +event counter experiment. + +We start by using the default set of counters defined for our processor and we +use 2 threads: + +@cartouche +@smallexample +$ exe=mxv-pthreads.exe +$ m=3000 +$ n=2000 +$ exp=mxv.hwc.def.2.thr.er +$ gprofng collect app -O $exp -h auto ./$exe -m $m -n $n -t 2 +@end smallexample +@end cartouche + +@IndexSubentry{Options, @code{-h}} +@IndexSubentry{Hardware event counters, @code{auto} option} +The new option here is @code{-h auto}. The @code{auto} keyword enables +hardware event counter profiling and selects the default set of counters +defined for this processor. + +As before, we can display the information, but there is one practical hurdle +to take. Unless we like to view all metrics recorded, we would need to know +the names of the events that have been enabled. This is tedious and also not +portable in case we would like to repeat this experiment on another processor. + +@IndexSubentry{Hardware event counters, @code{hwc} metric} +This is where the special @code{hwc} metric comes very handy. It +automatically expands to the active set of events used. + +With this, it is very easy to display the event counter values. Note that +although the regular clock based profiling was enabled, we only want to see +the counter values. We also request to see the percentages and limit the +output to the first 5 lines: + +@cartouche +@smallexample +$ exp=mxv.hwc.def.2.thr.er +$ gprofng display text -metrics e.%hwc -limit 5 -functions $exp +@end smallexample +@end cartouche + +@smallexample +@verbatim +Current metrics: e.%cycles:e+%insts:e+%llm:name +Current Sort Metric: Exclusive CPU Cycles ( e.%cycles ) +Print limit set to 5 +Functions sorted by metric: Exclusive CPU Cycles + +Excl. CPU Excl. Instructions Excl. Last-Level Name +Cycles Executed Cache Misses + sec. % % % +2.691 100.00 7906475309 100.00 122658983 100.00 <Total> +2.598 96.54 7432724378 94.01 121745696 99.26 mxv_core +0.035 1.31 188860269 2.39 70084 0.06 erand48_r +0.026 0.95 73623396 0.93 763116 0.62 init_data +0.018 0.66 76824434 0.97 40040 0.03 drand48 +@end verbatim +@end smallexample + +As we have seen before, the first few lines echo the settings. +This includes a list with the hardware event counters used by +default. + +The table that follows makes it very easy to get an overview where the +time is spent and how many of the target events have occurred. + +As before, we can drill down deeper and see the same metrics at the source +line and instruction level. Other than using @code{hwc} in the metrics +definitions, nothing has changed compared to the previous examples: + +@cartouche +@smallexample +$ exp=mxv.hwc.def.2.thr.er +$ gprofng display text -metrics e.hwc -source mxv_core $exp +@end smallexample +@end cartouche + +This is the relevant part of the output. Since the lines get very long, +we have somewhat modified the lay-out: + +@smallexample +@verbatim + Excl. CPU Excl. Excl. + Cycles Instructions Last-Level + sec. Executed Cache Misses + <Function: mxv_core> + 0. 0 0 32. void __attribute__ ((noinline)) + mxv_core(...) + 0. 0 0 33. { + 0. 0 0 34. for (uint64_t i=...) { + 0. 0 0 35. double row_sum = 0.0; +## 1.872 7291879319 88150571 36. for (int64_t j=0; j<n; j++) + 0.725 140845059 33595125 37. row_sum += A[i][j]*b[j]; + 0. 0 0 38. c[i] = row_sum; + 39. } + 0. 0 0 40. } +@end verbatim +@end smallexample + +In a smiliar way we can display the event counter values at the instruction +level. Again we have modified the lay-out due to page width limitations: + +@cartouche +@smallexample +$ exp=mxv.hwc.def.2.thr.er +$ gprofng display text -metrics e.hwc -disasm mxv_core $exp +@end smallexample +@end cartouche + +@smallexample +@verbatim + Excl. CPU Excl. Excl. + Cycles Instructions Last-Level + sec. Executed Cache Misses + <Function: mxv_core> + 0. 0 0 [33] 4021ba: mov 0x8(%rsp),%r10 + 34. for (uint64_t i=...) { + 0. 0 0 [34] 4021bf: cmp %rsi,%rdi + 0. 0 0 [34] 4021c2: jbe 0x37 + 0. 0 0 [34] 4021c4: ret + 35. double row_sum = 0.0; + 36. for (int64_t j=0; j<n; j++) + 37. row_sum += A[i][j]*b[j]; + 0. 0 0 [37] 4021c5: mov (%r8,%rdi,8),%rdx + 0. 0 0 [36] 4021c9: mov $0x0,%eax + 0. 0 0 [35] 4021ce: pxor %xmm1,%xmm1 + 0.002 12804230 321394 [37] 4021d2: movsd (%rdx,%rax,8),%xmm0 + 0.141 60819025 3866677 [37] 4021d7: mulsd (%r9,%rax,8),%xmm0 + 0.582 67221804 29407054 [37] 4021dd: addsd %xmm0,%xmm1 +## 1.871 7279075109 87989870 [36] 4021e1: add $0x1,%rax + 0.002 12804210 80351 [36] 4021e5: cmp %rax,%rcx + 0. 0 0 [36] 4021e8: jne 0xffffffffffffffea + 38. c[i] = row_sum; + 0. 0 0 [38] 4021ea: movsd %xmm1,(%r10,%rdi,8) + 0. 0 0 [34] 4021f0: add $0x1,%rdi + 0. 0 0 [34] 4021f4: cmp %rdi,%rsi + 0. 0 0 [34] 4021f7: jb 0xd + 0. 0 0 [35] 4021f9: pxor %xmm1,%xmm1 + 0. 0 0 [36] 4021fd: test %rcx,%rcx + 0. 0 80350 [36] 402200: jne 0xffffffffffffffc5 + 0. 0 0 [36] 402202: jmp 0xffffffffffffffe8 + 39. } + 40. } + 0. 0 0 [40] 402204: ret +@end verbatim +@end smallexample + +So far we have used the default settings for the event counters. It is +quite straightforward to select specific counters. For sake of the +example, let's assume we would like to count how many branch instructions +and retired memory load instructions that missed in the L1 cache have been +executed. We also want to count these events with a high resolution. + +This is the command to do so: + +@cartouche +@smallexample +$ exe=mxv-pthreads.exe +$ m=3000 +$ n=2000 +$ exp=mxv.hwc.sel.2.thr.er +$ hwc1=br_ins,hi +$ hwc2=mem_load_retired.l1_miss,hi +$ gprofng collect app -O $exp -h $hwc1 -h $hwc2 $exe -m $m -n $n -t 2 +@end smallexample +@end cartouche + +As before, we get a table with the event counts. Due to the very +long name for the second counter, we have somewhat modified the +output. + +@cartouche +@smallexample +$ gprofng display text -limit 10 -functions mxv.hwc.sel.2.thr.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +Functions sorted by metric: Exclusive Total CPU Time +Excl. Incl. Excl. Branch Excl. Name +Total Total Instructions mem_load_retired.l1_miss +CPU sec. CPU sec. Events +2.597 2.597 1305305319 4021340 <Total> +2.481 2.481 1233233242 3982327 mxv_core +0.040 0.107 19019012 9003 init_data +0.028 0.052 23023048 15006 erand48_r +0.024 0.024 19019008 9004 __drand48_iterate +0.015 0.067 11011009 2998 drand48 +0.008 0.010 0 3002 _int_malloc +0.001 0.001 0 0 brk +0.001 0.002 0 0 sysmalloc +0. 0.001 0 0 __default_morecore +@end verbatim +@end smallexample + +@IndexSubentry{Commands, @code{compare ratio}} +When using event counters, the values could be very large and it is not easy +to compare the numbers. As we will show next, the @code{ratio} feature is +very useful when comparing such profiles. + +To demonstrate this, we have set up another event counter experiment where +we would like to compare the number of last level cache miss and the number +of branch instructions executed when using a single thread, or two threads. + +These are the commands used to generate the experiment directories: + +@cartouche +@smallexample +$ exe=./mxv-pthreads.exe +$ m=3000 +$ n=2000 +$ exp1=mxv.hwc.comp.1.thr.er +$ exp2=mxv.hwc.comp.2.thr.er +$ gprofng collect app -O $exp1 -h llm -h br_ins $exe -m $m -n $n -t 1 +$ gprofng collect app -O $exp2 -h llm -h br_ins $exe -m $m -n $n -t 2 +@end smallexample +@end cartouche + +The following script has been used to get the tables. Due to lay-out +restrictions, we have to create two tables, one for each counter. + +@cartouche +@smallexample +# Limit the output to 5 lines +limit 5 +# Define the metrics +metrics name:e.llm +# Set the comparison to ratio +compare ratio +functions +# Define the metrics +metrics name:e.br_ins +# Set the comparison to ratio +compare ratio +functions +@end smallexample +@end cartouche + +Note that we print the name of the function first, followed by the counter +data. +The new element is that we set the comparison mode to @code{ratio}. This +divides the data in a column by its counterpart in the reference experiment. + +This is the command using this script and the two experiment directories as +input: + +@cartouche +@smallexample +$ gprofng display text -script my-script-comp-counters \ + mxv.hwc.comp.1.thr.er \ + mxv.hwc.comp.2.thr.er +@end smallexample +@end cartouche + +By design, we get two tables, one for each counter: + +@smallexample +@verbatim +Functions sorted by metric: Exclusive Last-Level Cache Misses + + mxv.hwc.comp.1.thr.er mxv.hwc.comp.2.thr.er +Name Excl. Last-Level Excl. Last-Level + Cache Misses Cache Misses + ratio + <Total> 122709276 x 0.788 + mxv_core 121796001 x 0.787 + init_data 723064 x 1.055 + erand48_r 100111 x 0.500 + drand48 60065 x 1.167 + +Functions sorted by metric: Exclusive Branch Instructions + + mxv.hwc.comp.1.thr.er mxv.hwc.comp.2.thr.er +Name Excl. Branch Excl. Branch + Instructions Instructions + ratio + <Total> 1307307316 x 0.997 + mxv_core 1235235239 x 0.997 + erand48_r 23023033 x 0.957 + drand48 20020009 x 0.600 + __drand48_iterate 17017028 x 0.882 +@end verbatim +@end smallexample + +A ratio less than one in the second column, means that this counter +value was smaller than the value from the reference experiment shown +in the first column. + +This kind of presentation of the results makes it much easier to +quickly interpret the data. + +We conclude this section with thread-level event counter overviews, +but before we go into this, there is an important metric we need to +mention. + +@IndexSubentry{Hardware event counters, IPC} +In case it is known how many instructions and CPU cycles have been executed, +the value for the IPC (``Instructions Per Clockycle'') can be computed. +@xref{Hardware Event Counters Explained}. +This is a derived metric that gives an indication how well the processor +is utilized. The inverse of the IPC is called CPI. + +The @DisplayText{} command automatically computes the IPC and CPI values +if an experiment contains the event counter values for the instructions +and CPU cycles executed. These are part of the metric list and can be +displayed, just like any other metric. + +@IndexSubentry{Commands, @code{metric_list}} +This can be verified through the @code{metric_list} command. If we go +back to our earlier experiment with the default event counters, we get +the following result. + +@cartouche +@smallexample +$ gprofng display text -metric_list mxv.hwc.def.2.thr.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +Current metrics: e.totalcpu:i.totalcpu:e.cycles:e+insts:e+llm:name +Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu ) +Available metrics: + Exclusive Total CPU Time: e.%totalcpu + Inclusive Total CPU Time: i.%totalcpu + Exclusive CPU Cycles: e.+%cycles + Inclusive CPU Cycles: i.+%cycles + Exclusive Instructions Executed: e+%insts + Inclusive Instructions Executed: i+%insts +Exclusive Last-Level Cache Misses: e+%llm +Inclusive Last-Level Cache Misses: i+%llm + Exclusive Instructions Per Cycle: e+IPC + Inclusive Instructions Per Cycle: i+IPC + Exclusive Cycles Per Instruction: e+CPI + Inclusive Cycles Per Instruction: i+CPI + Size: size + PC Address: address + Name: name +@end verbatim +@end smallexample + +Among the other metrics, we see the new metrics for the IPC and CPI +listed. + +In the script below, we use this information and add the IPC and CPI +to the metrics to be displayed. We also use a the thread filter to +display these values for the individual threads. + +This is the complete script we have used. Other than a different selection +of the metrics, there are no new features. + +@cartouche +@smallexample +# Define the metrics +metrics e.insts:e.%cycles:e.IPC:e.CPI +# Sort with respect to cycles +sort e.cycles +# Limit the output to 5 lines +limit 5 +# Get the function overview for all threads +functions +# Get the function overview for thread 1 +thread_select 1 +functions +# Get the function overview for thread 2 +thread_select 2 +functions +# Get the function overview for thread 3 +thread_select 3 +functions +@end smallexample +@end cartouche + +In the metrics definition on the second line, we explicitly request the +counter values for the instructions (@code{e.insts}) and CPU cycles +(@code{e.cycles}) executed. These names can be found in output from the +@code{metric_list} commad above. +In addition to these metrics, we also request the IPC and CPI to be shown. + +As before, we used the @code{limit} command to control the number of +functions displayed. We then request an overview for all the threads, +followed by three sets of two commands to select a thread and display the +function overview. + +The script above is used as follows: + +@cartouche +@smallexample +$ gprofng display text -script my-script-ipc mxv.hwc.def.2.thr.er +@end smallexample +@end cartouche + +This script produces four tables. We list them separately below, +and have left out the additional output. + +The first table shows the accumulated values across the three +threads that have been active. + +@smallexample +@verbatim +Functions sorted by metric: Exclusive CPU Cycles + +Excl. Excl. CPU Excl. Excl. Name +Instructions Cycles IPC CPI +Executed sec. % +7906475309 2.691 100.00 1.473 0.679 <Total> +7432724378 2.598 96.54 1.434 0.697 mxv_core + 188860269 0.035 1.31 2.682 0.373 erand48_r + 73623396 0.026 0.95 1.438 0.696 init_data + 76824434 0.018 0.66 2.182 0.458 drand48 +@end verbatim +@end smallexample + +This shows that IPC of this program is completely dominated +by function @code{mxv_core}. It has a fairly low IPC value +of 1.43. + +The next table is for thread 1 and shows the values for the +main thread. + +@smallexample +@verbatim +Exp Sel Total +=== === ===== + 1 1 3 +Functions sorted by metric: Exclusive CPU Cycles + +Excl. Excl. CPU Excl. Excl. Name +Instructions Cycles IPC CPI +Executed sec. % +473750931 0.093 100.00 2.552 0.392 <Total> +188860269 0.035 37.93 2.682 0.373 erand48_r + 73623396 0.026 27.59 1.438 0.696 init_data + 76824434 0.018 18.97 2.182 0.458 drand48 +134442832 0.013 13.79 5.250 0.190 __drand48_iterate +@end verbatim +@end smallexample + +Although this thread hardly uses any CPU cycles, the overall IPC +of 2.55 is not all that bad. + +Last, we show the tables for threads 2 and 3: + +@smallexample +@verbatim +Exp Sel Total +=== === ===== + 1 2 3 +Functions sorted by metric: Exclusive CPU Cycles + +Excl. Excl. CPU Excl. Excl. Name +Instructions Cycles IPC CPI +Executed sec. % +3716362189 1.298 100.00 1.435 0.697 <Total> +3716362189 1.298 100.00 1.435 0.697 mxv_core + 0 0. 0. 0. 0. collector_root + 0 0. 0. 0. 0. driver_mxv + +Exp Sel Total +=== === ===== + 1 3 3 +Functions sorted by metric: Exclusive CPU Cycles + +Excl. Excl. CPU Excl. Excl. Name +Instructions Cycles IPC CPI +Executed sec. % +3716362189 1.300 100.00 1.433 0.698 <Total> +3716362189 1.300 100.00 1.433 0.698 mxv_core + 0 0. 0. 0. 0. collector_root + 0 0. 0. 0. 0. driver_mxv +@end verbatim +@end smallexample + +It is seen that both execute the same number of instructions and +take about the same number of CPU cycles. As a result, the IPC is +the same for both threads. + +@c -- A new node -------------------------------------------------------------- +@c TBD @node Additional Features +@c TBD @section Additional Features +@c ---------------------------------------------------------------------------- + +@c -- A new node -------------------------------------------------------------- +@c TBD @node More Filtering Capabilities +@c TBD @subsection More Filtering Capabilities +@c ---------------------------------------------------------------------------- + +@c TBD Cover @code{samples} and @code{seconds} + +@c -- A new node -------------------------------------------------------------- +@node Java Profiling +@section Java Profiling +@c ---------------------------------------------------------------------------- + +@IndexSubentry{Java profiling, @code{-j on/off}} +The @CollectApp{} command supports Java profiling. The @code{-j on} option +can be used for this, but since this feature is enabled by default, there is +no need to set this explicitly. Java profiling may be disabled through the +@code{-j off} option. + +The program is compiled as usual and the experiment directory is created +similar to what we have seen before. The only difference with a C/C++ +application is that the program has to be explicitly executed by java. + +For example, this is how to generate the experiment data for a Java +program that has the source code stored in file @code{Pi.java}: + +@cartouche +@smallexample +$ javac Pi.java +$ gprofng collect app -j on -O pi.demo.er java Pi < pi.in +@end smallexample +@end cartouche + +Regarding which java is selected to generate the data, @ToolName{} +first looks for the JDK in the path set in either the +@IndexSubentry{Java profiling, @code{JDK_HOME}} +@code{JDK_HOME} environment variable, or in the +@IndexSubentry{Java profiling, @code{JAVA_PATH}} +@code{JAVA_PATH} environment variable. If neither of these variables is +set, it checks for a JDK in the search path (set in the PATH +environment variable). If there is no JDK in this path, it checks for +the java executable in @code{/usr/java/bin/java}. + +In case additional options need to be passed on to the JVM, the +@IndexSubentry{Java profiling, @code{-J <string>}} +@code{-J <string>} option can be used. The string with the +option(s) has to be delimited by quotation marks in case +there is more than one argument. + +The @DisplayText{} command may be used to view the performance data. There is +no need for any special options and the same commands as previously discussed +are supported. + +@IndexSubentry{Commands, @code{viewmode}} +@IndexSubentry{Java profiling, different view modes} +The @code{viewmode} command +@xref{The Viewmode} +is very useful to examine the call stacks. + +For example, this is how one can see the native call stacks. For +lay-out purposes we have restricted the list to the first five entries: + +@cartouche +@smallexample +$ gprofng display text -limit 5 -viewmode machine -calltree pi.demo.er +@end smallexample +@end cartouche + +@smallexample +@verbatim +Print limit set to 5 +Viewmode set to machine +Functions Call Tree. Metric: Attributed Total CPU Time + +Attr. Name +Total +CPU sec. +1.381 +-<Total> +1.171 +-Pi.calculatePi(double) +0.110 +-collector_root +0.110 | +-JavaMain +0.070 | +-jni_CallStaticVoidMethod +@end verbatim +@end smallexample + +@noindent +Note that the selection of the viewmode is echoed in the output. + +@c -- A new node -------------------------------------------------------------- +@c TBD @node Summary of Options and Commands +@c TBD @chapter Summary of Options and Commands +@c ---------------------------------------------------------------------------- + +@c -- A new node -------------------------------------------------------------- +@node Terminology +@chapter Terminology + +Throughout this manual, certain terminology specific to profiling tools, +or @ToolName{}, or even to this document only, is used. In this chapter we +explain this terminology in detail. + +@menu +* The Program Counter:: What is a Program Counter? +* Inclusive and Exclusive Metrics:: An explanation of inclusive and exclusive metrics. +* Metric Definitions:: Definitions associated with metrics. +* The Viewmode:: Select the way call stacks are presented. +* The Selection List:: How to define a selection. +* Load Objects and Functions:: The components in an application. +* The Concept of a CPU in @ProductName{}:: The definition of a CPU. +* Hardware Event Counters Explained:: What are event counters? +* apath:: Our generic definition of a path. +@end menu + +@c ---------------------------------------------------------------------------- +@node The Program Counter +@section The Program Counter +@c ---------------------------------------------------------------------------- + +@cindex PC +@cindex Program Counter +The @emph{Program Counter}, or PC for short, keeps track where program execution is. +The address of the next instruction to be executed is stored in a special +purpose register in the processor, or core. + +@cindex Instruction pointer +The PC is sometimes also referred to as the @emph{instruction pointer}, but +we will use Program Counter or PC throughout this document. + +@c ---------------------------------------------------------------------------- +@node Inclusive and Exclusive Metrics +@section Inclusive and Exclusive Metrics +@c ---------------------------------------------------------------------------- + +In the remainder, these two concepts occur quite often and for lack of a better +place, they are explained here. + +@cindex Inclusive metric +The @emph{inclusive} value for a metric includes all values that are part of +the dynamic extent of the target function. For example if function @code{A} +calls functions @code{B} and @code{C}, the inclusive CPU time for @code{A} +includes the CPU time spent in @code{B} and @code{C}. + +@cindex Exclusive metric +In contrast with this, the @emph{exclusive} value for a metric is computed +by excluding the metric values used by other functions called. In our imaginary +example, the exclusive CPU time for function @code{A} is the time spent outside +calling functions @code{B} and @code{C}. + +@cindex Leaf function +In case of a @emph{leaf function}, the inclusive and exclusive values for the +metric are the same since by definition, it is not calling any other +function(s). + +Why do we use these two different values? The inclusive metric shows the most +expensive path, in terms of this metric, in the application. For example, if +the metric is cache misses, the function with the highest inclusive metric +tells you where most of the cache misses come from. + +Within this branch of the application, the exclusive metric points to the +functions that contribute and help to identify which part(s) to consider +for further analysis. + +@c ---------------------------------------------------------------------------- +@node Metric Definitions +@section Metric Definitions +@c ---------------------------------------------------------------------------- +The metrics to be shown are highly customizable. In this section we explain +the definitions associated with metrics. + +@IndexSubentry{Commands, @code{metrics}} +The @code{metrics} command takes a colon (:) separated list with special +keywords. This keyword consists of the following three fields: +@code{<flavor>}@code{<visibility>}@code{<metric_name>}. + +@cindex Flavor field +@cindex Visibility field +@cindex Metric name field +The @emph{<flavor>} field is either an @code{e} for ``exclusive'', or @code{i} +for ``inclusive''. The @code{<metric_name>} field is the name of the metric +request. The @emph{<visibility>} field consists of one ore more characters +from the following table: + +@table @code + +@item . +Show the metric as time. This applies to timing metrics and hardware event counters +that measure cycles. Interpret as @code{+} for other metrics. + +@item % +Show the metric as a percentage of the total value for this metric. + +@item + +Show the metric as an absolute value. For hardware event counters this is +the event count. Interpret as @code{.} for timing metrics. + +@item | +Do not show any metric value. Cannot be used with other visibility characters. + +@end table + +@c ---------------------------------------------------------------------------- +@node The Viewmode +@section The Viewmode + +@cindex Viewmode +@IndexSubentry{Commands, @code{viewmode}} + +There are different ways to view a call stack in Java. In @ToolName{}, this +is called the @emph{viewmode} and the setting is controlled through a command +with the same name. + +The @code{viewmode} command takes one of the following keywords: + +@table @code + +@item user +This is the default and shows the Java call stacks for Java threads. +No call stacks for any housekeeping threads are shown. The function +list contains a function +@IndexSubentry{Java profiling, @code{<JVM-System>}} +@code{<JVM-System>} that represents the aggregated time from non-Java +threads. +When the JVM software does not report a Java call stack, time is reported +against the function +@IndexSubentry{Java profiling, @code{<no Java callstack recorded>}} +@code{<no Java callstack recorded>}. + + +@item expert +Show the Java call stacks for Java threads when the Java code from the +user is executed and machine call stacks when JVM code is executed, or +when the JVM software does not report a Java call stack. +Show the machine call stacks for housekeeping threads. + +@item machine +Show the actual native call stacks for all threads. + +@end table + +@c ---------------------------------------------------------------------------- +@c ---------------------------------------------------------------------------- +@node The Selection List +@section The Selection List +@c ---------------------------------------------------------------------------- + +@cindex Selection list +@cindex List specification +Several commands allow the user to specify a subset of a list. For example, +to select specific threads from all the threads that have been used when +conducting the experiment(s). + +Such a selection list (or ``list'' in the remainder of this section) can be a +single number, a contiguous range of numbers with the start and end numbers +separated by a hyphen (@code{-}), a comma-separated list of numbers and +ranges, or the @code{all} keyword. Lists must not contain spaces. + +Each list can optionally be preceded by an experiment list with a similar +format, separated from the list by a colon (:). +If no experiment list is included, the list applies to all experiments. + +Multiple lists can be concatenated by separating the individual lists +by a plus sign. + +These are some examples of various filters using a list: + +@table @code + +@item thread_select 1 +Select thread 1 from all experiments. + +@item thread_select all:1 +Select thread 1 from all experiments. + +@item thread_select 1:1+2:2 +Select thread 1 from experiment 1 and thread 2 from experiment 2. + +@item cpu_select all:1,3,5 +Selects cores 1, 3, and 5 from all experiments. + +@item cpu_select 1,2:all +Select all cores from experiments 1 and 2, as listed by the @code{by exp_list} command. + +@end table + +@c ---------------------------------------------------------------------------- +@node Load Objects and Functions +@section Load Objects and Functions +@c ---------------------------------------------------------------------------- + +An application consists of various components. The source code files are +compiled into object files. These are then glued together at link time to form +the executable. +During execution, the program may also dynamically load objects. + +@cindex Load object +A @emph{load object} is defined to be an executable, or shared object. A shared +library is an example of a load object in @ToolName{}. + +Each load object, contains a text section with the instructions generated by the +compiler, a data section for data, and various symbol tables. +All load objects must contain an +@cindex ELF +ELF +symbol table, which gives the names and addresses of all the globally known +functions in that object. + +Load objects compiled with the -g option contain additional symbolic information +that can augment the ELF symbol table and provide information about functions that +are not global, additional information about object modules from which the functions +came, and line number information relating addresses to source lines. + +The term +@cindex Function +@emph{function} +is used to describe a set of instructions that represent a high-level operation +described in the source code. The term also covers methods as used in C++ and in +the Java programming language. + +In the @ToolName{} context, functions are provided in source code format. +Normally their names appear in the symbol table representing a set of addresses. +@cindex Program Counter +@cindex PC +If the Program Counter (PC) is within that set, the program is executing within that function. + +In principle, any address within the text segment of a load object can be mapped to a +function. Exactly the same mapping is used for the leaf PC and all the other PCs on the +call stack. + +Most of the functions correspond directly to the source model of the program, but +there are exceptions. This topic is however outside of the scope of this guide. + +@c ---------------------------------------------------------------------------- +@node The Concept of a CPU in @ProductName{} +@section The Concept of a CPU in @ProductName{} +@c ---------------------------------------------------------------------------- + +@cindex CPU +In @ProductName{}, there is the concept of a CPU. Admittedly, this is not the +best word to describe what is meant here and may be replaced in the future. + +The word CPU is used in many of the displays. +In the context of @ProductName{}, it is meant to denote a part of the +processor that is capable of executing instructions and with its own state, +like the program counter. + +For example, on a contemporary processor, a CPU could be a core. In case +hardware threads are supported within a core, it could be one of those +hardware threads. + +@c ---------------------------------------------------------------------------- +@node Hardware Event Counters Explained +@section Hardware Event Counters Explained +@c ---------------------------------------------------------------------------- + +@IndexSubentry{Hardware event counters, description} +For quite a number of years now, many microprocessors have supported hardware +event counters. + +On the hardware side, this means that in the processor there are one or more +registers dedicated to count certain activities, or ``events''. +Examples of such events are the number of instructions executed, or the number +of cache misses at level 2 in the memory hierarchy. + +While there is a limited set of such registers, the user can map events onto +them. In case more than one register is available, this allows for the +simultaenous measurement of various events. + +A simple, yet powerful, example is to simultaneously count the number of CPU +cycles and the number of instructions excuted. These two numbers can then be +used to compute the +@cindex IPC +@emph{IPC} value. IPC stands for ``Instructions Per Clockcycle'' and each processor +has a maximum. For example, if this maximum number is 2, it means the +processor is capable of executing two instructions every clock cycle. + +Whether this is actually achieved, depends on several factors, including the +instruction characteristics. +However, in case the IPC value is well below this maximum in a time critical +part of the application and this cannot be easily explained, further +investigation is probably warranted. + +@cindex CPI +A related metric is called @emph{CPI}, or ``Clockcycles Per Instruction''. +It is the inverse of the CPI and can be compared against the theoretical +value(s) of the target instruction(s). A significant difference may point +at a bottleneck. + +One thing to keep in mind is that the value returned by a counter can either +be the number of times the event occured, or a CPU cycle count. In case of +the latter it is possible to convert this number to time. + +@IndexSubentry{Hardware event counters, variable CPU frequency} +This is often easier to interpret than a simple count, but there is one +caveat to keep in mind. The CPU frequency may not have been constant while +the experimen was recorded and this impacts the time reported. + +These event counters, or ``counters'' for short, provide great insight into +what happens deep inside the processor. In case higher level information does +not provide the insight needed, the counters provide the information to get +to the bottom of a performance problem. + +There are some things to consider though. + +@itemize @bullet + +@item +The event definitions and names vary across processors and it may even happen +that some events change with an update. +Unfortunately and this is luckily rare, there are sometimes bugs causing the +wrong count to be returned. + +@IndexSubentry{Hardware event counters, alias name} +In @ToolName{}, some of the processor specific event names have an alias +name. For example @code{insts} measures the instructions executed. +These aliases not only makes it easier to identify the functionality, but also +provide portability of certain events across processors. + +@item +Another complexity is that there are typically many events one can monitor. +There may up to hundreds of events available and it could require several +experiments to zoom in on the root cause of a performance problem. + +@item +There may be restrictions regarding the mapping of event(s) onto the +counters. For example, certain events may be restricted to specific +counters only. As a result, one may have to conduct additional experiments +to cover all the events of interest. + +@item +The names of the events may also not be easy to interpret. In such cases, +the description can be found in the architecture manual for the processor. + +@end itemize + +Despite these drawbacks, hardware event counters are extremely useful and +may even turn out to be indispensable. + +@c ---------------------------------------------------------------------------- +@node apath +@section What is <apath>? +@c ---------------------------------------------------------------------------- + +In most cases, @ToolName{} shows the absolute pathnames of directories. These +tend to be rather long, causing display issues in this document. + +Instead of wrapping these long pathnames over multiple lines, we decided to +represent them by the @code{<apath>} symbol, which stands for ``an absolute +pathname''. + +Note that different occurrences of @code{<apath>} may represent different +absolute pathnames. + +@c -- A new node -------------------------------------------------------------- +@node Other Document Formats +@chapter Other Document Formats +@c ---------------------------------------------------------------------------- + +This document is written in Texinfo and the source text is made available as +part of the binutils distribution. The file name is @code{gprofng.texi} and +can be found in subdirectory @code{doc} under directory @code{gprofng} in the +top level directory. + +This file can be used to generate the document in the @code{info}, @code{html}, +and @code{pdf} formats. +The default installation procedure creates a file in the @code{info} format and +stores it in the documentation section of binutils. + +The probably easiest way to generate a different format from this Texinfo +document is to go to the distribution directory that was created when the +tools were built. +This is either the default distribution directory, or the one that has been set +with the @code{--prefix} option as part of the @code{configure} command. +In this example we symbolize this location with @code{<dist>}. + +The make file called @code{Makefile} in directory @code{<dist>/gprofng/doc} +supports several commands to generate this document in different formats. +We recommend to use these commands. + +They create the file(s) and install it in the documentation directory of binutils, +which is @code{<dist>/share/doc} in case @code{html} or @code{pdf} is selected and +@code{<dist>/share/info} for the file in the @code{info} format. + +To generate this document in the requested format and install it in the documentation +directory, the commands below should be executed. In this notation, @code{<format>} +is one of @code{info}, @code{html}, or @code{pdf}: + +@smallexample +@verbatim +$ cd <dist>/gprofng/doc +$ make install-<format> +@end verbatim +@end smallexample + +@noindent +Some things to note: + +@itemize + +@item +For the @code{pdf} file to be generated, the +@cindex TeX +TeX document formatting software is required and the relevant commmands need +to be included in the search path. An example of a popular TeX implementation +is @emph{TexLive}. It is beyond the scope of this document to go into the +details of installing and using TeX, but it is well documented elsewhere. + +@item +Instead of generating a single file in the @code{html} format, it is also +possible to create a directory with individual files for the various chapters. +To do so, remove the use of @code{--no-split} in variable @code{MAKEINFOHTML} +in the make file in the @code{doc} directory. + +@item +The make file also supports commands to only generate the file in the desired +format and not move them to the documentation directory. This is +accomplished through the @code{make <format>} command. + +@end itemize + +@ifnothtml +@node Index +@unnumbered Index +@printindex cp +@end ifnothtml + +@bye diff --git a/gprofng/doc/mdate-sh b/gprofng/doc/mdate-sh new file mode 100755 index 0000000..f80075c --- /dev/null +++ b/gprofng/doc/mdate-sh @@ -0,0 +1,224 @@ +#!/bin/sh +# Get modification time of a file or directory and pretty-print it. + +scriptversion=2016-01-11.22; # UTC + +# Copyright (C) 1995-2017 Free Software Foundation, Inc. +# written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, June 1995 +# +# 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 <http://www.gnu.org/licenses/>. + +# 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 <bug-automake@gnu.org> or send patches to +# <automake-patches@gnu.org>. + +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +fi + +case $1 in + '') + echo "$0: No file. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: mdate-sh [--help] [--version] FILE + +Pretty-print the modification day of FILE, in the format: +1 January 1970 + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v | --v*) + echo "mdate-sh $scriptversion" + exit $? + ;; +esac + +error () +{ + echo "$0: $1" >&2 + exit 1 +} + + +# Prevent date giving response in another language. +LANG=C +export LANG +LC_ALL=C +export LC_ALL +LC_TIME=C +export LC_TIME + +# GNU ls changes its time format in response to the TIME_STYLE +# variable. Since we cannot assume 'unset' works, revert this +# variable to its documented default. +if test "${TIME_STYLE+set}" = set; then + TIME_STYLE=posix-long-iso + export TIME_STYLE +fi + +save_arg1=$1 + +# Find out how to get the extended ls output of a file or directory. +if ls -L /dev/null 1>/dev/null 2>&1; then + ls_command='ls -L -l -d' +else + ls_command='ls -l -d' +fi +# Avoid user/group names that might have spaces, when possible. +if ls -n /dev/null 1>/dev/null 2>&1; then + ls_command="$ls_command -n" +fi + +# A 'ls -l' line looks as follows on OS/2. +# drwxrwx--- 0 Aug 11 2001 foo +# This differs from Unix, which adds ownership information. +# drwxrwx--- 2 root root 4096 Aug 11 2001 foo +# +# To find the date, we split the line on spaces and iterate on words +# until we find a month. This cannot work with files whose owner is a +# user named "Jan", or "Feb", etc. However, it's unlikely that '/' +# will be owned by a user whose name is a month. So we first look at +# the extended ls output of the root directory to decide how many +# words should be skipped to get the date. + +# On HPUX /bin/sh, "set" interprets "-rw-r--r--" as options, so the "x" below. +set x`$ls_command /` + +# Find which argument is the month. +month= +command= +until test $month +do + test $# -gt 0 || error "failed parsing '$ls_command /' output" + shift + # Add another shift to the command. + command="$command shift;" + case $1 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; + esac +done + +test -n "$month" || error "failed parsing '$ls_command /' output" + +# Get the extended ls output of the file or directory. +set dummy x`eval "$ls_command \"\\\$save_arg1\""` + +# Remove all preceding arguments +eval $command + +# Because of the dummy argument above, month is in $2. +# +# On a POSIX system, we should have +# +# $# = 5 +# $1 = file size +# $2 = month +# $3 = day +# $4 = year or time +# $5 = filename +# +# On Darwin 7.7.0 and 7.6.0, we have +# +# $# = 4 +# $1 = day +# $2 = month +# $3 = year or time +# $4 = filename + +# Get the month. +case $2 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; +esac + +case $3 in + ???*) day=$1;; + *) day=$3; shift;; +esac + +# Here we have to deal with the problem that the ls output gives either +# the time of day or the year. +case $3 in + *:*) set `date`; eval year=\$$# + case $2 in + Jan) nummonthtod=1;; + Feb) nummonthtod=2;; + Mar) nummonthtod=3;; + Apr) nummonthtod=4;; + May) nummonthtod=5;; + Jun) nummonthtod=6;; + Jul) nummonthtod=7;; + Aug) nummonthtod=8;; + Sep) nummonthtod=9;; + Oct) nummonthtod=10;; + Nov) nummonthtod=11;; + Dec) nummonthtod=12;; + esac + # For the first six month of the year the time notation can also + # be used for files modified in the last year. + if (expr $nummonth \> $nummonthtod) > /dev/null; + then + year=`expr $year - 1` + fi;; + *) year=$3;; +esac + +# The result. +echo $day $month $year + +# 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: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/gprofng/doc/texinfo.tex b/gprofng/doc/texinfo.tex new file mode 100644 index 0000000..2bab634 --- /dev/null +++ b/gprofng/doc/texinfo.tex @@ -0,0 +1,11731 @@ +% texinfo.tex -- TeX macros to handle Texinfo files. +% +% Load plain if necessary, i.e., if running under initex. +\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi +% +\def\texinfoversion{2021-02-20.11} +% +% Copyright 1985, 1986, 1988, 1990-2021 Free Software Foundation, Inc. +% +% This texinfo.tex 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 3 of the +% License, or (at your option) any later version. +% +% This texinfo.tex 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 +% 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 <https://www.gnu.org/licenses/>. +% +% As a special exception, when this file is read by TeX when processing +% a Texinfo source document, you may use the result without +% restriction. This Exception is an additional permission under section 7 +% of the GNU General Public License, version 3 ("GPLv3"). +% +% Please try the latest version of texinfo.tex before submitting bug +% reports; you can get the latest version from: +% https://ftp.gnu.org/gnu/texinfo/ (the Texinfo release area), or +% https://ftpmirror.gnu.org/texinfo/ (same, via a mirror), or +% https://www.gnu.org/software/texinfo/ (the Texinfo home page) +% The texinfo.tex in any given distribution could well be out +% of date, so if that's what you're using, please check. +% +% Send bug reports to bug-texinfo@gnu.org. Please include a +% complete document in each bug report with which we can reproduce the +% problem. Patches are, of course, greatly appreciated. +% +% To process a Texinfo manual with TeX, it's most reliable to use the +% texi2dvi shell script that comes with the distribution. For a simple +% manual foo.texi, however, you can get away with this: +% tex foo.texi +% texindex foo.?? +% tex foo.texi +% tex foo.texi +% dvips foo.dvi -o # or whatever; this makes foo.ps. +% The extra TeX runs get the cross-reference information correct. +% Sometimes one run after texindex suffices, and sometimes you need more +% than two; texi2dvi does it as many times as necessary. +% +% It is possible to adapt texinfo.tex for other languages, to some +% extent. You can get the existing language-specific files from the +% full Texinfo distribution. +% +% The GNU Texinfo home page is https://www.gnu.org/software/texinfo. + + +\message{Loading texinfo [version \texinfoversion]:} + +% If in a .fmt file, print the version number +% and turn on active characters that we couldn't do earlier because +% they might have appeared in the input file name. +\everyjob{\message{[Texinfo version \texinfoversion]}% + \catcode`+=\active \catcode`\_=\active} + +% LaTeX's \typeout. This ensures that the messages it is used for +% are identical in format to the corresponding ones from latex/pdflatex. +\def\typeout{\immediate\write17}% + +\chardef\other=12 + +% We never want plain's \outer definition of \+ in Texinfo. +% For @tex, we can use \tabalign. +\let\+ = \relax + +% Save some plain tex macros whose names we will redefine. +\let\ptexb=\b +\let\ptexbullet=\bullet +\let\ptexc=\c +\let\ptexcomma=\, +\let\ptexdot=\. +\let\ptexdots=\dots +\let\ptexend=\end +\let\ptexequiv=\equiv +\let\ptexexclam=\! +\let\ptexfootnote=\footnote +\let\ptexgtr=> +\let\ptexhat=^ +\let\ptexi=\i +\let\ptexindent=\indent +\let\ptexinsert=\insert +\let\ptexlbrace=\{ +\let\ptexless=< +\let\ptexnewwrite\newwrite +\let\ptexnoindent=\noindent +\let\ptexplus=+ +\let\ptexraggedright=\raggedright +\let\ptexrbrace=\} +\let\ptexslash=\/ +\let\ptexsp=\sp +\let\ptexstar=\* +\let\ptexsup=\sup +\let\ptext=\t +\let\ptextop=\top +{\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Pre-3.0. +\else + \def\linenumber{l.\the\inputlineno:\space} +\fi + +% Set up fixed words for English if not already set. +\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi +\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi +\ifx\putworderror\undefined \gdef\putworderror{error}\fi +\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi +\ifx\putwordin\undefined \gdef\putwordin{in}\fi +\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi +\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi +\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi +\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi +\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi +\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi +\ifx\putwordof\undefined \gdef\putwordof{of}\fi +\ifx\putwordon\undefined \gdef\putwordon{on}\fi +\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi +\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi +\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi +\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi +\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi +\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi +\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi +% +\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi +\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi +\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi +\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi +\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi +\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi +\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi +\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi +\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi +\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi +\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi +\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi +% +\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi +\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi +\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi +\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi +\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi + +% Give the space character the catcode for a space. +\def\spaceisspace{\catcode`\ =10\relax} + +% Likewise for ^^M, the end of line character. +\def\endlineisspace{\catcode13=10\relax} + +\chardef\dashChar = `\- +\chardef\slashChar = `\/ +\chardef\underChar = `\_ + +% Ignore a token. +% +\def\gobble#1{} + +% The following is used inside several \edef's. +\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} + +% Hyphenation fixes. +\hyphenation{ + Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script + ap-pen-dix bit-map bit-maps + data-base data-bases eshell fall-ing half-way long-est man-u-script + man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm + par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces + spell-ing spell-ings + stand-alone strong-est time-stamp time-stamps which-ever white-space + wide-spread wrap-around +} + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. We also make +% some effort to order the tracing commands to reduce output in the log +% file; cf. trace.sty in LaTeX. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{% + \tracingstats2 + \tracingpages1 + \tracinglostchars2 % 2 gives us more in etex + \tracingparagraphs1 + \tracingoutput1 + \tracingmacros2 + \tracingrestores1 + \showboxbreadth\maxdimen \showboxdepth\maxdimen + \ifx\eTeXversion\thisisundefined\else % etex gives us more logging + \tracingscantokens1 + \tracingifs1 + \tracinggroups1 + \tracingnesting2 + \tracingassigns1 + \fi + \tracingcommands3 % 3 gives us more in etex + \errorcontextlines16 +}% + +% @errormsg{MSG}. Do the index-like expansions on MSG, but if things +% aren't perfect, it's not the end of the world, being an error message, +% after all. +% +\def\errormsg{\begingroup \indexnofonts \doerrormsg} +\def\doerrormsg#1{\errmessage{#1}} + +% add check for \lastpenalty to plain's definitions. If the last thing +% we did was a \nobreak, we don't want to insert more space. +% +\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount + \removelastskip\penalty-50\smallskip\fi\fi} +\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount + \removelastskip\penalty-100\medskip\fi\fi} +\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount + \removelastskip\penalty-200\bigskip\fi\fi} + +% Output routine +% + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt } + +\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines +\newdimen\topandbottommargin \topandbottommargin=.75in + +% Output a mark which sets \thischapter, \thissection and \thiscolor. +% We dump everything together because we only have one kind of mark. +% This works because we only use \botmark / \topmark, not \firstmark. +% +% A mark contains a subexpression of the \ifcase ... \fi construct. +% \get*marks macros below extract the needed part using \ifcase. +% +% Another complication is to let the user choose whether \thischapter +% (\thissection) refers to the chapter (section) in effect at the top +% of a page, or that at the bottom of a page. + +% \domark is called twice inside \chapmacro, to add one +% mark before the section break, and one after. +% In the second call \prevchapterdefs is the same as \currentchapterdefs, +% and \prevsectiondefs is the same as \currentsectiondefs. +% Then if the page is not broken at the mark, some of the previous +% section appears on the page, and we can get the name of this section +% from \firstmark for @everyheadingmarks top. +% @everyheadingmarks bottom uses \botmark. +% +% See page 260 of The TeXbook. +\def\domark{% + \toks0=\expandafter{\currentchapterdefs}% + \toks2=\expandafter{\currentsectiondefs}% + \toks4=\expandafter{\prevchapterdefs}% + \toks6=\expandafter{\prevsectiondefs}% + \toks8=\expandafter{\currentcolordefs}% + \mark{% + \the\toks0 \the\toks2 % 0: marks for @everyheadingmarks top + \noexpand\or \the\toks4 \the\toks6 % 1: for @everyheadingmarks bottom + \noexpand\else \the\toks8 % 2: color marks + }% +} + +% \gettopheadingmarks, \getbottomheadingmarks, +% \getcolormarks - extract needed part of mark. +% +% \topmark doesn't work for the very first chapter (after the title +% page or the contents), so we use \firstmark there -- this gets us +% the mark with the chapter defs, unless the user sneaks in, e.g., +% @setcolor (or @url, or @link, etc.) between @contents and the very +% first @chapter. +\def\gettopheadingmarks{% + \ifcase0\the\savedtopmark\fi + \ifx\thischapter\empty \ifcase0\firstmark\fi \fi +} +\def\getbottomheadingmarks{\ifcase1\botmark\fi} +\def\getcolormarks{\ifcase2\the\savedtopmark\fi} + +% Avoid "undefined control sequence" errors. +\def\currentchapterdefs{} +\def\currentsectiondefs{} +\def\currentsection{} +\def\prevchapterdefs{} +\def\prevsectiondefs{} +\def\currentcolordefs{} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen\bindingoffset +\newdimen\normaloffset +\newdimen\txipagewidth \newdimen\txipageheight + +% Main output routine. +% +\chardef\PAGE = 255 +\newtoks\defaultoutput +\defaultoutput = {\savetopmark\onepageout{\pagecontents\PAGE}} +\output=\expandafter{\the\defaultoutput} + +\newbox\headlinebox +\newbox\footlinebox + +% When outputting the double column layout for indices, an output routine +% is run several times, which hides the original value of \topmark. This +% can lead to a page heading being output and duplicating the chapter heading +% of the index. Hence, save the contents of \topmark at the beginning of +% the output routine. The saved contents are valid until we actually +% \shipout a page. +% +% (We used to run a short output routine to actually set \topmark and +% \firstmark to the right values, but if this was called with an empty page +% containing whatsits for writing index entries, the whatsits would be thrown +% away and the index auxiliary file would remain empty.) +% +\newtoks\savedtopmark +\newif\iftopmarksaved +\topmarksavedtrue +\def\savetopmark{% + \iftopmarksaved\else + \global\savedtopmark=\expandafter{\topmark}% + \global\topmarksavedtrue + \fi +} + +% \onepageout takes a vbox as an argument. +% \shipout a vbox for a single page, adding an optional header, footer +% and footnote. This also causes index entries for this page to be written +% to the auxiliary files. +% +\def\onepageout#1{% + \hoffset=\normaloffset + % + \ifodd\pageno \advance\hoffset by \bindingoffset + \else \advance\hoffset by -\bindingoffset\fi + % + \checkchapterpage + % + % Retrieve the information for the headings from the marks in the page, + % and call Plain TeX's \makeheadline and \makefootline, which use the + % values in \headline and \footline. + % + % Common context changes for both heading and footing. + % Do this outside of the \shipout so @code etc. will be expanded in + % the headline as they should be, not taken literally (outputting ''code). + \def\commonheadfootline{\let\hsize=\txipagewidth \texinfochars} + % + \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi + \global\setbox\headlinebox = \vbox{\commonheadfootline \makeheadline}% + \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi + \global\setbox\footlinebox = \vbox{\commonheadfootline \makefootline}% + % + {% + % Set context for writing to auxiliary files like index files. + % Have to do this stuff outside the \shipout because we want it to + % take effect in \write's, yet the group defined by the \vbox ends + % before the \shipout runs. + % + \atdummies % don't expand commands in the output. + \turnoffactive + \shipout\vbox{% + % Do this early so pdf references go to the beginning of the page. + \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi + % + \unvbox\headlinebox + \pagebody{#1}% + \ifdim\ht\footlinebox > 0pt + % Only leave this space if the footline is nonempty. + % (We lessened \vsize for it in \oddfootingyyy.) + % The \baselineskip=24pt in plain's \makefootline has no effect. + \vskip 24pt + \unvbox\footlinebox + \fi + % + }% + }% + \global\topmarksavedfalse + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +\newinsert\margin \dimen\margin=\maxdimen + +% Main part of page, including any footnotes +\def\pagebody#1{\vbox to\txipageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1\relax \unvbox#1\relax +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% Check if we are on the first page of a chapter. Used for printing headings. +\newif\ifchapterpage +\def\checkchapterpage{% + % Get the chapter that was current at the end of the last page + \ifcase1\the\savedtopmark\fi + \let\prevchaptername\thischaptername + % + \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi + \let\curchaptername\thischaptername + % + \ifx\curchaptername\prevchaptername + \chapterpagefalse + \else + \chapterpagetrue + \fi +} + +% Argument parsing + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% For example, \def\foo{\parsearg\fooxxx}. +% +\def\parsearg{\parseargusing{}} +\def\parseargusing#1#2{% + \def\argtorun{#2}% + \begingroup + \obeylines + \spaceisspace + #1% + \parseargline\empty% Insert the \empty token, see \finishparsearg below. +} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + \argremovecomment #1\comment\ArgTerm% + }% +} + +% First remove any @comment, then any @c comment. Pass the result on to +% \argcheckspaces. +\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} +\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} + +% Each occurrence of `\^^M' or `<space>\^^M' is replaced by a single space. +% +% \argremovec might leave us with trailing space, e.g., +% @end itemize @c foo +% This space token undergoes the same procedure and is eventually removed +% by \finishparsearg. +% +\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} +\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} +\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% + \def\temp{#3}% + \ifx\temp\empty + % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: + \let\temp\finishparsearg + \else + \let\temp\argcheckspaces + \fi + % Put the space token in: + \temp#1 #3\ArgTerm +} + +% If a _delimited_ argument is enclosed in braces, they get stripped; so +% to get _exactly_ the rest of the line, we had to prevent such situation. +% We prepended an \empty token at the very beginning and we expand it now, +% just before passing the control to \argtorun. +% (Similarly, we have to think about #3 of \argcheckspacesY above: it is +% either the null string, or it ends with \^^M---thus there is no danger +% that a pair of braces would be stripped. +% +% But first, we have to remove the trailing space token. +% +\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} + + +% \parseargdef - define a command taking an argument on the line +% +% \parseargdef\foo{...} +% is roughly equivalent to +% \def\foo{\parsearg\Xfoo} +% \def\Xfoo#1{...} +\def\parseargdef#1{% + \expandafter \doparseargdef \csname\string#1\endcsname #1% +} +\def\doparseargdef#1#2{% + \def#2{\parsearg#1}% + \def#1##1% +} + +% Several utility definitions with active space: +{ + \obeyspaces + \gdef\obeyedspace{ } + + % Make each space character in the input produce a normal interword + % space in the output. Don't allow a line break at this space, as this + % is used only in environments like @example, where each line of input + % should produce a line of output anyway. + % + \gdef\sepspaces{\obeyspaces\let =\tie} + + % If an index command is used in an @example environment, any spaces + % therein should become regular spaces in the raw index file, not the + % expansion of \tie (\leavevmode \penalty \@M \ ). + \gdef\unsepspaces{\let =\space} +} + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +% Define the framework for environments in texinfo.tex. It's used like this: +% +% \envdef\foo{...} +% \def\Efoo{...} +% +% It's the responsibility of \envdef to insert \begingroup before the +% actual body; @end closes the group after calling \Efoo. \envdef also +% defines \thisenv, so the current environment is known; @end checks +% whether the environment name matches. The \checkenv macro can also be +% used to check whether the current environment is the one expected. +% +% Non-false conditionals (@iftex, @ifset) don't fit into this, so they +% are not treated as environments; they don't open a group. (The +% implementation of @end takes care not to call \endgroup in this +% special case.) + + +% At run-time, environments start with this: +\def\startenvironment#1{\begingroup\def\thisenv{#1}} +% initialize +\let\thisenv\empty + +% ... but they get defined via ``\envdef\foo{...}'': +\long\def\envdef#1#2{\def#1{\startenvironment#1#2}} +\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} + +% Check whether we're in the right environment: +\def\checkenv#1{% + \def\temp{#1}% + \ifx\thisenv\temp + \else + \badenverr + \fi +} + +% Environment mismatch, #1 expected: +\def\badenverr{% + \errhelp = \EMsimple + \errmessage{This command can appear only \inenvironment\temp, + not \inenvironment\thisenv}% +} +\def\inenvironment#1{% + \ifx#1\empty + outside of any environment% + \else + in environment \expandafter\string#1% + \fi +} + + +% @end foo calls \checkenv and executes the definition of \Efoo. +\parseargdef\end{ + \if 1\csname iscond.#1\endcsname + \else + % The general wording of \badenverr may not be ideal. + \expandafter\checkenv\csname#1\endcsname + \csname E#1\endcsname + \endgroup + \fi +} + +\newhelp\EMsimple{Press RETURN to continue.} + + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\unskip\hfil\break\hbox{}\ignorespaces} + +% @/ allows a line break. +\let\/=\allowbreak + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=\endofsentencespacefactor\space} + +% @! is an end-of-sentence bang. +\def\!{!\spacefactor=\endofsentencespacefactor\space} + +% @? is an end-of-sentence query. +\def\?{?\spacefactor=\endofsentencespacefactor\space} + +% @frenchspacing on|off says whether to put extra space after punctuation. +% +\def\onword{on} +\def\offword{off} +% +\parseargdef\frenchspacing{% + \def\temp{#1}% + \ifx\temp\onword \plainfrenchspacing + \else\ifx\temp\offword \plainnonfrenchspacing + \else + \errhelp = \EMsimple + \errmessage{Unknown @frenchspacing option `\temp', must be on|off}% + \fi\fi +} + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +% Another complication is that the group might be very large. This can +% cause the glue on the previous page to be unduly stretched, because it +% does not have much material. In this case, it's better to add an +% explicit \vfill so that the extra space is at the bottom. The +% threshold for doing this is if the group is more than \vfilllimit +% percent of a page (\vfilllimit can be changed inside of @tex). +% +\newbox\groupbox +\def\vfilllimit{0.7} +% +\envdef\group{% + \ifnum\catcode`\^^M=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + \startsavinginserts + % + \setbox\groupbox = \vtop\bgroup + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% The \vtop produces a box with normal height and large depth; thus, TeX puts +% \baselineskip glue before it, and (when the next line of text is done) +% \lineskip glue after it. Thus, space below is not quite equal to space +% above. But it's pretty close. +\def\Egroup{% + % To get correct interline space between the last line of the group + % and the first line afterwards, we have to propagate \prevdepth. + \endgraf % Not \par, as it may have been set to \lisppar. + \global\dimen1 = \prevdepth + \egroup % End the \vtop. + \addgroupbox + \prevdepth = \dimen1 + \checkinserts +} + +\def\addgroupbox{ + % \dimen0 is the vertical size of the group's box. + \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox + % \dimen2 is how much space is left on the page (more or less). + \dimen2 = \txipageheight \advance\dimen2 by -\pagetotal + % if the group doesn't fit on the current page, and it's a big big + % group, force a page break. + \ifdim \dimen0 > \dimen2 + \ifdim \pagetotal < \vfilllimit\txipageheight + \page + \fi + \fi + \box\groupbox +} + +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +\parseargdef\need{% + % Ensure vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % If the @need value is less than one line space, it's useless. + \dimen0 = #1\mil + \dimen2 = \ht\strutbox + \advance\dimen2 by \dp\strutbox + \ifdim\dimen0 > \dimen2 + % + % Do a \strut just to make the height of this box be normal, so the + % normal leading is inserted relative to the preceding line. + % And a page break here is fine. + \vtop to #1\mil{\strut\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak + \fi +} + +% @br forces paragraph break (and is undocumented). + +\let\br = \par + +% @page forces the start of a new page. +% +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} + +% This defn is used inside nofill environments such as @example. +\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount + \leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current +% paragraph. For more general purposes, use the \margin insertion +% class. WHICH is `l' or `r'. Not documented, written for gawk manual. +% +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} +% +\def\doinmargin#1#2{\strut\vadjust{% + \nobreak + \kern-\strutdepth + \vtop to \strutdepth{% + \baselineskip=\strutdepth + \vss + % if you have multiple lines of stuff to put here, you'll need to + % make the vbox yourself of the appropriate size. + \ifx#1l% + \llap{\ignorespaces #2\hskip\inmarginspacing}% + \else + \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% + \fi + \null + }% +}} +\def\inleftmargin{\doinmargin l} +\def\inrightmargin{\doinmargin r} +% +% @inmargin{TEXT [, RIGHT-TEXT]} +% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; +% else use TEXT for both). +% +\def\inmargin#1{\parseinmargin #1,,\finish} +\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \def\lefttext{#1}% have both texts + \def\righttext{#2}% + \else + \def\lefttext{#1}% have only one text + \def\righttext{#1}% + \fi + % + \ifodd\pageno + \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin + \else + \def\temp{\inleftmargin\lefttext}% + \fi + \temp +} + +% @include FILE -- \input text of FILE. +% +\def\include{\parseargusing\filenamecatcodes\includezzz} +\def\includezzz#1{% + \pushthisfilestack + \def\thisfile{#1}% + {% + \makevalueexpandable % we want to expand any @value in FILE. + \turnoffactive % and allow special characters in the expansion + \indexnofonts % Allow `@@' and other weird things in file names. + \wlog{texinfo.tex: doing @include of #1^^J}% + \edef\temp{\noexpand\input #1 }% + % + % This trickery is to read FILE outside of a group, in case it makes + % definitions, etc. + \expandafter + }\temp + \popthisfilestack +} +\def\filenamecatcodes{% + \catcode`\\=\other + \catcode`~=\other + \catcode`^=\other + \catcode`_=\other + \catcode`|=\other + \catcode`<=\other + \catcode`>=\other + \catcode`+=\other + \catcode`-=\other + \catcode`\`=\other + \catcode`\'=\other +} + +\def\pushthisfilestack{% + \expandafter\pushthisfilestackX\popthisfilestack\StackTerm +} +\def\pushthisfilestackX{% + \expandafter\pushthisfilestackY\thisfile\StackTerm +} +\def\pushthisfilestackY #1\StackTerm #2\StackTerm {% + \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% +} + +\def\popthisfilestack{\errthisfilestackempty} +\def\errthisfilestackempty{\errmessage{Internal error: + the stack of filenames is empty.}} +% +\def\thisfile{} + +% @center line +% outputs that line, centered. +% +\parseargdef\center{% + \ifhmode + \let\centersub\centerH + \else + \let\centersub\centerV + \fi + \centersub{\hfil \ignorespaces#1\unskip \hfil}% + \let\centersub\relax % don't let the definition persist, just in case +} +\def\centerH#1{{% + \hfil\break + \advance\hsize by -\leftskip + \advance\hsize by -\rightskip + \line{#1}% + \break +}} +% +\newcount\centerpenalty +\def\centerV#1{% + % The idea here is the same as in \startdefun, \cartouche, etc.: if + % @center is the first thing after a section heading, we need to wipe + % out the negative parskip inserted by \sectionheading, but still + % prevent a page break here. + \centerpenalty = \lastpenalty + \ifnum\centerpenalty>10000 \vskip\parskip \fi + \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi + \line{\kern\leftskip #1\kern\rightskip}% +} + +% @sp n outputs n lines of vertical space +% +\parseargdef\sp{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment + + +\def\c{\begingroup \catcode`\^^M=\active% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% +\cxxx} +{\catcode`\^^M=\active \gdef\cxxx#1^^M{\endgroup}} +% +\let\comment\c + +% @paragraphindent NCHARS +% We'll use ems for NCHARS, close enough. +% NCHARS can also be the word `asis' or `none'. +% We cannot feasibly implement @paragraphindent asis, though. +% +\def\asisword{asis} % no translation, these are keywords +\def\noneword{none} +% +\parseargdef\paragraphindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \defaultparindent = 0pt + \else + \defaultparindent = #1em + \fi + \fi + \parindent = \defaultparindent +} + +% @exampleindent NCHARS +% We'll use ems for NCHARS like @paragraphindent. +% It seems @exampleindent asis isn't necessary, but +% I preserve it to make it similar to @paragraphindent. +\parseargdef\exampleindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \lispnarrowing = 0pt + \else + \lispnarrowing = #1em + \fi + \fi +} + +% @firstparagraphindent WORD +% If WORD is `none', then suppress indentation of the first paragraph +% after a section heading. If WORD is `insert', then do indent at such +% paragraphs. +% +% The paragraph indentation is suppressed or not by calling +% \suppressfirstparagraphindent, which the sectioning commands do. +% We switch the definition of this back and forth according to WORD. +% By default, we suppress indentation. +% +\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} +\def\insertword{insert} +% +\parseargdef\firstparagraphindent{% + \def\temp{#1}% + \ifx\temp\noneword + \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent + \else\ifx\temp\insertword + \let\suppressfirstparagraphindent = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @firstparagraphindent option `\temp'}% + \fi\fi +} + +% Here is how we actually suppress indentation. Redefine \everypar to +% \kern backwards by \parindent, and then reset itself to empty. +% +% We also make \indent itself not actually do anything until the next +% paragraph. +% +\gdef\dosuppressfirstparagraphindent{% + \gdef\indent {\restorefirstparagraphindent \indent}% + \gdef\noindent{\restorefirstparagraphindent \noindent}% + \global\everypar = {\kern -\parindent \restorefirstparagraphindent}% +} +% +\gdef\restorefirstparagraphindent{% + \global\let\indent = \ptexindent + \global\let\noindent = \ptexnoindent + \global\everypar = {}% +} + + +% @refill is a no-op. +\let\refill=\relax + +% @setfilename INFO-FILENAME - ignored +\let\setfilename=\comment + +% @bye. +\outer\def\bye{\chappager\pagelabels\tracingstats=1\ptexend} + + +\message{pdf,} +% adobe `portable' document format +\newcount\tempnum +\newcount\lnkcount +\newtoks\filename +\newcount\filenamelength +\newcount\pgn +\newtoks\toksA +\newtoks\toksB +\newtoks\toksC +\newtoks\toksD +\newbox\boxA +\newbox\boxB +\newcount\countA +\newif\ifpdf +\newif\ifpdfmakepagedest + +% +% For LuaTeX +% + +\newif\iftxiuseunicodedestname +\txiuseunicodedestnamefalse % For pdfTeX etc. + +\ifx\luatexversion\thisisundefined +\else + % Use Unicode destination names + \txiuseunicodedestnametrue + % Escape PDF strings with converting UTF-16 from UTF-8 + \begingroup + \catcode`\%=12 + \directlua{ + function UTF16oct(str) + tex.sprint(string.char(0x5c) .. '376' .. string.char(0x5c) .. '377') + for c in string.utfvalues(str) do + if c < 0x10000 then + tex.sprint( + string.format(string.char(0x5c) .. string.char(0x25) .. '03o' .. + string.char(0x5c) .. string.char(0x25) .. '03o', + math.floor(c / 256), math.floor(c % 256))) + else + c = c - 0x10000 + local c_hi = c / 1024 + 0xd800 + local c_lo = c % 1024 + 0xdc00 + tex.sprint( + string.format(string.char(0x5c) .. string.char(0x25) .. '03o' .. + string.char(0x5c) .. string.char(0x25) .. '03o' .. + string.char(0x5c) .. string.char(0x25) .. '03o' .. + string.char(0x5c) .. string.char(0x25) .. '03o', + math.floor(c_hi / 256), math.floor(c_hi % 256), + math.floor(c_lo / 256), math.floor(c_lo % 256))) + end + end + end + } + \endgroup + \def\pdfescapestrutfsixteen#1{\directlua{UTF16oct('\luaescapestring{#1}')}} + % Escape PDF strings without converting + \begingroup + \directlua{ + function PDFescstr(str) + for c in string.bytes(str) do + if c <= 0x20 or c >= 0x80 or c == 0x28 or c == 0x29 or c == 0x5c then + tex.sprint(-2, + string.format(string.char(0x5c) .. string.char(0x25) .. '03o', + c)) + else + tex.sprint(-2, string.char(c)) + end + end + end + } + % The -2 in the arguments here gives all the input to TeX catcode 12 + % (other) or 10 (space), preventing undefined control sequence errors. See + % https://lists.gnu.org/archive/html/bug-texinfo/2019-08/msg00031.html + % + \endgroup + \def\pdfescapestring#1{\directlua{PDFescstr('\luaescapestring{#1}')}} + \ifnum\luatexversion>84 + % For LuaTeX >= 0.85 + \def\pdfdest{\pdfextension dest} + \let\pdfoutput\outputmode + \def\pdfliteral{\pdfextension literal} + \def\pdfcatalog{\pdfextension catalog} + \def\pdftexversion{\numexpr\pdffeedback version\relax} + \let\pdfximage\saveimageresource + \let\pdfrefximage\useimageresource + \let\pdflastximage\lastsavedimageresourceindex + \def\pdfendlink{\pdfextension endlink\relax} + \def\pdfoutline{\pdfextension outline} + \def\pdfstartlink{\pdfextension startlink} + \def\pdffontattr{\pdfextension fontattr} + \def\pdfobj{\pdfextension obj} + \def\pdflastobj{\numexpr\pdffeedback lastobj\relax} + \let\pdfpagewidth\pagewidth + \let\pdfpageheight\pageheight + \edef\pdfhorigin{\pdfvariable horigin} + \edef\pdfvorigin{\pdfvariable vorigin} + \fi +\fi + +% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 +% can be set). So we test for \relax and 0 as well as being undefined. +\ifx\pdfoutput\thisisundefined +\else + \ifx\pdfoutput\relax + \else + \ifcase\pdfoutput + \else + \pdftrue + \fi + \fi +\fi + +\newif\ifpdforxetex +\pdforxetexfalse +\ifpdf + \pdforxetextrue +\fi +\ifx\XeTeXrevision\thisisundefined\else + \pdforxetextrue +\fi + + +% Output page labels information. +% See PDF reference v.1.7 p.594, section 8.3.1. +\ifpdf +\def\pagelabels{% + \def\title{0 << /P (T-) /S /D >>}% + \edef\roman{\the\romancount << /S /r >>}% + \edef\arabic{\the\arabiccount << /S /D >>}% + % + % Page label ranges must be increasing. Remove any duplicates. + % (There is a slight chance of this being wrong if e.g. there is + % a @contents but no @titlepage, etc.) + % + \ifnum\romancount=0 \def\roman{}\fi + \ifnum\arabiccount=0 \def\title{}% + \else + \ifnum\romancount=\arabiccount \def\roman{}\fi + \fi + % + \ifnum\romancount<\arabiccount + \pdfcatalog{/PageLabels << /Nums [\title \roman \arabic ] >> }\relax + \else + \pdfcatalog{/PageLabels << /Nums [\title \arabic \roman ] >> }\relax + \fi +} +\else + \let\pagelabels\relax +\fi + +\newcount\pagecount \pagecount=0 +\newcount\romancount \romancount=0 +\newcount\arabiccount \arabiccount=0 +\ifpdf + \let\ptxadvancepageno\advancepageno + \def\advancepageno{% + \ptxadvancepageno\global\advance\pagecount by 1 + } +\fi + + +% PDF uses PostScript string constants for the names of xref targets, +% for display in the outlines, and in other places. Thus, we have to +% double any backslashes. Otherwise, a name like "\node" will be +% interpreted as a newline (\n), followed by o, d, e. Not good. +% +% See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and +% related messages. The final outcome is that it is up to the TeX user +% to double the backslashes and otherwise make the string valid, so +% that's what we do. pdftex 1.30.0 (ca.2005) introduced a primitive to +% do this reliably, so we use it. + +% #1 is a control sequence in which to do the replacements, +% which we \xdef. +\def\txiescapepdf#1{% + \ifx\pdfescapestring\thisisundefined + % No primitive available; should we give a warning or log? + % Many times it won't matter. + \xdef#1{#1}% + \else + % The expandable \pdfescapestring primitive escapes parentheses, + % backslashes, and other special chars. + \xdef#1{\pdfescapestring{#1}}% + \fi +} +\def\txiescapepdfutfsixteen#1{% + \ifx\pdfescapestrutfsixteen\thisisundefined + % No UTF-16 converting macro available. + \txiescapepdf{#1}% + \else + \xdef#1{\pdfescapestrutfsixteen{#1}}% + \fi +} + +\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images +with PDF output, and none of those formats could be found. (.eps cannot +be supported due to the design of the PDF format; use regular TeX (DVI +output) for that.)} + +\ifpdf + % + % Color manipulation macros using ideas from pdfcolor.tex, + % except using rgb instead of cmyk; the latter is said to render as a + % very dark gray on-screen and a very dark halftone in print, instead + % of actual black. The dark red here is dark enough to print on paper as + % nearly black, but still distinguishable for online viewing. We use + % black by default, though. + \def\rgbDarkRed{0.50 0.09 0.12} + \def\rgbBlack{0 0 0} + % + % rg sets the color for filling (usual text, etc.); + % RG sets the color for stroking (thin rules, e.g., normal _'s). + \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}} + % + % Set color, and create a mark which defines \thiscolor accordingly, + % so that \makeheadline knows which color to restore. + \def\setcolor#1{% + \xdef\currentcolordefs{\gdef\noexpand\thiscolor{#1}}% + \domark + \pdfsetcolor{#1}% + } + % + \def\maincolor{\rgbBlack} + \pdfsetcolor{\maincolor} + \edef\thiscolor{\maincolor} + \def\currentcolordefs{} + % + \def\makefootline{% + \baselineskip24pt + \line{\pdfsetcolor{\maincolor}\the\footline}% + } + % + \def\makeheadline{% + \vbox to 0pt{% + \vskip-22.5pt + \line{% + \vbox to8.5pt{}% + % Extract \thiscolor definition from the marks. + \getcolormarks + % Typeset the headline with \maincolor, then restore the color. + \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% + }% + \vss + }% + \nointerlineskip + } + % + % + \pdfcatalog{/PageMode /UseOutlines} + % + % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). + \def\dopdfimage#1#2#3{% + \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% + \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% + % + % pdftex (and the PDF format) support .pdf, .png, .jpg (among + % others). Let's try in that order, PDF first since if + % someone has a scalable image, presumably better to use that than a + % bitmap. + \let\pdfimgext=\empty + \begingroup + \openin 1 #1.pdf \ifeof 1 + \openin 1 #1.PDF \ifeof 1 + \openin 1 #1.png \ifeof 1 + \openin 1 #1.jpg \ifeof 1 + \openin 1 #1.jpeg \ifeof 1 + \openin 1 #1.JPG \ifeof 1 + \errhelp = \nopdfimagehelp + \errmessage{Could not find image file #1 for pdf}% + \else \gdef\pdfimgext{JPG}% + \fi + \else \gdef\pdfimgext{jpeg}% + \fi + \else \gdef\pdfimgext{jpg}% + \fi + \else \gdef\pdfimgext{png}% + \fi + \else \gdef\pdfimgext{PDF}% + \fi + \else \gdef\pdfimgext{pdf}% + \fi + \closein 1 + \endgroup + % + % without \immediate, ancient pdftex seg faults when the same image is + % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) + \ifnum\pdftexversion < 14 + \immediate\pdfimage + \else + \immediate\pdfximage + \fi + \ifdim \wd0 >0pt width \pdfimagewidth \fi + \ifdim \wd2 >0pt height \pdfimageheight \fi + \ifnum\pdftexversion<13 + #1.\pdfimgext + \else + {#1.\pdfimgext}% + \fi + \ifnum\pdftexversion < 14 \else + \pdfrefximage \pdflastximage + \fi} + % + \def\setpdfdestname#1{{% + % We have to set dummies so commands such as @code, and characters + % such as \, aren't expanded when present in a section title. + \indexnofonts + \makevalueexpandable + \turnoffactive + \iftxiuseunicodedestname + \ifx \declaredencoding \latone + % Pass through Latin-1 characters. + % LuaTeX with byte wise I/O converts Latin-1 characters to Unicode. + \else + \ifx \declaredencoding \utfeight + % Pass through Unicode characters. + \else + % Use ASCII approximations in destination names. + \passthroughcharsfalse + \fi + \fi + \else + % Use ASCII approximations in destination names. + \passthroughcharsfalse + \fi + \def\pdfdestname{#1}% + \txiescapepdf\pdfdestname + }} + % + \def\setpdfoutlinetext#1{{% + \indexnofonts + \makevalueexpandable + \turnoffactive + \ifx \declaredencoding \latone + % The PDF format can use an extended form of Latin-1 in bookmark + % strings. See Appendix D of the PDF Reference, Sixth Edition, for + % the "PDFDocEncoding". + \passthroughcharstrue + % Pass through Latin-1 characters. + % LuaTeX: Convert to Unicode + % pdfTeX: Use Latin-1 as PDFDocEncoding + \def\pdfoutlinetext{#1}% + \else + \ifx \declaredencoding \utfeight + \ifx\luatexversion\thisisundefined + % For pdfTeX with UTF-8. + % TODO: the PDF format can use UTF-16 in bookmark strings, + % but the code for this isn't done yet. + % Use ASCII approximations. + \passthroughcharsfalse + \def\pdfoutlinetext{#1}% + \else + % For LuaTeX with UTF-8. + % Pass through Unicode characters for title texts. + \passthroughcharstrue + \def\pdfoutlinetext{#1}% + \fi + \else + % For non-Latin-1 or non-UTF-8 encodings. + % Use ASCII approximations. + \passthroughcharsfalse + \def\pdfoutlinetext{#1}% + \fi + \fi + % LuaTeX: Convert to UTF-16 + % pdfTeX: Use Latin-1 as PDFDocEncoding + \txiescapepdfutfsixteen\pdfoutlinetext + }} + % + \def\pdfmkdest#1{% + \setpdfdestname{#1}% + \safewhatsit{\pdfdest name{\pdfdestname} xyz}% + } + % + % used to mark target names; must be expandable. + \def\pdfmkpgn#1{#1} + % + % by default, use black for everything. + \def\urlcolor{\rgbBlack} + \def\linkcolor{\rgbBlack} + \def\endlink{\setcolor{\maincolor}\pdfendlink} + % + % Adding outlines to PDF; macros for calculating structure of outlines + % come from Petr Olsak + \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% + \else \csname#1\endcsname \fi} + \def\advancenumber#1{\tempnum=\expnumber{#1}\relax + \advance\tempnum by 1 + \expandafter\xdef\csname#1\endcsname{\the\tempnum}} + % + % #1 is the section text, which is what will be displayed in the + % outline by the pdf viewer. #2 is the pdf expression for the number + % of subentries (or empty, for subsubsections). #3 is the node text, + % which might be empty if this toc entry had no corresponding node. + % #4 is the page number + % + \def\dopdfoutline#1#2#3#4{% + % Generate a link to the node text if that exists; else, use the + % page number. We could generate a destination for the section + % text in the case where a section has no node, but it doesn't + % seem worth the trouble, since most documents are normally structured. + \setpdfoutlinetext{#1} + \setpdfdestname{#3} + \ifx\pdfdestname\empty + \def\pdfdestname{#4}% + \fi + % + \pdfoutline goto name{\pdfmkpgn{\pdfdestname}}#2{\pdfoutlinetext}% + } + % + \def\pdfmakeoutlines{% + \begingroup + % Read toc silently, to get counts of subentries for \pdfoutline. + \def\partentry##1##2##3##4{}% ignore parts in the outlines + \def\numchapentry##1##2##3##4{% + \def\thischapnum{##2}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + }% + \def\numsecentry##1##2##3##4{% + \advancenumber{chap\thischapnum}% + \def\thissecnum{##2}% + \def\thissubsecnum{0}% + }% + \def\numsubsecentry##1##2##3##4{% + \advancenumber{sec\thissecnum}% + \def\thissubsecnum{##2}% + }% + \def\numsubsubsecentry##1##2##3##4{% + \advancenumber{subsec\thissubsecnum}% + }% + \def\thischapnum{0}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + % + % use \def rather than \let here because we redefine \chapentry et + % al. a second time, below. + \def\appentry{\numchapentry}% + \def\appsecentry{\numsecentry}% + \def\appsubsecentry{\numsubsecentry}% + \def\appsubsubsecentry{\numsubsubsecentry}% + \def\unnchapentry{\numchapentry}% + \def\unnsecentry{\numsecentry}% + \def\unnsubsecentry{\numsubsecentry}% + \def\unnsubsubsecentry{\numsubsubsecentry}% + \readdatafile{toc}% + % + % Read toc second time, this time actually producing the outlines. + % The `-' means take the \expnumber as the absolute number of + % subentries, which we calculated on our first read of the .toc above. + % + % We use the node names as the destinations. + % + % Currently we prefix the section name with the section number + % for chapter and appendix headings only in order to avoid too much + % horizontal space being required in the PDF viewer. + \def\numchapentry##1##2##3##4{% + \dopdfoutline{##2 ##1}{count-\expnumber{chap##2}}{##3}{##4}}% + \def\unnchapentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% + \def\numsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% + \def\numsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% + \def\numsubsubsecentry##1##2##3##4{% count is always zero + \dopdfoutline{##1}{}{##3}{##4}}% + % + % PDF outlines are displayed using system fonts, instead of + % document fonts. Therefore we cannot use special characters, + % since the encoding is unknown. For example, the eogonek from + % Latin 2 (0xea) gets translated to a | character. Info from + % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. + % + % TODO this right, we have to translate 8-bit characters to + % their "best" equivalent, based on the @documentencoding. Too + % much work for too little return. Just use the ASCII equivalents + % we use for the index sort strings. + % + \indexnofonts + \setupdatafile + % We can have normal brace characters in the PDF outlines, unlike + % Texinfo index files. So set that up. + \def\{{\lbracecharliteral}% + \def\}{\rbracecharliteral}% + \catcode`\\=\active \otherbackslash + \input \tocreadfilename + \endgroup + } + {\catcode`[=1 \catcode`]=2 + \catcode`{=\other \catcode`}=\other + \gdef\lbracecharliteral[{]% + \gdef\rbracecharliteral[}]% + ] + % + \def\skipspaces#1{\def\PP{#1}\def\D{|}% + \ifx\PP\D\let\nextsp\relax + \else\let\nextsp\skipspaces + \addtokens{\filename}{\PP}% + \advance\filenamelength by 1 + \fi + \nextsp} + \def\getfilename#1{% + \filenamelength=0 + % If we don't expand the argument now, \skipspaces will get + % snagged on things like "@value{foo}". + \edef\temp{#1}% + \expandafter\skipspaces\temp|\relax + } + \ifnum\pdftexversion < 14 + \let \startlink \pdfannotlink + \else + \let \startlink \pdfstartlink + \fi + % make a live url in pdf output. + \def\pdfurl#1{% + \begingroup + % it seems we really need yet another set of dummies; have not + % tried to figure out what each command should do in the context + % of @url. for now, just make @/ a no-op, that's the only one + % people have actually reported a problem with. + % + \normalturnoffactive + \def\@{@}% + \let\/=\empty + \makevalueexpandable + % do we want to go so far as to use \indexnofonts instead of just + % special-casing \var here? + \def\var##1{##1}% + % + \leavevmode\setcolor{\urlcolor}% + \startlink attr{/Border [0 0 0]}% + user{/Subtype /Link /A << /S /URI /URI (#1) >>}% + \endgroup} + % \pdfgettoks - Surround page numbers in #1 with @pdflink. #1 may + % be a simple number, or a list of numbers in the case of an index + % entry. + \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} + \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} + \def\maketoks{% + \expandafter\poptoks\the\toksA|ENDTOKS|\relax + \ifx\first0\adn0 + \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 + \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else + \ifnum0=\countA\else\makelink\fi + \ifx\first.\let\next=\done\else + \let\next=\maketoks + \addtokens{\toksB}{\the\toksD} + \ifx\first,\addtokens{\toksB}{\space}\fi + \fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next} + \def\makelink{\addtokens{\toksB}% + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} + \def\pdflink#1{% + \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} + \setcolor{\linkcolor}#1\endlink} + \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\else + % non-pdf mode + \let\pdfmkdest = \gobble + \let\pdfurl = \gobble + \let\endlink = \relax + \let\setcolor = \gobble + \let\pdfsetcolor = \gobble + \let\pdfmakeoutlines = \relax +\fi % \ifx\pdfoutput + +% +% For XeTeX +% +\ifx\XeTeXrevision\thisisundefined +\else + % + % XeTeX version check + % + \ifnum\strcmp{\the\XeTeXversion\XeTeXrevision}{0.99996}>-1 + % TeX Live 2016 contains XeTeX 0.99996 and xdvipdfmx 20160307. + % It can use the `dvipdfmx:config' special (from TeX Live SVN r40941). + % For avoiding PDF destination name replacement, we use this special + % instead of xdvipdfmx's command line option `-C 0x0010'. + \special{dvipdfmx:config C 0x0010} + % XeTeX 0.99995+ comes with xdvipdfmx 20160307+. + % It can handle Unicode destination names for PDF. + \txiuseunicodedestnametrue + \else + % XeTeX < 0.99996 (TeX Live < 2016) cannot use the + % `dvipdfmx:config' special. + % So for avoiding PDF destination name replacement, + % xdvipdfmx's command line option `-C 0x0010' is necessary. + % + % XeTeX < 0.99995 can not handle Unicode destination names for PDF + % because xdvipdfmx 20150315 has a UTF-16 conversion issue. + % It is fixed by xdvipdfmx 20160106 (TeX Live SVN r39753). + \txiuseunicodedestnamefalse + \fi + % + % Color support + % + \def\rgbDarkRed{0.50 0.09 0.12} + \def\rgbBlack{0 0 0} + % + \def\pdfsetcolor#1{\special{pdf:scolor [#1]}} + % + % Set color, and create a mark which defines \thiscolor accordingly, + % so that \makeheadline knows which color to restore. + \def\setcolor#1{% + \xdef\currentcolordefs{\gdef\noexpand\thiscolor{#1}}% + \domark + \pdfsetcolor{#1}% + } + % + \def\maincolor{\rgbBlack} + \pdfsetcolor{\maincolor} + \edef\thiscolor{\maincolor} + \def\currentcolordefs{} + % + \def\makefootline{% + \baselineskip24pt + \line{\pdfsetcolor{\maincolor}\the\footline}% + } + % + \def\makeheadline{% + \vbox to 0pt{% + \vskip-22.5pt + \line{% + \vbox to8.5pt{}% + % Extract \thiscolor definition from the marks. + \getcolormarks + % Typeset the headline with \maincolor, then restore the color. + \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% + }% + \vss + }% + \nointerlineskip + } + % + % PDF outline support + % + % Emulate pdfTeX primitive + \def\pdfdest name#1 xyz{% + \special{pdf:dest (#1) [@thispage /XYZ @xpos @ypos null]}% + } + % + \def\setpdfdestname#1{{% + % We have to set dummies so commands such as @code, and characters + % such as \, aren't expanded when present in a section title. + \indexnofonts + \makevalueexpandable + \turnoffactive + \iftxiuseunicodedestname + % Pass through Unicode characters. + \else + % Use ASCII approximations in destination names. + \passthroughcharsfalse + \fi + \def\pdfdestname{#1}% + \txiescapepdf\pdfdestname + }} + % + \def\setpdfoutlinetext#1{{% + \turnoffactive + % Always use Unicode characters in title texts. + \def\pdfoutlinetext{#1}% + % For XeTeX, xdvipdfmx converts to UTF-16. + % So we do not convert. + \txiescapepdf\pdfoutlinetext + }} + % + \def\pdfmkdest#1{% + \setpdfdestname{#1}% + \safewhatsit{\pdfdest name{\pdfdestname} xyz}% + } + % + % by default, use black for everything. + \def\urlcolor{\rgbBlack} + \def\linkcolor{\rgbBlack} + \def\endlink{\setcolor{\maincolor}\pdfendlink} + % + \def\dopdfoutline#1#2#3#4{% + \setpdfoutlinetext{#1} + \setpdfdestname{#3} + \ifx\pdfdestname\empty + \def\pdfdestname{#4}% + \fi + % + \special{pdf:out [-] #2 << /Title (\pdfoutlinetext) /A + << /S /GoTo /D (\pdfdestname) >> >> }% + } + % + \def\pdfmakeoutlines{% + \begingroup + % + % For XeTeX, counts of subentries are not necessary. + % Therefore, we read toc only once. + % + % We use node names as destinations. + % + % Currently we prefix the section name with the section number + % for chapter and appendix headings only in order to avoid too much + % horizontal space being required in the PDF viewer. + \def\partentry##1##2##3##4{}% ignore parts in the outlines + \def\numchapentry##1##2##3##4{% + \dopdfoutline{##2 ##1}{1}{##3}{##4}}% + \def\numsecentry##1##2##3##4{% + \dopdfoutline{##1}{2}{##3}{##4}}% + \def\numsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{3}{##3}{##4}}% + \def\numsubsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{4}{##3}{##4}}% + % + \let\appentry\numchapentry% + \let\appsecentry\numsecentry% + \let\appsubsecentry\numsubsecentry% + \let\appsubsubsecentry\numsubsubsecentry% + \def\unnchapentry##1##2##3##4{% + \dopdfoutline{##1}{1}{##3}{##4}}% + \let\unnsecentry\numsecentry% + \let\unnsubsecentry\numsubsecentry% + \let\unnsubsubsecentry\numsubsubsecentry% + % + % For XeTeX, xdvipdfmx converts strings to UTF-16. + % Therefore, the encoding and the language may not be considered. + % + \indexnofonts + \setupdatafile + % We can have normal brace characters in the PDF outlines, unlike + % Texinfo index files. So set that up. + \def\{{\lbracecharliteral}% + \def\}{\rbracecharliteral}% + \catcode`\\=\active \otherbackslash + \input \tocreadfilename + \endgroup + } + {\catcode`[=1 \catcode`]=2 + \catcode`{=\other \catcode`}=\other + \gdef\lbracecharliteral[{]% + \gdef\rbracecharliteral[}]% + ] + + \special{pdf:docview << /PageMode /UseOutlines >> } + % ``\special{pdf:tounicode ...}'' is not necessary + % because xdvipdfmx converts strings from UTF-8 to UTF-16 without it. + % However, due to a UTF-16 conversion issue of xdvipdfmx 20150315, + % ``\special{pdf:dest ...}'' cannot handle non-ASCII strings. + % It is fixed by xdvipdfmx 20160106 (TeX Live SVN r39753). +% + \def\skipspaces#1{\def\PP{#1}\def\D{|}% + \ifx\PP\D\let\nextsp\relax + \else\let\nextsp\skipspaces + \addtokens{\filename}{\PP}% + \advance\filenamelength by 1 + \fi + \nextsp} + \def\getfilename#1{% + \filenamelength=0 + % If we don't expand the argument now, \skipspaces will get + % snagged on things like "@value{foo}". + \edef\temp{#1}% + \expandafter\skipspaces\temp|\relax + } + % make a live url in pdf output. + \def\pdfurl#1{% + \begingroup + % it seems we really need yet another set of dummies; have not + % tried to figure out what each command should do in the context + % of @url. for now, just make @/ a no-op, that's the only one + % people have actually reported a problem with. + % + \normalturnoffactive + \def\@{@}% + \let\/=\empty + \makevalueexpandable + % do we want to go so far as to use \indexnofonts instead of just + % special-casing \var here? + \def\var##1{##1}% + % + \leavevmode\setcolor{\urlcolor}% + \special{pdf:bann << /Border [0 0 0] + /Subtype /Link /A << /S /URI /URI (#1) >> >>}% + \endgroup} + \def\endlink{\setcolor{\maincolor}\special{pdf:eann}} + \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} + \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} + \def\maketoks{% + \expandafter\poptoks\the\toksA|ENDTOKS|\relax + \ifx\first0\adn0 + \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 + \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else + \ifnum0=\countA\else\makelink\fi + \ifx\first.\let\next=\done\else + \let\next=\maketoks + \addtokens{\toksB}{\the\toksD} + \ifx\first,\addtokens{\toksB}{\space}\fi + \fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next} + \def\makelink{\addtokens{\toksB}% + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} + \def\pdflink#1{% + \special{pdf:bann << /Border [0 0 0] + /Type /Annot /Subtype /Link /A << /S /GoTo /D (#1) >> >>}% + \setcolor{\linkcolor}#1\endlink} + \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +% + % + % @image support + % + % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). + \def\doxeteximage#1#2#3{% + \def\xeteximagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% + \def\xeteximageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% + % + % XeTeX (and the PDF format) supports .pdf, .png, .jpg (among + % others). Let's try in that order, PDF first since if + % someone has a scalable image, presumably better to use that than a + % bitmap. + \let\xeteximgext=\empty + \begingroup + \openin 1 #1.pdf \ifeof 1 + \openin 1 #1.PDF \ifeof 1 + \openin 1 #1.png \ifeof 1 + \openin 1 #1.jpg \ifeof 1 + \openin 1 #1.jpeg \ifeof 1 + \openin 1 #1.JPG \ifeof 1 + \errmessage{Could not find image file #1 for XeTeX}% + \else \gdef\xeteximgext{JPG}% + \fi + \else \gdef\xeteximgext{jpeg}% + \fi + \else \gdef\xeteximgext{jpg}% + \fi + \else \gdef\xeteximgext{png}% + \fi + \else \gdef\xeteximgext{PDF}% + \fi + \else \gdef\xeteximgext{pdf}% + \fi + \closein 1 + \endgroup + % + \def\xetexpdfext{pdf}% + \ifx\xeteximgext\xetexpdfext + \XeTeXpdffile "#1".\xeteximgext "" + \else + \def\xetexpdfext{PDF}% + \ifx\xeteximgext\xetexpdfext + \XeTeXpdffile "#1".\xeteximgext "" + \else + \XeTeXpicfile "#1".\xeteximgext "" + \fi + \fi + \ifdim \wd0 >0pt width \xeteximagewidth \fi + \ifdim \wd2 >0pt height \xeteximageheight \fi \relax + } +\fi + + +% +\message{fonts,} + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +% can get a sort of poor man's double spacing by redefining this. +\def\baselinefactor{1} +% +\newdimen\textleading +\def\setleading#1{% + \dimen0 = #1\relax + \normalbaselineskip = \baselinefactor\dimen0 + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% PDF CMaps. See also LaTeX's t1.cmap. +% +% do nothing with this by default. +\expandafter\let\csname cmapOT1\endcsname\gobble +\expandafter\let\csname cmapOT1IT\endcsname\gobble +\expandafter\let\csname cmapOT1TT\endcsname\gobble + +% if we are producing pdf, and we have \pdffontattr, then define cmaps. +% (\pdffontattr was introduced many years ago, but people still run +% older pdftex's; it's easy to conditionalize, so we do.) +\ifpdf \ifx\pdffontattr\thisisundefined \else + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1-0) +%%Title: (TeX-OT1-0 TeX OT1 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1) +/Supplement 0 +>> def +/CMapName /TeX-OT1-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<23> <26> <0023> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +40 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1IT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1IT-0) +%%Title: (TeX-OT1IT-0 TeX OT1IT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1IT) +/Supplement 0 +>> def +/CMapName /TeX-OT1IT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<25> <26> <0025> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +42 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<23> <0023> +<24> <00A3> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1IT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1TT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1TT-0) +%%Title: (TeX-OT1TT-0 TeX OT1TT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1TT) +/Supplement 0 +>> def +/CMapName /TeX-OT1TT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +5 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<21> <26> <0021> +<28> <5F> <0028> +<61> <7E> <0061> +endbfrange +32 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <2191> +<0C> <2193> +<0D> <0027> +<0E> <00A1> +<0F> <00BF> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<20> <2423> +<27> <2019> +<60> <2018> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1TT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +\fi\fi + + +% Set the font macro #1 to the font named \fontprefix#2. +% #3 is the font's design size, #4 is a scale factor, #5 is the CMap +% encoding (only OT1, OT1IT and OT1TT are allowed, or empty to omit). +% Example: +% #1 = \textrm +% #2 = \rmshape +% #3 = 10 +% #4 = \mainmagstep +% #5 = OT1 +% +\def\setfont#1#2#3#4#5{% + \font#1=\fontprefix#2#3 scaled #4 + \csname cmap#5\endcsname#1% +} +% This is what gets called when #5 of \setfont is empty. +\let\cmap\gobble +% +% (end of cmaps) + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\thisisundefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} % where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +% Definitions for a main text size of 11pt. (The default in Texinfo.) +% +\def\definetextfontsizexi{% +% Text fonts (11.2pt, magstep1). +\def\textnominalsize{11pt} +\edef\mainmagstep{\magstephalf} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1095} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstep1}{OT1} +\setfont\deftt\ttshape{10}{\magstep1}{OT1TT} +\setfont\defsl\slshape{10}{\magstep1}{OT1} +\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} +\def\df{\let\ttfont=\deftt \let\bffont = \defbf +\let\ttslfont=\defttsl \let\slfont=\defsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for math mode superscripts (7pt). +\def\sevennominalsize{7pt} +\setfont\sevenrm\rmshape{7}{1000}{OT1} +\setfont\seventt\ttshape{10}{700}{OT1TT} +\setfont\sevenbf\bfshape{10}{700}{OT1} +\setfont\sevenit\itshape{7}{1000}{OT1IT} +\setfont\sevensl\slshape{10}{700}{OT1} +\setfont\sevensf\sfshape{10}{700}{OT1} +\setfont\sevensc\scshape{10}{700}{OT1} +\setfont\seventtsl\ttslshape{10}{700}{OT1TT} +\font\seveni=cmmi7 +\font\sevensy=cmsy7 +\def\sevenecsize{0700} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter (and unnumbered) fonts (17.28pt). +\def\chapnominalsize{17pt} +\setfont\chaprm\rmbshape{12}{\magstep2}{OT1} +\setfont\chapit\itbshape{10}{\magstep3}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep3}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} +\setfont\chapsf\sfbshape{17}{1000}{OT1} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3}{OT1} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 +\def\chapecsize{1728} + +% Section fonts (14.4pt). +\def\secnominalsize{14pt} +\setfont\secrm\rmbshape{12}{\magstep1}{OT1} +\setfont\secrmnotbold\rmshape{12}{\magstep1}{OT1} +\setfont\secit\itbshape{10}{\magstep2}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep2}{OT1} +\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\secsf\sfbshape{12}{\magstep1}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2}{OT1} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 +\def\sececsize{1440} + +% Subsection fonts (13.15pt). +\def\ssecnominalsize{13pt} +\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} +\setfont\ssecit\itbshape{10}{1315}{OT1IT} +\setfont\ssecsl\slbshape{10}{1315}{OT1} +\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} +\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1315}{OT1} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled 1315 +\def\ssececsize{1200} + +% Reduced fonts for @acronym in text (10pt). +\def\reducednominalsize{10pt} +\setfont\reducedrm\rmshape{10}{1000}{OT1} +\setfont\reducedtt\ttshape{10}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{1000}{OT1} +\setfont\reducedit\itshape{10}{1000}{OT1IT} +\setfont\reducedsl\slshape{10}{1000}{OT1} +\setfont\reducedsf\sfshape{10}{1000}{OT1} +\setfont\reducedsc\scshape{10}{1000}{OT1} +\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} +\font\reducedi=cmmi10 +\font\reducedsy=cmsy10 +\def\reducedecsize{1000} + +\textleading = 13.2pt % line spacing for 11pt CM +\textfonts % reset the current fonts +\rm +} % end of 11pt text font size definitions, \definetextfontsizexi + + +% Definitions to make the main text be 10pt Computer Modern, with +% section, chapter, etc., sizes following suit. This is for the GNU +% Press printing of the Emacs 22 manual. Maybe other manuals in the +% future. Used with @smallbook, which sets the leading to 12pt. +% +\def\definetextfontsizex{% +% Text fonts (10pt). +\def\textnominalsize{10pt} +\edef\mainmagstep{1000} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1000} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstephalf}{OT1} +\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} +\setfont\defsl\slshape{10}{\magstephalf}{OT1} +\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} +\def\df{\let\ttfont=\deftt \let\bffont = \defbf +\let\slfont=\defsl \let\ttslfont=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for math mode superscripts (7pt). +\def\sevennominalsize{7pt} +\setfont\sevenrm\rmshape{7}{1000}{OT1} +\setfont\seventt\ttshape{10}{700}{OT1TT} +\setfont\sevenbf\bfshape{10}{700}{OT1} +\setfont\sevenit\itshape{7}{1000}{OT1IT} +\setfont\sevensl\slshape{10}{700}{OT1} +\setfont\sevensf\sfshape{10}{700}{OT1} +\setfont\sevensc\scshape{10}{700}{OT1} +\setfont\seventtsl\ttslshape{10}{700}{OT1TT} +\font\seveni=cmmi7 +\font\sevensy=cmsy7 +\def\sevenecsize{0700} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter fonts (14.4pt). +\def\chapnominalsize{14pt} +\setfont\chaprm\rmbshape{12}{\magstep1}{OT1} +\setfont\chapit\itbshape{10}{\magstep2}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep2}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\chapsf\sfbshape{12}{\magstep1}{OT1} +\let\chapbf\chaprm +\setfont\chapsc\scbshape{10}{\magstep2}{OT1} +\font\chapi=cmmi12 scaled \magstep1 +\font\chapsy=cmsy10 scaled \magstep2 +\def\chapecsize{1440} + +% Section fonts (12pt). +\def\secnominalsize{12pt} +\setfont\secrm\rmbshape{12}{1000}{OT1} +\setfont\secit\itbshape{10}{\magstep1}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep1}{OT1} +\setfont\sectt\ttbshape{12}{1000}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} +\setfont\secsf\sfbshape{12}{1000}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep1}{OT1} +\font\seci=cmmi12 +\font\secsy=cmsy10 scaled \magstep1 +\def\sececsize{1200} + +% Subsection fonts (10pt). +\def\ssecnominalsize{10pt} +\setfont\ssecrm\rmbshape{10}{1000}{OT1} +\setfont\ssecit\itbshape{10}{1000}{OT1IT} +\setfont\ssecsl\slbshape{10}{1000}{OT1} +\setfont\ssectt\ttbshape{10}{1000}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} +\setfont\ssecsf\sfbshape{10}{1000}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1000}{OT1} +\font\sseci=cmmi10 +\font\ssecsy=cmsy10 +\def\ssececsize{1000} + +% Reduced fonts for @acronym in text (9pt). +\def\reducednominalsize{9pt} +\setfont\reducedrm\rmshape{9}{1000}{OT1} +\setfont\reducedtt\ttshape{9}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{900}{OT1} +\setfont\reducedit\itshape{9}{1000}{OT1IT} +\setfont\reducedsl\slshape{9}{1000}{OT1} +\setfont\reducedsf\sfshape{9}{1000}{OT1} +\setfont\reducedsc\scshape{10}{900}{OT1} +\setfont\reducedttsl\ttslshape{10}{900}{OT1TT} +\font\reducedi=cmmi9 +\font\reducedsy=cmsy9 +\def\reducedecsize{0900} + +\divide\parskip by 2 % reduce space between paragraphs +\textleading = 12pt % line spacing for 10pt CM +\textfonts % reset the current fonts +\rm +} % end of 10pt text font size definitions, \definetextfontsizex + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000}{OT1} +\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 +\setfont\shortcontsl\slshape{12}{1000}{OT1} +\setfont\shortconttt\ttshape{12}{1000}{OT1TT} + + +% We provide the user-level command +% @fonttextsize 10 +% (or 11) to redefine the text font size. pt is assumed. +% +\def\xiword{11} +\def\xword{10} +\def\xwordpt{10pt} +% +\parseargdef\fonttextsize{% + \def\textsizearg{#1}% + %\wlog{doing @fonttextsize \textsizearg}% + % + % Set \globaldefs so that documents can use this inside @tex, since + % makeinfo 4.8 does not support it, but we need it nonetheless. + % + \begingroup \globaldefs=1 + \ifx\textsizearg\xword \definetextfontsizex + \else \ifx\textsizearg\xiword \definetextfontsizexi + \else + \errhelp=\EMsimple + \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} + \fi\fi + \endgroup +} + +% +% Change the current font style to #1, remembering it in \curfontstyle. +% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in +% italics, not bold italics. +% +\def\setfontstyle#1{% + \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. + \csname #1font\endcsname % change the current font +} + +\def\rm{\fam=0 \setfontstyle{rm}} +\def\it{\fam=\itfam \setfontstyle{it}} +\def\sl{\fam=\slfam \setfontstyle{sl}} +\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} +\def\tt{\fam=\ttfam \setfontstyle{tt}}\def\ttstylename{tt} + +% Texinfo sort of supports the sans serif font style, which plain TeX does not. +% So we set up a \sf. +\newfam\sffam +\def\sf{\fam=\sffam \setfontstyle{sf}} + +% We don't need math for this font style. +\def\ttsl{\setfontstyle{ttsl}} + + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. +% We don't bother to reset \scriptscriptfont; awaiting user need. +% +\def\resetmathfonts{% + \textfont0=\rmfont \textfont1=\ifont \textfont2=\syfont + \textfont\itfam=\itfont \textfont\slfam=\slfont \textfont\bffam=\bffont + \textfont\ttfam=\ttfont \textfont\sffam=\sffont + % + % Fonts for superscript. Note that the 7pt fonts are used regardless + % of the current font size. + \scriptfont0=\sevenrm \scriptfont1=\seveni \scriptfont2=\sevensy + \scriptfont\itfam=\sevenit \scriptfont\slfam=\sevensl + \scriptfont\bffam=\sevenbf \scriptfont\ttfam=\seventt + \scriptfont\sffam=\sevensf +} + +% + +% The font-changing commands (all called \...fonts) redefine the meanings +% of \STYLEfont, instead of just \STYLE. We do this because \STYLE needs +% to also set the current \fam for math mode. Our \STYLE (e.g., \rm) +% commands hardwire \STYLEfont to set the current font. +% +% The fonts used for \ifont are for "math italics" (\itfont is for italics +% in regular text). \syfont is also used in math mode only. +% +% Each font-changing command also sets the names \lsize (one size lower) +% and \lllsize (three sizes lower). These relative commands are used +% in, e.g., the LaTeX logo and acronyms. +% +% This all needs generalizing, badly. +% + +\def\assignfonts#1{% + \expandafter\let\expandafter\rmfont\csname #1rm\endcsname + \expandafter\let\expandafter\itfont\csname #1it\endcsname + \expandafter\let\expandafter\slfont\csname #1sl\endcsname + \expandafter\let\expandafter\bffont\csname #1bf\endcsname + \expandafter\let\expandafter\ttfont\csname #1tt\endcsname + \expandafter\let\expandafter\smallcaps\csname #1sc\endcsname + \expandafter\let\expandafter\sffont \csname #1sf\endcsname + \expandafter\let\expandafter\ifont \csname #1i\endcsname + \expandafter\let\expandafter\syfont \csname #1sy\endcsname + \expandafter\let\expandafter\ttslfont\csname #1ttsl\endcsname +} + +\newif\ifrmisbold + +% Select smaller font size with the current style. Used to change font size +% in, e.g., the LaTeX logo and acronyms. If we are using bold fonts for +% normal roman text, also use bold fonts for roman text in the smaller size. +\def\switchtolllsize{% + \expandafter\assignfonts\expandafter{\lllsize}% + \ifrmisbold + \let\rmfont\bffont + \fi + \csname\curfontstyle\endcsname +}% + +\def\switchtolsize{% + \expandafter\assignfonts\expandafter{\lsize}% + \ifrmisbold + \let\rmfont\bffont + \fi + \csname\curfontstyle\endcsname +}% + +\def\definefontsetatsize#1#2#3#4#5{% +\expandafter\def\csname #1fonts\endcsname{% + \def\curfontsize{#1}% + \def\lsize{#2}\def\lllsize{#3}% + \csname rmisbold#5\endcsname + \assignfonts{#1}% + \resetmathfonts + \setleading{#4}% +}} + +\definefontsetatsize{text} {reduced}{smaller}{\textleading}{false} +\definefontsetatsize{title} {chap} {subsec} {27pt} {true} +\definefontsetatsize{chap} {sec} {text} {19pt} {true} +\definefontsetatsize{sec} {subsec} {reduced}{17pt} {true} +\definefontsetatsize{ssec} {text} {small} {15pt} {true} +\definefontsetatsize{reduced}{small} {smaller}{10.5pt}{false} +\definefontsetatsize{small} {smaller}{smaller}{10.5pt}{false} +\definefontsetatsize{smaller}{smaller}{smaller}{9.5pt} {false} + +\def\titlefont#1{{\titlefonts\rm #1}} +\let\subsecfonts = \ssecfonts +\let\subsubsecfonts = \ssecfonts + +% Define these just so they can be easily changed for other fonts. +\def\angleleft{$\langle$} +\def\angleright{$\rangle$} + +% Set the fonts to use with the @small... environments. +\let\smallexamplefonts = \smallfonts + +% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample +% can fit this many characters: +% 8.5x11=86 smallbook=72 a4=90 a5=69 +% If we use \scriptfonts (8pt), then we can fit this many characters: +% 8.5x11=90+ smallbook=80 a4=90+ a5=77 +% For me, subjectively, the few extra characters that fit aren't worth +% the additional smallness of 8pt. So I'm making the default 9pt. +% +% By the way, for comparison, here's what fits with @example (10pt): +% 8.5x11=71 smallbook=60 a4=75 a5=58 +% --karl, 24jan03. + +% Set up the default fonts, so we can use them for creating boxes. +% +\definetextfontsizexi + + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +{ +\catcode`\'=\active +\catcode`\`=\active + +\gdef\setcodequotes{\let`\codequoteleft \let'\codequoteright} +\gdef\setregularquotes{\let`\lq \let'\rq} +} + +% Allow an option to not use regular directed right quote/apostrophe +% (char 0x27), but instead the undirected quote from cmtt (char 0x0d). +% The undirected quote is ugly, so don't make it the default, but it +% works for pasting with more pdf viewers (at least evince), the +% lilypond developers report. xpdf does work with the regular 0x27. +% +\def\codequoteright{% + \ifmonospace + \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax + \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax + '% + \else \char'15 \fi + \else \char'15 \fi + \else + '% + \fi +} +% +% and a similar option for the left quote char vs. a grave accent. +% Modern fonts display ASCII 0x60 as a grave accent, so some people like +% the code environments to do likewise. +% +\def\codequoteleft{% + \ifmonospace + \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax + \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax + % [Knuth] pp. 380,381,391 + % \relax disables Spanish ligatures ?` and !` of \tt font. + \relax`% + \else \char'22 \fi + \else \char'22 \fi + \else + \relax`% + \fi +} + +% Commands to set the quote options. +% +\parseargdef\codequoteundirected{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxicodequoteundirected\endcsname + = t% + \else\ifx\temp\offword + \expandafter\let\csname SETtxicodequoteundirected\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}% + \fi\fi +} +% +\parseargdef\codequotebacktick{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxicodequotebacktick\endcsname + = t% + \else\ifx\temp\offword + \expandafter\let\csname SETtxicodequotebacktick\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}% + \fi\fi +} + +% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font. +\def\noligaturesquoteleft{\relax\lq} + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Font commands. + +% #1 is the font command (\sl or \it), #2 is the text to slant. +% If we are in a monospaced environment, however, 1) always use \ttsl, +% and 2) do not add an italic correction. +\def\dosmartslant#1#2{% + \ifusingtt + {{\ttsl #2}\let\next=\relax}% + {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}% + \next +} +\def\smartslanted{\dosmartslant\sl} +\def\smartitalic{\dosmartslant\it} + +% Output an italic correction unless \next (presumed to be the following +% character) is such as not to need one. +\def\smartitaliccorrection{% + \ifx\next,% + \else\ifx\next-% + \else\ifx\next.% + \else\ifx\next\.% + \else\ifx\next\comma% + \else\ptexslash + \fi\fi\fi\fi\fi + \aftersmartic +} + +% Unconditional use \ttsl, and no ic. @var is set to this for defuns. +\def\ttslanted#1{{\ttsl #1}} + +% @cite is like \smartslanted except unconditionally use \sl. We never want +% ttsl for book titles, do we? +\def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection} + +\def\aftersmartic{} +\def\var#1{% + \let\saveaftersmartic = \aftersmartic + \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}% + \smartslanted{#1}% +} + +\let\i=\smartitalic +\let\slanted=\smartslanted +\let\dfn=\smartslanted +\let\emph=\smartitalic + +% Explicit font changes: @r, @sc, undocumented @ii. +\def\r#1{{\rm #1}} % roman font +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @b, explicit bold. Also @strong. +\def\b#1{{\bf #1}} +\let\strong=\b + +% @sansserif, explicit sans. +\def\sansserif#1{{\sf #1}} + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +% Set sfcode to normal for the chars that usually have another value. +% Can't use plain's \frenchspacing because it uses the `\x notation, and +% sometimes \x has an active definition that messes things up. +% +\catcode`@=11 + \def\plainfrenchspacing{% + \sfcode`\.=\@m \sfcode`\?=\@m \sfcode`\!=\@m + \sfcode`\:=\@m \sfcode`\;=\@m \sfcode`\,=\@m + \def\endofsentencespacefactor{1000}% for @. and friends + } + \def\plainnonfrenchspacing{% + \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 + \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 + \def\endofsentencespacefactor{3000}% for @. and friends + } +\catcode`@=\other +\def\endofsentencespacefactor{3000}% default + +% @t, explicit typewriter. +\def\t#1{% + {\tt \plainfrenchspacing #1}% + \null +} + +% @samp. +\def\samp#1{{\setcodequotes\lq\tclose{#1}\rq\null}} + +% @indicateurl is \samp, that is, with quotes. +\let\indicateurl=\samp + +% @code (and similar) prints in typewriter, but with spaces the same +% size as normal in the surrounding text, without hyphenation, etc. +% This is a subroutine for that. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \plainfrenchspacing + #1% + }% + \null % reset spacefactor to 1000 +} + +% We *must* turn on hyphenation at `-' and `_' in @code. +% (But see \codedashfinish below.) +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. +% +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. -- rms. +{ + \catcode`\-=\active \catcode`\_=\active + \catcode`\'=\active \catcode`\`=\active + \global\let'=\rq \global\let`=\lq % default definitions + % + \global\def\code{\begingroup + \setcodequotes + \catcode\dashChar=\active \catcode\underChar=\active + \ifallowcodebreaks + \let-\codedash + \let_\codeunder + \else + \let-\normaldash + \let_\realunder + \fi + % Given -foo (with a single dash), we do not want to allow a break + % after the hyphen. + \global\let\codedashprev=\codedash + % + \codex + } + % + \gdef\codedash{\futurelet\next\codedashfinish} + \gdef\codedashfinish{% + \normaldash % always output the dash character itself. + % + % Now, output a discretionary to allow a line break, unless + % (a) the next character is a -, or + % (b) the preceding character is a -. + % E.g., given --posix, we do not want to allow a break after either -. + % Given --foo-bar, we do want to allow a break between the - and the b. + \ifx\next\codedash \else + \ifx\codedashprev\codedash + \else \discretionary{}{}{}\fi + \fi + % we need the space after the = for the case when \next itself is a + % space token; it would get swallowed otherwise. As in @code{- a}. + \global\let\codedashprev= \next + } +} +\def\normaldash{-} +% +\def\codex #1{\tclose{#1}\endgroup} + +\def\codeunder{% + % this is all so @math{@code{var_name}+1} can work. In math mode, _ + % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) + % will therefore expand the active definition of _, which is us + % (inside @code that is), therefore an endless loop. + \ifusingtt{\ifmmode + \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. + \else\normalunderscore \fi + \discretionary{}{}{}}% + {\_}% +} + +% An additional complication: the above will allow breaks after, e.g., +% each of the four underscores in __typeof__. This is bad. +% @allowcodebreaks provides a document-level way to turn breaking at - +% and _ on and off. +% +\newif\ifallowcodebreaks \allowcodebreakstrue + +\def\keywordtrue{true} +\def\keywordfalse{false} + +\parseargdef\allowcodebreaks{% + \def\txiarg{#1}% + \ifx\txiarg\keywordtrue + \allowcodebreakstrue + \else\ifx\txiarg\keywordfalse + \allowcodebreaksfalse + \else + \errhelp = \EMsimple + \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}% + \fi\fi +} + +% For @command, @env, @file, @option quotes seem unnecessary, +% so use \code rather than \samp. +\let\command=\code +\let\env=\code +\let\file=\code +\let\option=\code + +% @uref (abbreviation for `urlref') aka @url takes an optional +% (comma-separated) second argument specifying the text to display and +% an optional third arg as text to display instead of (rather than in +% addition to) the url itself. First (mandatory) arg is the url. + +% TeX-only option to allow changing PDF output to show only the second +% arg (if given), and not the url (which is then just the link target). +\newif\ifurefurlonlylink + +% The default \pretolerance setting stops the penalty inserted in +% \urefallowbreak being a discouragement to line breaking. Set it to +% a negative value for this paragraph only. Hopefully this does not +% conflict with redefinitions of \par done elsewhere. +\def\nopretolerance{% +\pretolerance=-1 +\def\par{\endgraf\pretolerance=100 \let\par\endgraf}% +} + +% The main macro is \urefbreak, which allows breaking at expected +% places within the url. +\def\urefbreak{\nopretolerance \begingroup \urefcatcodes \dourefbreak} +\let\uref=\urefbreak +% +\def\dourefbreak#1{\urefbreakfinish #1,,,\finish} +\def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% look for second arg + \ifdim\wd0 > 0pt + \ifpdf + % For pdfTeX and LuaTeX + \ifurefurlonlylink + % PDF plus option to not display url, show just arg + \unhbox0 + \else + % PDF, normally display both arg and url for consistency, + % visibility, if the pdf is eventually used to print, etc. + \unhbox0\ (\urefcode{#1})% + \fi + \else + \ifx\XeTeXrevision\thisisundefined + \unhbox0\ (\urefcode{#1})% DVI, always show arg and url + \else + % For XeTeX + \ifurefurlonlylink + % PDF plus option to not display url, show just arg + \unhbox0 + \else + % PDF, normally display both arg and url for consistency, + % visibility, if the pdf is eventually used to print, etc. + \unhbox0\ (\urefcode{#1})% + \fi + \fi + \fi + \else + \urefcode{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% Allow line breaks around only a few characters (only). +\def\urefcatcodes{% + \catcode`\&=\active \catcode`\.=\active + \catcode`\#=\active \catcode`\?=\active + \catcode`\/=\active +} +{ + \urefcatcodes + % + \global\def\urefcode{\begingroup + \setcodequotes + \urefcatcodes + \let&\urefcodeamp + \let.\urefcodedot + \let#\urefcodehash + \let?\urefcodequest + \let/\urefcodeslash + \codex + } + % + % By default, they are just regular characters. + \global\def&{\normalamp} + \global\def.{\normaldot} + \global\def#{\normalhash} + \global\def?{\normalquest} + \global\def/{\normalslash} +} + +\def\urefcodeamp{\urefprebreak \&\urefpostbreak} +\def\urefcodedot{\urefprebreak .\urefpostbreak} +\def\urefcodehash{\urefprebreak \#\urefpostbreak} +\def\urefcodequest{\urefprebreak ?\urefpostbreak} +\def\urefcodeslash{\futurelet\next\urefcodeslashfinish} +{ + \catcode`\/=\active + \global\def\urefcodeslashfinish{% + \urefprebreak \slashChar + % Allow line break only after the final / in a sequence of + % slashes, to avoid line break between the slashes in http://. + \ifx\next/\else \urefpostbreak \fi + } +} + +% By default we'll break after the special characters, but some people like to +% break before the special chars, so allow that. Also allow no breaking at +% all, for manual control. +% +\parseargdef\urefbreakstyle{% + \def\txiarg{#1}% + \ifx\txiarg\wordnone + \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak} + \else\ifx\txiarg\wordbefore + \def\urefprebreak{\urefallowbreak}\def\urefpostbreak{\nobreak} + \else\ifx\txiarg\wordafter + \def\urefprebreak{\nobreak}\def\urefpostbreak{\urefallowbreak} + \else + \errhelp = \EMsimple + \errmessage{Unknown @urefbreakstyle setting `\txiarg'}% + \fi\fi\fi +} +\def\wordafter{after} +\def\wordbefore{before} +\def\wordnone{none} + +% Allow a ragged right output to aid breaking long URL's. There can +% be a break at the \allowbreak with no extra glue (if the existing stretch in +% the line is sufficient), a break at the \penalty with extra glue added +% at the end of the line, or no break at all here. +% Changing the value of the penalty and/or the amount of stretch affects how +% preferable one choice is over the other. +\def\urefallowbreak{% + \penalty0\relax + \hskip 0pt plus 2 em\relax + \penalty1000\relax + \hskip 0pt plus -2 em\relax +} + +\urefbreakstyle after + +% @url synonym for @uref, since that's how everyone uses it. +% +\let\url=\uref + +% rms does not like angle brackets --karl, 17may97. +% So now @email is just like @uref, unless we are pdf. +% +%\def\email#1{\angleleft{\tt #1}\angleright} +\ifpdforxetex + \def\email#1{\doemail#1,,\finish} + \def\doemail#1,#2,#3\finish{\begingroup + \unsepspaces + \pdfurl{mailto:#1}% + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi + \endlink + \endgroup} +\else + \let\email=\uref +\fi + +% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), +% `example' (@kbd uses ttsl only inside of @example and friends), +% or `code' (@kbd uses normal tty font always). +\parseargdef\kbdinputstyle{% + \def\txiarg{#1}% + \ifx\txiarg\worddistinct + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% + \else\ifx\txiarg\wordexample + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% + \else\ifx\txiarg\wordcode + \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \else + \errhelp = \EMsimple + \errmessage{Unknown @kbdinputstyle setting `\txiarg'}% + \fi\fi\fi +} +\def\worddistinct{distinct} +\def\wordexample{example} +\def\wordcode{code} + +% Default is `distinct'. +\kbdinputstyle distinct + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. +\def\kbd#1{{\def\look{#1}\expandafter\kbdsub\look??\par}} + +\def\xkey{\key} +\def\kbdsub#1#2#3\par{% + \def\one{#1}\def\three{#3}\def\threex{??}% + \ifx\one\xkey\ifx\threex\three \key{#2}% + \else{\tclose{\kbdfont\setcodequotes\look}}\fi + \else{\tclose{\kbdfont\setcodequotes\look}}\fi +} + +% definition of @key that produces a lozenge. Doesn't adjust to text size. +%\setfont\keyrm\rmshape{8}{1000}{OT1} +%\font\keysy=cmsy9 +%\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% +% \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% +% \vbox{\hrule\kern-0.4pt +% \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% +% \kern-0.4pt\hrule}% +% \kern-.06em\raise0.4pt\hbox{\angleright}}}} + +% definition of @key with no lozenge. If the current font is already +% monospace, don't change it; that way, we respect @kbdinputstyle. But +% if it isn't monospace, then use \tt. +% +\def\key#1{{\setregularquotes + \nohyphenation + \ifmonospace\else\tt\fi + #1}\null} + +% @clicksequence{File @click{} Open ...} +\def\clicksequence#1{\begingroup #1\endgroup} + +% @clickstyle @arrow (by default) +\parseargdef\clickstyle{\def\click{#1}} +\def\click{\arrow} + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +% @acronym for "FBI", "NATO", and the like. +% We print this one point size smaller, since it's intended for +% all-uppercase. +% +\def\acronym#1{\doacronym #1,,\finish} +\def\doacronym#1,#2,#3\finish{% + {\switchtolsize #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi + \null % reset \spacefactor=1000 +} + +% @abbr for "Comput. J." and the like. +% No font change, but don't do end-of-sentence spacing. +% +\def\abbr#1{\doabbr #1,,\finish} +\def\doabbr#1,#2,#3\finish{% + {\plainfrenchspacing #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi + \null % reset \spacefactor=1000 +} + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math outputs its argument in math mode. +% +% One complication: _ usually means subscripts, but it could also mean +% an actual _ character, as in @math{@var{some_variable} + 1}. So make +% _ active, and distinguish by seeing if the current family is \slfam, +% which is what @var uses. +{ + \catcode`\_ = \active + \gdef\mathunderscore{% + \catcode`\_=\active + \def_{\ifnum\fam=\slfam \_\else\sb\fi}% + } +} +% Another complication: we want \\ (and @\) to output a math (or tt) \. +% FYI, plain.tex uses \\ as a temporary control sequence (for no +% particular reason), but this is not advertised and we don't care. +% +% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. +\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} +% +\def\math{% + \ifmmode\else % only go into math if not in math mode already + \tex + \mathunderscore + \let\\ = \mathbackslash + \mathactive + % make the texinfo accent commands work in math mode + \let\"=\ddot + \let\'=\acute + \let\==\bar + \let\^=\hat + \let\`=\grave + \let\u=\breve + \let\v=\check + \let\~=\tilde + \let\dotaccent=\dot + % have to provide another name for sup operator + \let\mathopsup=\sup + $\expandafter\finishmath\fi +} +\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. + +% Some active characters (such as <) are spaced differently in math. +% We have to reset their definitions in case the @math was an argument +% to a command which sets the catcodes (such as @item or @section). +% +{ + \catcode`^ = \active + \catcode`< = \active + \catcode`> = \active + \catcode`+ = \active + \catcode`' = \active + \gdef\mathactive{% + \let^ = \ptexhat + \let< = \ptexless + \let> = \ptexgtr + \let+ = \ptexplus + \let' = \ptexquoteright + } +} + +% for @sub and @sup, if in math mode, just do a normal sub/superscript. +% If in text, use math to place as sub/superscript, but switch +% into text mode, with smaller fonts. This is a different font than the +% one used for real math sub/superscripts (8pt vs. 7pt), but let's not +% fix it (significant additions to font machinery) until someone notices. +% +\def\sub{\ifmmode \expandafter\sb \else \expandafter\finishsub\fi} +\def\finishsub#1{$\sb{\hbox{\switchtolllsize #1}}$}% +% +\def\sup{\ifmmode \expandafter\ptexsp \else \expandafter\finishsup\fi} +\def\finishsup#1{$\ptexsp{\hbox{\switchtolllsize #1}}$}% + +% provide this command from LaTeX as it is very common +\def\frac#1#2{{{#1}\over{#2}}} + +% @displaymath. +% \globaldefs is needed to recognize the end lines in \tex and +% \end tex. Set \thisenv as @end displaymath is seen before @end tex. +{\obeylines +\globaldefs=1 +\envdef\displaymath{% +\tex% +\def\thisenv{\displaymath}% +\begingroup\let\end\displaymathend% +$$% +} + +\def\displaymathend{$$\endgroup\end}% + +\def\Edisplaymath{% +\def\thisenv{\tex}% +\end tex +}} + + +% @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}. +% Ignore unless FMTNAME == tex; then it is like @iftex and @tex, +% except specified as a normal braced arg, so no newlines to worry about. +% +\def\outfmtnametex{tex} +% +\long\def\inlinefmt#1{\doinlinefmt #1,\finish} +\long\def\doinlinefmt#1,#2,\finish{% + \def\inlinefmtname{#1}% + \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi +} +% +% @inlinefmtifelse{FMTNAME,THEN-TEXT,ELSE-TEXT} expands THEN-TEXT if +% FMTNAME is tex, else ELSE-TEXT. +\long\def\inlinefmtifelse#1{\doinlinefmtifelse #1,,,\finish} +\long\def\doinlinefmtifelse#1,#2,#3,#4,\finish{% + \def\inlinefmtname{#1}% + \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\else \ignorespaces #3\fi +} +% +% For raw, must switch into @tex before parsing the argument, to avoid +% setting catcodes prematurely. Doing it this way means that, for +% example, @inlineraw{html, foo{bar} gets a parse error instead of being +% ignored. But this isn't important because if people want a literal +% *right* brace they would have to use a command anyway, so they may as +% well use a command to get a left brace too. We could re-use the +% delimiter character idea from \verb, but it seems like overkill. +% +\long\def\inlineraw{\tex \doinlineraw} +\long\def\doinlineraw#1{\doinlinerawtwo #1,\finish} +\def\doinlinerawtwo#1,#2,\finish{% + \def\inlinerawname{#1}% + \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi + \endgroup % close group opened by \tex. +} + +% @inlineifset{VAR, TEXT} expands TEXT if VAR is @set. +% +\long\def\inlineifset#1{\doinlineifset #1,\finish} +\long\def\doinlineifset#1,#2,\finish{% + \def\inlinevarname{#1}% + \expandafter\ifx\csname SET\inlinevarname\endcsname\relax + \else\ignorespaces#2\fi +} + +% @inlineifclear{VAR, TEXT} expands TEXT if VAR is not @set. +% +\long\def\inlineifclear#1{\doinlineifclear #1,\finish} +\long\def\doinlineifclear#1,#2,\finish{% + \def\inlinevarname{#1}% + \expandafter\ifx\csname SET\inlinevarname\endcsname\relax \ignorespaces#2\fi +} + + +\message{glyphs,} +% and logos. + +% @@ prints an @, as does @atchar{}. +\def\@{\char64 } +\let\atchar=\@ + +% @{ @} @lbracechar{} @rbracechar{} all generate brace characters. +\def\lbracechar{{\ifmonospace\char123\else\ensuremath\lbrace\fi}} +\def\rbracechar{{\ifmonospace\char125\else\ensuremath\rbrace\fi}} +\let\{=\lbracechar +\let\}=\rbracechar + +% @comma{} to avoid , parsing problems. +\let\comma = , + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. +\let\, = \ptexc +\let\dotaccent = \ptexdot +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \ptext +\let\ubaraccent = \ptexb +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown @ordf @ordm +% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} +\def\ordf{\leavevmode\raise1ex\hbox{\switchtolllsize \underbar{a}}} +\def\ordm{\leavevmode\raise1ex\hbox{\switchtolllsize \underbar{o}}} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi + \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% The \TeX{} logo, as in plain, but resetting the spacing so that a +% period following counts as ending a sentence. (Idea found in latex.) +% +\edef\TeX{\TeX \spacefactor=1000 } + +% @LaTeX{} logo. Not quite the same results as the definition in +% latex.ltx, since we use a different font for the raised A; it's most +% convenient for us to use an explicitly smaller font, rather than using +% the \scriptstyle font (since we don't reset \scriptstyle and +% \scriptscriptstyle). +% +\def\LaTeX{% + L\kern-.36em + {\setbox0=\hbox{T}% + \vbox to \ht0{\hbox{% + \ifx\textnominalsize\xwordpt + % for 10pt running text, lllsize (8pt) is too small for the A in LaTeX. + % Revert to plain's \scriptsize, which is 7pt. + \count255=\the\fam $\fam\count255 \scriptstyle A$% + \else + % For 11pt, we can use our lllsize. + \switchtolllsize A% + \fi + }% + \vss + }}% + \kern-.15em + \TeX +} + +% Some math mode symbols. Define \ensuremath to switch into math mode +% unless we are already there. Expansion tricks may not be needed here, +% but safer, and can't hurt. +\def\ensuremath{\ifmmode \expandafter\asis \else\expandafter\ensuredmath \fi} +\def\ensuredmath#1{$\relax#1$} +% +\def\bullet{\ensuremath\ptexbullet} +\def\geq{\ensuremath\ge} +\def\leq{\ensuremath\le} +\def\minus{\ensuremath-} + +% @dots{} outputs an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in the cm +% typewriter fonts as three actual period characters; on the other hand, +% in other typewriter fonts three periods are wider than 1.5em. So do +% whichever is larger. +% +\def\dots{% + \leavevmode + \setbox0=\hbox{...}% get width of three periods + \ifdim\wd0 > 1.5em + \dimen0 = \wd0 + \else + \dimen0 = 1.5em + \fi + \hbox to \dimen0{% + \hskip 0pt plus.25fil + .\hskip 0pt plus1fil + .\hskip 0pt plus1fil + .\hskip 0pt plus.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \dots + \spacefactor=\endofsentencespacefactor +} + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +% +% Since these characters are used in examples, they should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% +\def\point{$\star$} +\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}} +\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% The @error{} command. +% Adapted from the TeXbook's \boxit. +% +\newbox\errorbox +% +{\ttfont \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt} +% +\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{% + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} +% +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @pounds{} is a sterling sign, which Knuth put in the CM italic font. +% +\def\pounds{\ifmonospace{\ecfont\char"BF}\else{\it\$}\fi} + +% @euro{} comes from a separate font, depending on the current style. +% We use the free feym* fonts from the eurosym package by Henrik +% Theiling, which support regular, slanted, bold and bold slanted (and +% "outlined" (blackboard board, sort of) versions, which we don't need). +% It is available from http://www.ctan.org/tex-archive/fonts/eurosym. +% +% Although only regular is the truly official Euro symbol, we ignore +% that. The Euro is designed to be slightly taller than the regular +% font height. +% +% feymr - regular +% feymo - slanted +% feybr - bold +% feybo - bold slanted +% +% There is no good (free) typewriter version, to my knowledge. +% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. +% Hmm. +% +% Also doesn't work in math. Do we need to do math with euro symbols? +% Hope not. +% +% +\def\euro{{\eurofont e}} +\def\eurofont{% + % We set the font at each command, rather than predefining it in + % \textfonts and the other font-switching commands, so that + % installations which never need the symbol don't have to have the + % font installed. + % + % There is only one designed size (nominal 10pt), so we always scale + % that to the current nominal size. + % + % By the way, simply using "at 1em" works for cmr10 and the like, but + % does not work for cmbx10 and other extended/shrunken fonts. + % + \def\eurosize{\csname\curfontsize nominalsize\endcsname}% + % + \ifx\curfontstyle\bfstylename + % bold: + \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize + \else + % regular: + \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize + \fi + \thiseurofont +} + +% Glyphs from the EC fonts. We don't use \let for the aliases, because +% sometimes we redefine the original macro, and the alias should reflect +% the redefinition. +% +% Use LaTeX names for the Icelandic letters. +\def\DH{{\ecfont \char"D0}} % Eth +\def\dh{{\ecfont \char"F0}} % eth +\def\TH{{\ecfont \char"DE}} % Thorn +\def\th{{\ecfont \char"FE}} % thorn +% +\def\guillemetleft{{\ecfont \char"13}} +\def\guillemotleft{\guillemetleft} +\def\guillemetright{{\ecfont \char"14}} +\def\guillemotright{\guillemetright} +\def\guilsinglleft{{\ecfont \char"0E}} +\def\guilsinglright{{\ecfont \char"0F}} +\def\quotedblbase{{\ecfont \char"12}} +\def\quotesinglbase{{\ecfont \char"0D}} +% +% This positioning is not perfect (see the ogonek LaTeX package), but +% we have the precomposed glyphs for the most common cases. We put the +% tests to use those glyphs in the single \ogonek macro so we have fewer +% dummy definitions to worry about for index entries, etc. +% +% ogonek is also used with other letters in Lithuanian (IOU), but using +% the precomposed glyphs for those is not so easy since they aren't in +% the same EC font. +\def\ogonek#1{{% + \def\temp{#1}% + \ifx\temp\macrocharA\Aogonek + \else\ifx\temp\macrochara\aogonek + \else\ifx\temp\macrocharE\Eogonek + \else\ifx\temp\macrochare\eogonek + \else + \ecfont \setbox0=\hbox{#1}% + \ifdim\ht0=1ex\accent"0C #1% + \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}% + \fi + \fi\fi\fi\fi + }% +} +\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A} +\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a} +\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E} +\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e} +% +% Use the European Computer Modern fonts (cm-super in outline format) +% for non-CM glyphs. That is ec* for regular text and tc* for the text +% companion symbols (LaTeX TS1 encoding). Both are part of the ec +% package and follow the same conventions. +% +\def\ecfont{\etcfont{e}} +\def\tcfont{\etcfont{t}} +% +\def\etcfont#1{% + % We can't distinguish serif/sans and italic/slanted, but this + % is used for crude hacks anyway (like adding French and German + % quotes to documents typeset with CM, where we lose kerning), so + % hopefully nobody will notice/care. + \edef\ecsize{\csname\curfontsize ecsize\endcsname}% + \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}% + \ifmonospace + % typewriter: + \font\thisecfont = #1ctt\ecsize \space at \nominalsize + \else + \ifx\curfontstyle\bfstylename + % bold: + \font\thisecfont = #1cb\ifusingit{i}{x}\ecsize \space at \nominalsize + \else + % regular: + \font\thisecfont = #1c\ifusingit{ti}{rm}\ecsize \space at \nominalsize + \fi + \fi + \thisecfont +} + +% @registeredsymbol - R in a circle. The font for the R should really +% be smaller yet, but lllsize is the best we can do for now. +% Adapted from the plain.tex definition of \copyright. +% +\def\registeredsymbol{% + $^{{\ooalign{\hfil\raise.07ex\hbox{\switchtolllsize R}% + \hfil\crcr\Orb}}% + }$% +} + +% @textdegree - the normal degrees sign. +% +\def\textdegree{$^\circ$} + +% Laurent Siebenmann reports \Orb undefined with: +% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 +% so we'll define it if necessary. +% +\ifx\Orb\thisisundefined +\def\Orb{\mathhexbox20D} +\fi + +% Quotes. +\chardef\quoteleft=`\` +\chardef\quoteright=`\' + +% only change font for tt for correct kerning and to avoid using +% \ecfont unless necessary. +\def\quotedblleft{% + \ifmonospace{\ecfont\char"10}\else{\char"5C}\fi +} + +\def\quotedblright{% + \ifmonospace{\ecfont\char"11}\else{\char`\"}\fi +} + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\newif\ifseenauthor +\newif\iffinishedtitlepage + +% @setcontentsaftertitlepage used to do an implicit @contents or +% @shortcontents after @end titlepage, but it is now obsolete. +\def\setcontentsaftertitlepage{% + \errmessage{@setcontentsaftertitlepage has been removed as a Texinfo + command; move your @contents command if you want the contents + after the title page.}}% +\def\setshortcontentsaftertitlepage{% + \errmessage{@setshortcontentsaftertitlepage has been removed as a Texinfo + command; move your @shortcontents and @contents commands if you + want the contents after the title page.}}% + +\parseargdef\shorttitlepage{% + \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\envdef\titlepage{% + % Open one extra group, as we want to close it in the middle of \Etitlepage. + \begingroup + \parindent=0pt \textfonts + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \let\page = \oldpage + \page + \null + }% +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % Need this before the \...aftertitlepage checks so that if they are + % in effect the toc pages will come out with page numbers. + \HEADINGSon +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +% Settings used for typesetting titles: no hyphenation, no indentation, +% don't worry much about spacing, ragged right. This should be used +% inside a \vbox, and fonts need to be set appropriately first. \par should +% be specified before the end of the \vbox, since a vbox is a group. +% +\def\raggedtitlesettings{% + \rm + \hyphenpenalty=10000 + \parindent=0pt + \tolerance=5000 + \ptexraggedright +} + +% Macros to be used within @titlepage: + +\let\subtitlerm=\rmfont +\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} + +\parseargdef\title{% + \checkenv\titlepage + \vbox{\titlefonts \raggedtitlesettings #1\par}% + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt +} + +\parseargdef\subtitle{% + \checkenv\titlepage + {\subtitlefont \rightline{#1}}% +} + +% @author should come last, but may come many times. +% It can also be used inside @quotation. +% +\parseargdef\author{% + \def\temp{\quotation}% + \ifx\thisenv\temp + \def\quotationauthor{#1}% printed in \Equotation. + \else + \checkenv\titlepage + \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi + {\secfonts\rm \leftline{#1}}% + \fi +} + + +% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks\evenheadline % headline on even pages +\newtoks\oddheadline % headline on odd pages +\newtoks\evenchapheadline% headline on even pages with a new chapter +\newtoks\oddchapheadline % headline on odd pages with a new chapter +\newtoks\evenfootline % footline on even pages +\newtoks\oddfootline % footline on odd pages + +% Now make \makeheadline and \makefootline in Plain TeX use those variables +\headline={{\textfonts\rm + \ifchapterpage + \ifodd\pageno\the\oddchapheadline\else\the\evenchapheadline\fi + \else + \ifodd\pageno\the\oddheadline\else\the\evenheadline\fi + \fi}} + +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + + +\def\evenheading{\parsearg\evenheadingxxx} +\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} +\def\evenheadingyyy #1\|#2\|#3\|#4\finish{% + \global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}} + \global\evenchapheadline=\evenheadline} + +\def\oddheading{\parsearg\oddheadingxxx} +\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} +\def\oddheadingyyy #1\|#2\|#3\|#4\finish{% + \global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}% + \global\oddchapheadline=\oddheadline} + +\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} +\def\evenfootingyyy #1\|#2\|#3\|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddfooting{\parsearg\oddfootingxxx} +\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} +\def\oddfootingyyy #1\|#2\|#3\|#4\finish{% + \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% + % + % Leave some space for the footline. Hopefully ok to assume + % @evenfooting will not be used by itself. + \global\advance\txipageheight by -12pt + \global\advance\vsize by -12pt +} + +\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} + +% @evenheadingmarks top \thischapter <- chapter at the top of a page +% @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page +% +% The same set of arguments for: +% +% @oddheadingmarks +% @evenfootingmarks +% @oddfootingmarks +% @everyheadingmarks +% @everyfootingmarks + +% These define \getoddheadingmarks, \getevenheadingmarks, +% \getoddfootingmarks, and \getevenfootingmarks, each to one of +% \gettopheadingmarks, \getbottomheadingmarks. +% +\def\evenheadingmarks{\headingmarks{even}{heading}} +\def\oddheadingmarks{\headingmarks{odd}{heading}} +\def\evenfootingmarks{\headingmarks{even}{footing}} +\def\oddfootingmarks{\headingmarks{odd}{footing}} +\parseargdef\everyheadingmarks{\headingmarks{even}{heading}{#1} + \headingmarks{odd}{heading}{#1} } +\parseargdef\everyfootingmarks{\headingmarks{even}{footing}{#1} + \headingmarks{odd}{footing}{#1} } +% #1 = even/odd, #2 = heading/footing, #3 = top/bottom. +\def\headingmarks#1#2#3 {% + \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname + \global\expandafter\let\csname get#1#2marks\endcsname \temp +} + +\everyheadingmarks bottom +\everyfootingmarks bottom + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\parseargdef\headings{\csname HEADINGS#1\endcsname} + +\def\headingsoff{% non-global headings elimination + \evenheadline={\hfil}\evenfootline={\hfil}\evenchapheadline={\hfil}% + \oddheadline={\hfil}\oddfootline={\hfil}\oddchapheadline={\hfil}% +} + +\def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting +\HEADINGSoff % it's the default + +% When we turn headings on, set the page number to 1. +\def\pageone{ + \global\pageno=1 + \global\arabiccount = \pagecount +} + +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{% +\pageone +\HEADINGSdoublex +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{% +\pageone +\HEADINGSsinglex +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\evenchapheadline={\line{\folio\hfil}} +\global\oddchapheadline={\line{\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\evenchapheadline={\line{\hfil\folio}} +\global\oddchapheadline={\line{\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% for @setchapternewpage off +\def\HEADINGSsinglechapoff{% +\pageone +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\evenchapheadline=\evenheadline +\global\oddchapheadline=\oddheadline +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% This produces Day Month Year style of output. +% Only define if not already defined, in case a txi-??.tex file has set +% up a different format (e.g., txi-cs.tex does this). +\ifx\today\thisisundefined +\def\today{% + \number\day\space + \ifcase\month + \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr + \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug + \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec + \fi + \space\number\year} +\fi + +% @settitle line... specifies the title of the document, for headings. +% It generates no output of its own. +\def\thistitle{\putwordNoTitle} +\def\settitle{\parsearg{\gdef\thistitle}} + + +\message{tables,} +% Tables -- @table, @ftable, @vtable, @item(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @ftable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemindicate{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil\relax + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. However, if + % what follows is an environment such as @example, there will be no + % \parskip glue; then the negative vskip we just inserted would + % cause the example and the item to crash together. So we use this + % bizarre value of 10001 as a signal to \aboveenvbreak to insert + % \parskip glue after all. Section titles are handled this way also. + % + \penalty 10001 + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. + \noindent + % Do this with kerns and \unhbox so that if there is a footnote in + % the item text, it can migrate to the main vertical list and + % eventually be printed. + \nobreak\kern-\tableindent + \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 + \unhbox0 + \nobreak\kern\dimen0 + \endgroup + \itemxneedsnegativevskiptrue + \fi +} + +\def\item{\errmessage{@item while not in a list environment}} +\def\itemx{\errmessage{@itemx while not in a list environment}} + +% @table, @ftable, @vtable. +\envdef\table{% + \let\itemindex\gobble + \tablecheck{table}% +} +\envdef\ftable{% + \def\itemindex ##1{\doind {fn}{\code{##1}}}% + \tablecheck{ftable}% +} +\envdef\vtable{% + \def\itemindex ##1{\doind {vr}{\code{##1}}}% + \tablecheck{vtable}% +} +\def\tablecheck#1{% + \ifnum \the\catcode`\^^M=\active + \endgroup + \errmessage{This command won't work in this context; perhaps the problem is + that we are \inenvironment\thisenv}% + \def\next{\doignore{#1}}% + \else + \let\next\tablex + \fi + \next +} +\def\tablex#1{% + \def\itemindicate{#1}% + \parsearg\tabley +} +\def\tabley#1{% + {% + \makevalueexpandable + \edef\temp{\noexpand\tablez #1\space\space\space}% + \expandafter + }\temp \endtablez +} +\def\tablez #1 #2 #3 #4\endtablez{% + \aboveenvbreak + \ifnum 0#1>0 \advance \leftskip by #1\mil \fi + \ifnum 0#2>0 \tableindent=#2\mil \fi + \ifnum 0#3>0 \advance \rightskip by #3\mil \fi + \itemmax=\tableindent + \advance \itemmax by -\itemmargin + \advance \leftskip by \tableindent + \exdentamount=\tableindent + \parindent = 0pt + \parskip = \smallskipamount + \ifdim \parskip=0pt \parskip=2pt \fi + \let\item = \internalBitem + \let\itemx = \internalBitemx +} +\def\Etable{\endgraf\afterenvbreak} +\let\Eftable\Etable +\let\Evtable\Etable +\let\Eitemize\Etable +\let\Eenumerate\Etable + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\envdef\itemize{\parsearg\doitemize} + +\def\doitemize#1{% + \aboveenvbreak + \itemmax=\itemindent + \advance\itemmax by -\itemmargin + \advance\leftskip by \itemindent + \exdentamount=\itemindent + \parindent=0pt + \parskip=\smallskipamount + \ifdim\parskip=0pt \parskip=2pt \fi + % + % Try typesetting the item mark so that if the document erroneously says + % something like @itemize @samp (intending @table), there's an error + % right away at the @itemize. It's not the best error message in the + % world, but it's better than leaving it to the @item. This means if + % the user wants an empty mark, they have to say @w{} not just @w. + \def\itemcontents{#1}% + \setbox0 = \hbox{\itemcontents}% + % + % @itemize with no arg is equivalent to @itemize @bullet. + \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi + % + \let\item=\itemizeitem +} + +% Definition of @item while inside @itemize and @enumerate. +% +\def\itemizeitem{% + \advance\itemno by 1 % for enumerations + {\let\par=\endgraf \smallbreak}% reasonable place to break + {% + % If the document has an @itemize directly after a section title, a + % \nobreak will be last on the list, and \sectionheading will have + % done a \vskip-\parskip. In that case, we don't want to zero + % parskip, or the item text will crash with the heading. On the + % other hand, when there is normal text preceding the item (as there + % usually is), we do want to zero parskip, or there would be too much + % space. In that case, we won't have a \nobreak before. At least + % that's the theory. + \ifnum\lastpenalty<10000 \parskip=0in \fi + \noindent + \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% + % + \ifinner\else + \vadjust{\penalty 1200}% not good to break after first line of item. + \fi + % We can be in inner vertical mode in a footnote, although an + % @itemize looks awful there. + }% + \flushcr +} + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\envparseargdef\enumerate{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a <number>. + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call \doitemize, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \doitemize{#1.}\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab do not need to be on their own lines, but it will not hurt +% if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. +% +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +% Macros used to set up halign preamble: +% +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +% #1 is the @columnfraction, usually a decimal number like .5, but might +% be just 1. We just use it, whatever it is. +% +\def\pickupwholefraction#1 {% + \global\advance\colcount by 1 + \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% + \setuptable +} + +\newcount\colcount +\def\setuptable#1{% + \def\firstarg{#1}% + \ifx\firstarg\xendsetuptable + \let\go = \relax + \else + \ifx\firstarg\xcolumnfractions + \global\setpercenttrue + \else + \ifsetpercent + \let\go\pickupwholefraction + \else + \global\advance\colcount by 1 + \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a + % separator; typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi + \fi + \ifx\go\pickupwholefraction + % Put the argument back for the \pickupwholefraction call, so + % we'll always have a period there to be parsed. + \def\go{\pickupwholefraction#1}% + \else + \let\go = \setuptable + \fi% + \fi + \go +} + +% multitable-only commands. +% +% @headitem starts a heading row, which we typeset in bold. Assignments +% have to be global since we are inside the implicit group of an +% alignment entry. \everycr below resets \everytab so we don't have to +% undo it ourselves. +\def\headitemfont{\b}% for people to use in the template row; not changeable +\def\headitem{% + \checkenv\multitable + \crcr + \gdef\headitemcrhook{\nobreak}% attempt to avoid page break after headings + \global\everytab={\bf}% can't use \headitemfont since the parsing differs + \the\everytab % for the first item +}% +% +% default for tables with no headings. +\let\headitemcrhook=\relax +% +% A \tab used to include \hskip1sp. But then the space in a template +% line is not enough. That is bad. So let's go back to just `&' until +% we again encounter the problem the 1sp was intended to solve. +% --karl, nathan@acm.org, 20apr99. +\def\tab{\checkenv\multitable &\the\everytab}% + +% @multitable ... @end multitable definitions: +% +\newtoks\everytab % insert after every tab. +% +\envdef\multitable{% + \vskip\parskip + \startsavinginserts + % + % @item within a multitable starts a normal row. + % We use \def instead of \let so that if one of the multitable entries + % contains an @itemize, we don't choke on the \item (seen as \crcr aka + % \endtemplate) expanding \doitemize. + \def\item{\crcr}% + % + \tolerance=9500 + \hbadness=9500 + \setmultitablespacing + \parskip=\multitableparskip + \parindent=\multitableparindent + \overfullrule=0pt + \global\colcount=0 + % + \everycr = {% + \noalign{% + \global\everytab={}% Reset from possible headitem. + \global\colcount=0 % Reset the column counter. + % + % Check for saved footnotes, etc.: + \checkinserts + % + % Perhaps a \nobreak, then reset: + \headitemcrhook + \global\let\headitemcrhook=\relax + }% + }% + % + \parsearg\domultitable +} +\def\domultitable#1{% + % To parse everything between @multitable and @item: + \setuptable#1 \endsetuptable + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. + \halign\bgroup &% + \global\advance\colcount by 1 + \multistrut + \vtop{% + % Use the current \colcount to find the correct column width: + \hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively + % marking characters. + \noindent\ignorespaces##\unskip\multistrut + }\cr +} +\def\Emultitable{% + \crcr + \egroup % end the \halign + \global\setpercentfalse +} + +\def\setmultitablespacing{% + \def\multistrut{\strut}% just use the standard line spacing + % + % Compute \multitablelinespace (if not defined by user) for use in + % \multitableparskip calculation. We used define \multistrut based on + % this, but (ironically) that caused the spacing to be off. + % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. +\ifdim\multitablelinespace=0pt +\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip +\global\advance\multitablelinespace by-\ht0 +\fi +% Test to see if parskip is larger than space between lines of +% table. If not, do nothing. +% If so, set to same dimension as multitablelinespace. +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller + % than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller + % than skip between lines in the table. +\fi} + + +\message{conditionals,} + +% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, +% @ifnotxml always succeed. They currently do nothing; we don't +% attempt to check whether the conditionals are properly nested. But we +% have to remember that they are conditionals, so that @end doesn't +% attempt to close an environment group. +% +\def\makecond#1{% + \expandafter\let\csname #1\endcsname = \relax + \expandafter\let\csname iscond.#1\endcsname = 1 +} +\makecond{iftex} +\makecond{ifnotdocbook} +\makecond{ifnothtml} +\makecond{ifnotinfo} +\makecond{ifnotplaintext} +\makecond{ifnotxml} + +% Ignore @ignore, @ifhtml, @ifinfo, and the like. +% +\def\direntry{\doignore{direntry}} +\def\documentdescription{\doignore{documentdescription}} +\def\docbook{\doignore{docbook}} +\def\html{\doignore{html}} +\def\ifdocbook{\doignore{ifdocbook}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifinfo{\doignore{ifinfo}} +\def\ifnottex{\doignore{ifnottex}} +\def\ifplaintext{\doignore{ifplaintext}} +\def\ifxml{\doignore{ifxml}} +\def\ignore{\doignore{ignore}} +\def\menu{\doignore{menu}} +\def\xml{\doignore{xml}} + +% Ignore text until a line `@end #1', keeping track of nested conditionals. +% +% A count to remember the depth of nesting. +\newcount\doignorecount + +\def\doignore#1{\begingroup + % Scan in ``verbatim'' mode: + \obeylines + \catcode`\@ = \other + \catcode`\{ = \other + \catcode`\} = \other + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \spaceisspace + % + % Count number of #1's that we've seen. + \doignorecount = 0 + % + % Swallow text until we reach the matching `@end #1'. + \dodoignore{#1}% +} + +{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. + \obeylines % + % + \gdef\dodoignore#1{% + % #1 contains the command name as a string, e.g., `ifinfo'. + % + % Define a command to find the next `@end #1'. + \long\def\doignoretext##1^^M@end #1{% + \doignoretextyyy##1^^M@#1\_STOP_}% + % + % And this command to find another #1 command, at the beginning of a + % line. (Otherwise, we would consider a line `@c @ifset', for + % example, to count as an @ifset for nesting.) + \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% + % + % And now expand that command. + \doignoretext ^^M% + }% +} + +\def\doignoreyyy#1{% + \def\temp{#1}% + \ifx\temp\empty % Nothing found. + \let\next\doignoretextzzz + \else % Found a nested condition, ... + \advance\doignorecount by 1 + \let\next\doignoretextyyy % ..., look for another. + % If we're here, #1 ends with ^^M\ifinfo (for example). + \fi + \next #1% the token \_STOP_ is present just after this macro. +} + +% We have to swallow the remaining "\_STOP_". +% +\def\doignoretextzzz#1{% + \ifnum\doignorecount = 0 % We have just found the outermost @end. + \let\next\enddoignore + \else % Still inside a nested condition. + \advance\doignorecount by -1 + \let\next\doignoretext % Look for the next @end. + \fi + \next +} + +% Finish off ignored text. +{ \obeylines% + % Ignore anything after the last `@end #1'; this matters in verbatim + % environments, where otherwise the newline after an ignored conditional + % would result in a blank line in the output. + \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% +} + + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. +% We rely on the fact that \parsearg sets \catcode`\ =10. +% +\parseargdef\set{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + {% + \makevalueexpandable + \def\temp{#2}% + \edef\next{\gdef\makecsname{SET#1}}% + \ifx\temp\empty + \next{}% + \else + \setzzz#2\endsetzzz + \fi + }% +} +% Remove the trailing space \setxxx inserted. +\def\setzzz#1 \endsetzzz{\next{#1}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\parseargdef\clear{% + {% + \makevalueexpandable + \global\expandafter\let\csname SET#1\endcsname=\relax + }% +} + +% @value{foo} gets the text saved in variable foo. +\def\value{\begingroup\makevalueexpandable\valuexxx} +\def\valuexxx#1{\expandablevalue{#1}\endgroup} +{ + \catcode`\-=\active \catcode`\_=\active + % + \gdef\makevalueexpandable{% + \let\value = \expandablevalue + % We don't want these characters active, ... + \catcode`\-=\other \catcode`\_=\other + % ..., but we might end up with active ones in the argument if + % we're called from @code, as @code{@value{foo-bar_}}, though. + % So \let them to their normal equivalents. + \let-\normaldash \let_\normalunderscore + } +} + +\def\expandablevalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {[No value for ``#1'']}% + \message{Variable `#1', used in @value, is not set.}% + \else + \csname SET#1\endcsname + \fi +} + +% Like \expandablevalue, but completely expandable (the \message in the +% definition above operates at the execution level of TeX). Used when +% writing to auxiliary files, due to the expansion that \write does. +% If flag is undefined, pass through an unexpanded @value command: maybe it +% will be set by the time it is read back in. +% +% NB flag names containing - or _ may not work here. +\def\dummyvalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \string\value{#1}% + \else + \csname SET#1\endcsname + \fi +} + +% Used for @value's in index entries to form the sort key: expand the @value +% if possible, otherwise sort late. +\def\indexnofontsvalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + ZZZZZZZ% + \else + \csname SET#1\endcsname + \fi +} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +% To get the special treatment we need for `@end ifset,' we call +% \makecond and then redefine. +% +\makecond{ifset} +\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} +\def\doifset#1#2{% + {% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname SET#2\endcsname\relax + #1% If not set, redefine \next. + \fi + \expandafter + }\next +} +\def\ifsetfail{\doignore{ifset}} + +% @ifclear VAR ... @end executes the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +% The `\else' inside the `\doifset' parameter is a trick to reuse the +% above code: if the variable is not set, do nothing, if it is set, +% then redefine \next to \ifclearfail. +% +\makecond{ifclear} +\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} +\def\ifclearfail{\doignore{ifclear}} + +% @ifcommandisdefined CMD ... @end executes the `...' if CMD (written +% without the @) is in fact defined. We can only feasibly check at the +% TeX level, so something like `mathcode' is going to considered +% defined even though it is not a Texinfo command. +% +\makecond{ifcommanddefined} +\def\ifcommanddefined{\parsearg{\doifcmddefined{\let\next=\ifcmddefinedfail}}} +% +\def\doifcmddefined#1#2{{% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname #2\endcsname\relax + #1% If not defined, \let\next as above. + \fi + \expandafter + }\next +} +\def\ifcmddefinedfail{\doignore{ifcommanddefined}} + +% @ifcommandnotdefined CMD ... handled similar to @ifclear above. +\makecond{ifcommandnotdefined} +\def\ifcommandnotdefined{% + \parsearg{\doifcmddefined{\else \let\next=\ifcmdnotdefinedfail}}} +\def\ifcmdnotdefinedfail{\doignore{ifcommandnotdefined}} + +% Set the `txicommandconditionals' variable, so documents have a way to +% test if the @ifcommand...defined conditionals are available. +\set txicommandconditionals + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory=\comment + +% @defininfoenclose. +\let\definfoenclose=\comment + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within macros and \if's. +\edef\newwrite{\makecsname{ptexnewwrite}} + +% \newindex {foo} defines an index named IX. +% It automatically defines \IXindex such that +% \IXindex ...rest of line... puts an entry in the index IX. +% It also defines \IXindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is IX. +% The name of an index should be no more than 2 characters long +% for the sake of vms. +% +\def\newindex#1{% + \expandafter\chardef\csname#1indfile\endcsname=0 + \expandafter\xdef\csname#1index\endcsname{% % Define @#1index + \noexpand\doindex{#1}} +} + +% @defindex foo == \newindex{foo} +% +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. +% +\def\defcodeindex{\parsearg\newcodeindex} +% +\def\newcodeindex#1{% + \expandafter\chardef\csname#1indfile\endcsname=0 + \expandafter\xdef\csname#1index\endcsname{% + \noexpand\docodeindex{#1}}% +} + +% The default indices: +\newindex{cp}% concepts, +\newcodeindex{fn}% functions, +\newcodeindex{vr}% variables, +\newcodeindex{tp}% types, +\newcodeindex{ky}% keys +\newcodeindex{pg}% and programs. + + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +% +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +% +\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} +\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} + +% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), +% #3 the target index (bar). +\def\dosynindex#1#2#3{% + \requireopenindexfile{#3}% + % redefine \fooindfile: + \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname + \expandafter\let\csname#2indfile\endcsname=\temp + % redefine \fooindex: + \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% +} + +% Define \doindex, the driver for all index macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is the two-letter name of the index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\doindexxxx} +\def\doindexxxx #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\docodeindexxxx} +\def\docodeindexxxx #1{\docind{\indexname}{#1}} + + +% Used for the aux, toc and index files to prevent expansion of Texinfo +% commands. +% +\def\atdummies{% + \definedummyletter\@% + \definedummyletter\ % + \definedummyletter\{% + \definedummyletter\}% + \definedummyletter\&% + % + % Do the redefinitions. + \definedummies + \otherbackslash +} + +% \definedummyword defines \#1 as \string\#1\space, thus effectively +% preventing its expansion. This is used only for control words, +% not control letters, because the \space would be incorrect for +% control characters, but is needed to separate the control word +% from whatever follows. +% +% These can be used both for control words that take an argument and +% those that do not. If it is followed by {arg} in the input, then +% that will dutifully get written to the index (or wherever). +% +% For control letters, we have \definedummyletter, which omits the +% space. +% +\def\definedummyword #1{\def#1{\string#1\space}}% +\def\definedummyletter#1{\def#1{\string#1}}% +\let\definedummyaccent\definedummyletter + +% Called from \atdummies to prevent the expansion of commands. +% +\def\definedummies{% + % + \let\commondummyword\definedummyword + \let\commondummyletter\definedummyletter + \let\commondummyaccent\definedummyaccent + \commondummiesnofonts + % + \definedummyletter\_% + \definedummyletter\-% + % + % Non-English letters. + \definedummyword\AA + \definedummyword\AE + \definedummyword\DH + \definedummyword\L + \definedummyword\O + \definedummyword\OE + \definedummyword\TH + \definedummyword\aa + \definedummyword\ae + \definedummyword\dh + \definedummyword\exclamdown + \definedummyword\l + \definedummyword\o + \definedummyword\oe + \definedummyword\ordf + \definedummyword\ordm + \definedummyword\questiondown + \definedummyword\ss + \definedummyword\th + % + % Although these internal commands shouldn't show up, sometimes they do. + \definedummyword\bf + \definedummyword\gtr + \definedummyword\hat + \definedummyword\less + \definedummyword\sf + \definedummyword\sl + \definedummyword\tclose + \definedummyword\tt + % + \definedummyword\LaTeX + \definedummyword\TeX + % + % Assorted special characters. + \definedummyword\ampchar + \definedummyword\atchar + \definedummyword\arrow + \definedummyword\backslashchar + \definedummyword\bullet + \definedummyword\comma + \definedummyword\copyright + \definedummyword\registeredsymbol + \definedummyword\dots + \definedummyword\enddots + \definedummyword\entrybreak + \definedummyword\equiv + \definedummyword\error + \definedummyword\euro + \definedummyword\expansion + \definedummyword\geq + \definedummyword\guillemetleft + \definedummyword\guillemetright + \definedummyword\guilsinglleft + \definedummyword\guilsinglright + \definedummyword\lbracechar + \definedummyword\leq + \definedummyword\mathopsup + \definedummyword\minus + \definedummyword\ogonek + \definedummyword\pounds + \definedummyword\point + \definedummyword\print + \definedummyword\quotedblbase + \definedummyword\quotedblleft + \definedummyword\quotedblright + \definedummyword\quoteleft + \definedummyword\quoteright + \definedummyword\quotesinglbase + \definedummyword\rbracechar + \definedummyword\result + \definedummyword\sub + \definedummyword\sup + \definedummyword\textdegree + % + \definedummyword\subentry + % + % We want to disable all macros so that they are not expanded by \write. + \macrolist + \let\value\dummyvalue + % + \normalturnoffactive +} + +% \commondummiesnofonts: common to \definedummies and \indexnofonts. +% Define \commondummyletter, \commondummyaccent and \commondummyword before +% using. Used for accents, font commands, and various control letters. +% +\def\commondummiesnofonts{% + % Control letters and accents. + \commondummyletter\!% + \commondummyaccent\"% + \commondummyaccent\'% + \commondummyletter\*% + \commondummyaccent\,% + \commondummyletter\.% + \commondummyletter\/% + \commondummyletter\:% + \commondummyaccent\=% + \commondummyletter\?% + \commondummyaccent\^% + \commondummyaccent\`% + \commondummyaccent\~% + \commondummyword\u + \commondummyword\v + \commondummyword\H + \commondummyword\dotaccent + \commondummyword\ogonek + \commondummyword\ringaccent + \commondummyword\tieaccent + \commondummyword\ubaraccent + \commondummyword\udotaccent + \commondummyword\dotless + % + % Texinfo font commands. + \commondummyword\b + \commondummyword\i + \commondummyword\r + \commondummyword\sansserif + \commondummyword\sc + \commondummyword\slanted + \commondummyword\t + % + % Commands that take arguments. + \commondummyword\abbr + \commondummyword\acronym + \commondummyword\anchor + \commondummyword\cite + \commondummyword\code + \commondummyword\command + \commondummyword\dfn + \commondummyword\dmn + \commondummyword\email + \commondummyword\emph + \commondummyword\env + \commondummyword\file + \commondummyword\image + \commondummyword\indicateurl + \commondummyword\inforef + \commondummyword\kbd + \commondummyword\key + \commondummyword\math + \commondummyword\option + \commondummyword\pxref + \commondummyword\ref + \commondummyword\samp + \commondummyword\strong + \commondummyword\tie + \commondummyword\U + \commondummyword\uref + \commondummyword\url + \commondummyword\var + \commondummyword\verb + \commondummyword\w + \commondummyword\xref +} + +\let\indexlbrace\relax +\let\indexrbrace\relax +\let\indexatchar\relax +\let\indexbackslash\relax + +{\catcode`\@=0 +\catcode`\\=13 + @gdef@backslashdisappear{@def\{}} +} + +{ +\catcode`\<=13 +\catcode`\-=13 +\catcode`\`=13 + \gdef\indexnonalnumdisappear{% + \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax\else + % @set txiindexlquoteignore makes us ignore left quotes in the sort term. + % (Introduced for FSFS 2nd ed.) + \let`=\empty + \fi + % + \expandafter\ifx\csname SETtxiindexbackslashignore\endcsname\relax\else + \backslashdisappear + \fi + % + \expandafter\ifx\csname SETtxiindexhyphenignore\endcsname\relax\else + \def-{}% + \fi + \expandafter\ifx\csname SETtxiindexlessthanignore\endcsname\relax\else + \def<{}% + \fi + \expandafter\ifx\csname SETtxiindexatsignignore\endcsname\relax\else + \def\@{}% + \fi + } + + \gdef\indexnonalnumreappear{% + \let-\normaldash + \let<\normalless + } +} + + +% \indexnofonts is used when outputting the strings to sort the index +% by, and when constructing control sequence names. It eliminates all +% control sequences and just writes whatever the best ASCII sort string +% would be for a given command (usually its argument). +% +\def\indexnofonts{% + % Accent commands should become @asis. + \def\commondummyaccent##1{\let##1\asis}% + % We can just ignore other control letters. + \def\commondummyletter##1{\let##1\empty}% + % All control words become @asis by default; overrides below. + \let\commondummyword\commondummyaccent + \commondummiesnofonts + % + % Don't no-op \tt, since it isn't a user-level command + % and is used in the definitions of the active chars like <, >, |, etc. + % Likewise with the other plain tex font commands. + %\let\tt=\asis + % + \def\ { }% + \def\@{@}% + \def\_{\normalunderscore}% + \def\-{}% @- shouldn't affect sorting + % + \uccode`\1=`\{ \uppercase{\def\{{1}}% + \uccode`\1=`\} \uppercase{\def\}{1}}% + \let\lbracechar\{% + \let\rbracechar\}% + % + % + \let\do\indexnofontsdef + % + % Non-English letters. + \do\AA{AA}% + \do\AE{AE}% + \do\DH{DZZ}% + \do\L{L}% + \do\OE{OE}% + \do\O{O}% + \do\TH{TH}% + \do\aa{aa}% + \do\ae{ae}% + \do\dh{dzz}% + \do\exclamdown{!}% + \do\l{l}% + \do\oe{oe}% + \do\ordf{a}% + \do\ordm{o}% + \do\o{o}% + \do\questiondown{?}% + \do\ss{ss}% + \do\th{th}% + % + \do\LaTeX{LaTeX}% + \do\TeX{TeX}% + % + % Assorted special characters. + \do\atchar{@}% + \do\arrow{->}% + \do\bullet{bullet}% + \do\comma{,}% + \do\copyright{copyright}% + \do\dots{...}% + \do\enddots{...}% + \do\equiv{==}% + \do\error{error}% + \do\euro{euro}% + \do\expansion{==>}% + \do\geq{>=}% + \do\guillemetleft{<<}% + \do\guillemetright{>>}% + \do\guilsinglleft{<}% + \do\guilsinglright{>}% + \do\leq{<=}% + \do\lbracechar{\{}% + \do\minus{-}% + \do\point{.}% + \do\pounds{pounds}% + \do\print{-|}% + \do\quotedblbase{"}% + \do\quotedblleft{"}% + \do\quotedblright{"}% + \do\quoteleft{`}% + \do\quoteright{'}% + \do\quotesinglbase{,}% + \do\rbracechar{\}}% + \do\registeredsymbol{R}% + \do\result{=>}% + \do\textdegree{o}% + % + % We need to get rid of all macros, leaving only the arguments (if present). + % Of course this is not nearly correct, but it is the best we can do for now. + % makeinfo does not expand macros in the argument to @deffn, which ends up + % writing an index entry, and texindex isn't prepared for an index sort entry + % that starts with \. + % + % Since macro invocations are followed by braces, we can just redefine them + % to take a single TeX argument. The case of a macro invocation that + % goes to end-of-line is not handled. + % + \macrolist + \let\value\indexnofontsvalue +} + +% Give the control sequence a definition that removes the {} that follows +% its use, e.g. @AA{} -> AA +\def\indexnofontsdef#1#2{\def#1##1{#2}}% + + + + +% #1 is the index name, #2 is the entry text. +\def\doind#1#2{% + \iflinks + {% + % + \requireopenindexfile{#1}% + \edef\writeto{\csname#1indfile\endcsname}% + % + \def\indextext{#2}% + \safewhatsit\doindwrite + }% + \fi +} + +% Same as \doind, but for code indices +\def\docind#1#2{% + \iflinks + {% + % + \requireopenindexfile{#1}% + \edef\writeto{\csname#1indfile\endcsname}% + % + \def\indextext{#2}% + \safewhatsit\docindwrite + }% + \fi +} + +% Check if an index file has been opened, and if not, open it. +\def\requireopenindexfile#1{% +\ifnum\csname #1indfile\endcsname=0 + \expandafter\newwrite \csname#1indfile\endcsname + \edef\suffix{#1}% + % A .fls suffix would conflict with the file extension for the output + % of -recorder, so use .f1s instead. + \ifx\suffix\indexisfl\def\suffix{f1}\fi + % Open the file + \immediate\openout\csname#1indfile\endcsname \jobname.\suffix + % Using \immediate above here prevents an object entering into the current + % box, which could confound checks such as those in \safewhatsit for + % preceding skips. + \typeout{Writing index file \jobname.\suffix}% +\fi} +\def\indexisfl{fl} + +% Definition for writing index entry sort key. +{ +\catcode`\-=13 +\gdef\indexwritesortas{% + \begingroup + \indexnonalnumreappear + \indexwritesortasxxx} +\gdef\indexwritesortasxxx#1{% + \xdef\indexsortkey{#1}\endgroup} +} + +\def\indexwriteseealso#1{ + \gdef\pagenumbertext{\string\seealso{#1}}% +} +\def\indexwriteseeentry#1{ + \gdef\pagenumbertext{\string\seeentry{#1}}% +} + +% The default definitions +\def\sortas#1{}% +\def\seealso#1{\i{\putwordSeeAlso}\ #1}% for sorted index file only +\def\putwordSeeAlso{See also} +\def\seeentry#1{\i{\putwordSee}\ #1}% for sorted index file only + + +% Given index entry text like "aaa @subentry bbb @sortas{ZZZ}": +% * Set \bracedtext to "{aaa}{bbb}" +% * Set \fullindexsortkey to "aaa @subentry ZZZ" +% * If @seealso occurs, set \pagenumbertext +% +\def\splitindexentry#1{% + \gdef\fullindexsortkey{}% + \xdef\bracedtext{}% + \def\sep{}% + \def\seealso##1{}% + \def\seeentry##1{}% + \expandafter\doindexsegment#1\subentry\finish\subentry +} + +% append the results from the next segment +\def\doindexsegment#1\subentry{% + \def\segment{#1}% + \ifx\segment\isfinish + \else + % + % Fully expand the segment, throwing away any @sortas directives, and + % trim spaces. + \edef\trimmed{\segment}% + \edef\trimmed{\expandafter\eatspaces\expandafter{\trimmed}}% + \ifincodeindex + \edef\trimmed{\noexpand\code{\trimmed}}% + \fi + % + \xdef\bracedtext{\bracedtext{\trimmed}}% + % + % Get the string to sort by. Process the segment with all + % font commands turned off. + \bgroup + \let\sortas\indexwritesortas + \let\seealso\indexwriteseealso + \let\seeentry\indexwriteseeentry + \indexnofonts + % The braces around the commands are recognized by texindex. + \def\lbracechar{{\string\indexlbrace}}% + \def\rbracechar{{\string\indexrbrace}}% + \let\{=\lbracechar + \let\}=\rbracechar + \def\@{{\string\indexatchar}}% + \def\atchar##1{\@}% + \def\backslashchar{{\string\indexbackslash}}% + \uccode`\~=`\\ \uppercase{\let~\backslashchar}% + % + \let\indexsortkey\empty + \global\let\pagenumbertext\empty + % Execute the segment and throw away the typeset output. This executes + % any @sortas or @seealso commands in this segment. + \setbox\dummybox = \hbox{\segment}% + \ifx\indexsortkey\empty{% + \indexnonalnumdisappear + \xdef\trimmed{\segment}% + \xdef\trimmed{\expandafter\eatspaces\expandafter{\trimmed}}% + \xdef\indexsortkey{\trimmed}% + \ifx\indexsortkey\empty\xdef\indexsortkey{ }\fi + }\fi + % + % Append to \fullindexsortkey. + \edef\tmp{\gdef\noexpand\fullindexsortkey{% + \fullindexsortkey\sep\indexsortkey}}% + \tmp + \egroup + \def\sep{\subentry}% + % + \expandafter\doindexsegment + \fi +} +\def\isfinish{\finish}% +\newbox\dummybox % used above + +\let\subentry\relax + +% Use \ instead of @ in index files. To support old texi2dvi and texindex. +% This works without changing the escape character used in the toc or aux +% files because the index entries are fully expanded here, and \string uses +% the current value of \escapechar. +\def\escapeisbackslash{\escapechar=`\\} + +% Use \ in index files by default. texi2dvi didn't support @ as the escape +% character (as it checked for "\entry" in the files, and not "@entry"). When +% the new version of texi2dvi has had a chance to become more prevalent, then +% the escape character can change back to @ again. This should be an easy +% change to make now because both @ and \ are only used as escape characters in +% index files, never standing for themselves. +% +\set txiindexescapeisbackslash + +% Write the entry in \indextext to the index file. +% + +\newif\ifincodeindex +\def\doindwrite{\incodeindexfalse\doindwritex} +\def\docindwrite{\incodeindextrue\doindwritex} + +\def\doindwritex{% + \maybemarginindex + % + \atdummies + % + \expandafter\ifx\csname SETtxiindexescapeisbackslash\endcsname\relax\else + \escapeisbackslash + \fi + % + % For texindex which always views { and } as separators. + \def\{{\lbracechar{}}% + \def\}{\rbracechar{}}% + \uccode`\~=`\\ \uppercase{\def~{\backslashchar{}}}% + % + % Split the entry into primary entry and any subentries, and get the index + % sort key. + \splitindexentry\indextext + % + % Set up the complete index entry, with both the sort key and + % the original text, including any font commands. We write + % three arguments to \entry to the .?? file (four in the + % subentry case), texindex reduces to two when writing the .??s + % sorted result. + % + \edef\temp{% + \write\writeto{% + \string\entry{\fullindexsortkey}% + {\ifx\pagenumbertext\empty\noexpand\folio\else\pagenumbertext\fi}% + \bracedtext}% + }% + \temp +} + +% Put the index entry in the margin if desired (undocumented). +\def\maybemarginindex{% + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \relax\indextext}}% + \fi +} +\let\SETmarginindex=\relax + + +% Take care of unwanted page breaks/skips around a whatsit: +% +% If a skip is the last thing on the list now, preserve it +% by backing up by \lastskip, doing the \write, then inserting +% the skip again. Otherwise, the whatsit generated by the +% \write or \pdfdest will make \lastskip zero. The result is that +% sequences like this: +% @end defun +% @tindex whatever +% @defun ... +% will have extra space inserted, because the \medbreak in the +% start of the @defun won't see the skip inserted by the @end of +% the previous defun. +% +% But don't do any of this if we're not in vertical mode. We +% don't want to do a \vskip and prematurely end a paragraph. +% +% Avoid page breaks due to these extra skips, too. +% +% But wait, there is a catch there: +% We'll have to check whether \lastskip is zero skip. \ifdim is not +% sufficient for this purpose, as it ignores stretch and shrink parts +% of the skip. The only way seems to be to check the textual +% representation of the skip. +% +% The following is almost like \def\zeroskipmacro{0.0pt} except that +% the ``p'' and ``t'' characters have catcode \other, not 11 (letter). +% +\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} +% +\newskip\whatsitskip +\newcount\whatsitpenalty +% +% ..., ready, GO: +% +\def\safewhatsit#1{\ifhmode + #1% + \else + % \lastskip and \lastpenalty cannot both be nonzero simultaneously. + \whatsitskip = \lastskip + \edef\lastskipmacro{\the\lastskip}% + \whatsitpenalty = \lastpenalty + % + % If \lastskip is nonzero, that means the last item was a + % skip. And since a skip is discardable, that means this + % -\whatsitskip glue we're inserting is preceded by a + % non-discardable item, therefore it is not a potential + % breakpoint, therefore no \nobreak needed. + \ifx\lastskipmacro\zeroskipmacro + \else + \vskip-\whatsitskip + \fi + % + #1% + % + \ifx\lastskipmacro\zeroskipmacro + % If \lastskip was zero, perhaps the last item was a penalty, and + % perhaps it was >=10000, e.g., a \nobreak. In that case, we want + % to re-insert the same penalty (values >10000 are used for various + % signals); since we just inserted a non-discardable item, any + % following glue (such as a \parskip) would be a breakpoint. For example: + % @deffn deffn-whatever + % @vindex index-whatever + % Description. + % would allow a break between the index-whatever whatsit + % and the "Description." paragraph. + \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi + \else + % On the other hand, if we had a nonzero \lastskip, + % this make-up glue would be preceded by a non-discardable item + % (the whatsit from the \write), so we must insert a \nobreak. + \nobreak\vskip\whatsitskip + \fi +\fi} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% \entry {topic}{} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. +% \secondary {subtopic}{} +% for a subtopic with sub-subtopics +% \tertiary {subtopic}{subsubtopic}{pagelist} +% for each sub-subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +% Define the macros used in formatting output of the sorted index material. + +% @printindex causes a particular index (the ??s file) to get printed. +% It does not print any chapter heading (usually an @unnumbered). +% +\parseargdef\printindex{\begingroup + \dobreak \chapheadingskip{10000}% + % + \smallfonts \rm + \tolerance = 9500 + \plainfrenchspacing + \everypar = {}% don't want the \kern\-parindent from indentation suppression. + % + % See comment in \requireopenindexfile. + \def\indexname{#1}\ifx\indexname\indexisfl\def\indexname{f1}\fi + % + % See if the index file exists and is nonempty. + \openin 1 \jobname.\indexname s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + \putwordIndexNonexistent + \typeout{No file \jobname.\indexname s.}% + \else + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \thisline + \ifeof 1 + \putwordIndexIsEmpty + \else + \expandafter\printindexzz\thisline\relax\relax\finish% + \fi + \fi + \closein 1 +\endgroup} + +% If the index file starts with a backslash, forgo reading the index +% file altogether. If somebody upgrades texinfo.tex they may still have +% old index files using \ as the escape character. Reading this would +% at best lead to typesetting garbage, at worst a TeX syntax error. +\def\printindexzz#1#2\finish{% + \expandafter\ifx\csname SETtxiindexescapeisbackslash\endcsname\relax + \uccode`\~=`\\ \uppercase{\if\noexpand~}\noexpand#1 + \expandafter\ifx\csname SETtxiskipindexfileswithbackslash\endcsname\relax +\errmessage{% +ERROR: A sorted index file in an obsolete format was skipped. +To fix this problem, please upgrade your version of 'texi2dvi' +or 'texi2pdf' to that at <https://ftp.gnu.org/gnu/texinfo>. +If you are using an old version of 'texindex' (part of the Texinfo +distribution), you may also need to upgrade to a newer version (at least 6.0). +You may be able to typeset the index if you run +'texindex \jobname.\indexname' yourself. +You could also try setting the 'txiindexescapeisbackslash' flag by +running a command like +'texi2dvi -t "@set txiindexescapeisbackslash" \jobname.texi'. If you do +this, Texinfo will try to use index files in the old format. +If you continue to have problems, deleting the index files and starting again +might help (with 'rm \jobname.?? \jobname.??s')% +}% + \else + (Skipped sorted index file in obsolete format) + \fi + \else + \begindoublecolumns + \input \jobname.\indexname s + \enddoublecolumns + \fi + \else + \begindoublecolumns + \catcode`\\=0\relax + % + % Make @ an escape character to give macros a chance to work. This + % should work because we (hopefully) don't otherwise use @ in index files. + %\catcode`\@=12\relax + \catcode`\@=0\relax + \input \jobname.\indexname s + \enddoublecolumns + \fi +} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +{\catcode`\/=13 \catcode`\-=13 \catcode`\^=13 \catcode`\~=13 \catcode`\_=13 +\catcode`\|=13 \catcode`\<=13 \catcode`\>=13 \catcode`\+=13 \catcode`\"=13 +\catcode`\$=3 +\gdef\initialglyphs{% + % special control sequences used in the index sort key + \let\indexlbrace\{% + \let\indexrbrace\}% + \let\indexatchar\@% + \def\indexbackslash{\math{\backslash}}% + % + % Some changes for non-alphabetic characters. Using the glyphs from the + % math fonts looks more consistent than the typewriter font used elsewhere + % for these characters. + \uccode`\~=`\\ \uppercase{\def~{\math{\backslash}}} + % + % In case @\ is used for backslash + \uppercase{\let\\=~} + % Can't get bold backslash so don't use bold forward slash + \catcode`\/=13 + \def/{{\secrmnotbold \normalslash}}% + \def-{{\normaldash\normaldash}}% en dash `--' + \def^{{\chapbf \normalcaret}}% + \def~{{\chapbf \normaltilde}}% + \def\_{% + \leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }% + \def|{$\vert$}% + \def<{$\less$}% + \def>{$\gtr$}% + \def+{$\normalplus$}% +}} + +\def\initial{% + \bgroup + \initialglyphs + \initialx +} + +\def\initialx#1{% + % Remove any glue we may have, we'll be inserting our own. + \removelastskip + % + % We like breaks before the index initials, so insert a bonus. + % The glue before the bonus allows a little bit of space at the + % bottom of a column to reduce an increase in inter-line spacing. + \nobreak + \vskip 0pt plus 5\baselineskip + \penalty -300 + \vskip 0pt plus -5\baselineskip + % + % Typeset the initial. Making this add up to a whole number of + % baselineskips increases the chance of the dots lining up from column + % to column. It still won't often be perfect, because of the stretch + % we need before each entry, but it's better. + % + % No shrink because it confuses \balancecolumns. + \vskip 1.67\baselineskip plus 1\baselineskip + \leftline{\secfonts \kern-0.05em \secbf #1}% + % \secfonts is inside the argument of \leftline so that the change of + % \baselineskip will not affect any glue inserted before the vbox that + % \leftline creates. + % Do our best not to break after the initial. + \nobreak + \vskip .33\baselineskip plus .1\baselineskip + \egroup % \initialglyphs +} + +\newdimen\entryrightmargin +\entryrightmargin=0pt + +% \entry typesets a paragraph consisting of the text (#1), dot leaders, and +% then page number (#2) flushed to the right margin. It is used for index +% and table of contents entries. The paragraph is indented by \leftskip. +% +\def\entry{% + \begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % No extra space above this paragraph. + \parskip = 0in + % + % When reading the text of entry, convert explicit line breaks + % from @* into spaces. The user might give these in long section + % titles, for instance. + \def\*{\unskip\space\ignorespaces}% + \def\entrybreak{\hfil\break}% An undocumented command + % + % Swallow the left brace of the text (first parameter): + \afterassignment\doentry + \let\temp = +} +\def\entrybreak{\unskip\space\ignorespaces}% +\def\doentry{% + % Save the text of the entry + \global\setbox\boxA=\hbox\bgroup + \bgroup % Instead of the swallowed brace. + \noindent + \aftergroup\finishentry + % And now comes the text of the entry. + % Not absorbing as a macro argument reduces the chance of problems + % with catcodes occurring. +} +{\catcode`\@=11 +\gdef\finishentry#1{% + \egroup % end box A + \dimen@ = \wd\boxA % Length of text of entry + \global\setbox\boxA=\hbox\bgroup + \unhbox\boxA + % #1 is the page number. + % + % Get the width of the page numbers, and only use + % leaders if they are present. + \global\setbox\boxB = \hbox{#1}% + \ifdim\wd\boxB = 0pt + \null\nobreak\hfill\ % + \else + % + \null\nobreak\indexdotfill % Have leaders before the page number. + % + \ifpdforxetex + \pdfgettoks#1.% + \hskip\skip\thinshrinkable\the\toksA + \else + \hskip\skip\thinshrinkable #1% + \fi + \fi + \egroup % end \boxA + \ifdim\wd\boxB = 0pt + \noindent\unhbox\boxA\par + \nobreak + \else\bgroup + % We want the text of the entries to be aligned to the left, and the + % page numbers to be aligned to the right. + % + \parindent = 0pt + \advance\leftskip by 0pt plus 1fil + \advance\leftskip by 0pt plus -1fill + \rightskip = 0pt plus -1fil + \advance\rightskip by 0pt plus 1fill + % Cause last line, which could consist of page numbers on their own + % if the list of page numbers is long, to be aligned to the right. + \parfillskip=0pt plus -1fill + % + \advance\rightskip by \entryrightmargin + % Determine how far we can stretch into the margin. + % This allows, e.g., "Appendix H GNU Free Documentation License" to + % fit on one line in @letterpaper format. + \ifdim\entryrightmargin>2.1em + \dimen@i=2.1em + \else + \dimen@i=0em + \fi + \advance \parfillskip by 0pt minus 1\dimen@i + % + \dimen@ii = \hsize + \advance\dimen@ii by -1\leftskip + \advance\dimen@ii by -1\entryrightmargin + \advance\dimen@ii by 1\dimen@i + \ifdim\wd\boxA > \dimen@ii % If the entry doesn't fit in one line + \ifdim\dimen@ > 0.8\dimen@ii % due to long index text + % Try to split the text roughly evenly. \dimen@ will be the length of + % the first line. + \dimen@ = 0.7\dimen@ + \dimen@ii = \hsize + \ifnum\dimen@>\dimen@ii + % If the entry is too long (for example, if it needs more than + % two lines), use all the space in the first line. + \dimen@ = \dimen@ii + \fi + \advance\leftskip by 0pt plus 1fill % ragged right + \advance \dimen@ by 1\rightskip + \parshape = 2 0pt \dimen@ 0em \dimen@ii + % Ideally we'd add a finite glue at the end of the first line only, + % instead of using \parshape with explicit line lengths, but TeX + % doesn't seem to provide a way to do such a thing. + % + % Indent all lines but the first one. + \advance\leftskip by 1em + \advance\parindent by -1em + \fi\fi + \indent % start paragraph + \unhbox\boxA + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % Word spacing - no stretch + \spaceskip=\fontdimen2\font minus \fontdimen4\font + % + \linepenalty=1000 % Discourage line breaks. + \hyphenpenalty=5000 % Discourage hyphenation. + % + \par % format the paragraph + \egroup % The \vbox + \fi + \endgroup +}} + +\newskip\thinshrinkable +\skip\thinshrinkable=.15em minus .15em + +% Like plain.tex's \dotfill, except uses up at least 1 em. +% The filll stretch here overpowers both the fil and fill stretch to push +% the page number to the right. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1filll} + + +\def\primary #1{\line{#1\hfil}} + +\def\secondary{\indententry{0.5cm}} +\def\tertiary{\indententry{1cm}} + +\def\indententry#1#2#3{% + \bgroup + \leftskip=#1 + \entry{#2}{#3}% + \egroup +} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 % private names + +\newbox\partialpage +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % If not much space left on page, start a new page. + \ifdim\pagetotal>0.8\vsize\vfill\eject\fi + % + % Grab any single-column material above us. + \output = {% + \savetopmark + % + \global\setbox\partialpage = \vbox{% + % Unvbox the main output page. + \unvbox\PAGE + \kern-\topskip \kern\baselineskip + }% + }% + \eject % run that output routine to set \partialpage + % + % Use the double-column output routine for subsequent pages. + \output = {\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it in one place. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +-<1pt) + % as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \advance\vsize by -\ht\partialpage + \vsize = 2\vsize + % + % For the benefit of balancing columns + \advance\baselineskip by 0pt plus 0.5pt +} + +% The double-column output routine for all double-column pages except +% the last, which is done by \balancecolumns. +% +\def\doublecolumnout{% + % + \savetopmark + \splittopskip=\topskip \splitmaxdepth=\maxdepth + \dimen@ = \vsize + \divide\dimen@ by 2 + % + % box0 will be the left-hand column, box2 the right. + \setbox0=\vsplit\PAGE to\dimen@ \setbox2=\vsplit\PAGE to\dimen@ + \global\advance\vsize by 2\ht\partialpage + \onepageout\pagesofar % empty except for the first time we are called + \unvbox\PAGE + \penalty\outputpenalty +} +% +% Re-output the contents of the output page -- any previous material, +% followed by the two boxes we just split, in box0 and box2. +\def\pagesofar{% + \unvbox\partialpage + % + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize + \hbox to\txipagewidth{\box0\hfil\box2}% +} + + +% Finished with double columns. +\def\enddoublecolumns{% + % The following penalty ensures that the page builder is exercised + % _before_ we change the output routine. This is necessary in the + % following situation: + % + % The last section of the index consists only of a single entry. + % Before this section, \pagetotal is less than \pagegoal, so no + % break occurs before the last section starts. However, the last + % section, consisting of \initial and the single \entry, does not + % fit on the page and has to be broken off. Without the following + % penalty the page builder will not be exercised until \eject + % below, and by that time we'll already have changed the output + % routine to the \balancecolumns version, so the next-to-last + % double-column page will be processed with \balancecolumns, which + % is wrong: The two columns will go to the main vertical list, with + % the broken-off section in the recent contributions. As soon as + % the output routine finishes, TeX starts reconsidering the page + % break. The two columns and the broken-off section both fit on the + % page, because the two columns now take up only half of the page + % goal. When TeX sees \eject from below which follows the final + % section, it invokes the new output routine that we've set after + % \balancecolumns below; \onepageout will try to fit the two columns + % and the final section into the vbox of \txipageheight (see + % \pagebody), causing an overfull box. + % + % Note that glue won't work here, because glue does not exercise the + % page builder, unlike penalties (see The TeXbook, pp. 280-281). + \penalty0 + % + \output = {% + % Split the last of the double-column material. + \savetopmark + \balancecolumns + }% + \eject % call the \output just set + \ifdim\pagetotal=0pt + % Having called \balancecolumns once, we do not + % want to call it again. Therefore, reset \output to its normal + % definition right away. + \global\output=\expandafter{\the\defaultoutput} + % + \endgroup % started in \begindoublecolumns + % Leave the double-column material on the current page, no automatic + % page break. + \box\balancedcolumns + % + % \pagegoal was set to the doubled \vsize above, since we restarted + % the current page. We're now back to normal single-column + % typesetting, so reset \pagegoal to the normal \vsize. + \global\vsize = \txipageheight % + \pagegoal = \txipageheight % + \else + % We had some left-over material. This might happen when \doublecolumnout + % is called in \balancecolumns. Try again. + \expandafter\enddoublecolumns + \fi +} +\newbox\balancedcolumns +\setbox\balancedcolumns=\vbox{shouldnt see this}% +% +% Only called for the last of the double column material. \doublecolumnout +% does the others. +\def\balancecolumns{% + \setbox0 = \vbox{\unvbox\PAGE}% like \box255 but more efficient, see p.120. + \dimen@ = \ht0 + \ifdim\dimen@<7\baselineskip + % Don't split a short final column in two. + \setbox2=\vbox{}% + \global\setbox\balancedcolumns=\vbox{\pagesofar}% + \else + % double the leading vertical space + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \divide\dimen@ by 2 % target to split to + \dimen@ii = \dimen@ + \splittopskip = \topskip + % Loop until left column is at least as high as the right column. + {% + \vbadness = 10000 + \loop + \global\setbox3 = \copy0 + \global\setbox1 = \vsplit3 to \dimen@ + \ifdim\ht1<\ht3 + \global\advance\dimen@ by 1pt + \repeat + }% + % Now the left column is in box 1, and the right column in box 3. + % + % Check whether the left column has come out higher than the page itself. + % (Note that we have doubled \vsize for the double columns, so + % the actual height of the page is 0.5\vsize). + \ifdim2\ht1>\vsize + % It appears that we have been called upon to balance too much material. + % Output some of it with \doublecolumnout, leaving the rest on the page. + \setbox\PAGE=\box0 + \doublecolumnout + \else + % Compare the heights of the two columns. + \ifdim4\ht1>5\ht3 + % Column heights are too different, so don't make their bottoms + % flush with each other. + \setbox2=\vbox to \ht1 {\unvbox3\vfill}% + \setbox0=\vbox to \ht1 {\unvbox1\vfill}% + \else + % Make column bottoms flush with each other. + \setbox2=\vbox to\ht1{\unvbox3\unskip}% + \setbox0=\vbox to\ht1{\unvbox1\unskip}% + \fi + \global\setbox\balancedcolumns=\vbox{\pagesofar}% + \fi + \fi + % +} +\catcode`\@ = \other + + +\message{sectioning,} +% Chapters, sections, etc. + +% Let's start with @part. +\outer\parseargdef\part{\partzzz{#1}} +\def\partzzz#1{% + \chapoddpage + \null + \vskip.3\vsize % move it down on the page a bit + \begingroup + \noindent \titlefonts\rm #1\par % the text + \let\lastnode=\empty % no node to associate with + \writetocentry{part}{#1}{}% but put it in the toc + \headingsoff % no headline or footline on the part page + % This outputs a mark at the end of the page that clears \thischapter + % and \thissection, as is done in \startcontents. + \let\pchapsepmacro\relax + \chapmacro{}{Yomitfromtoc}{}% + \chapoddpage + \endgroup +} + +% \unnumberedno is an oxymoron. But we count the unnumbered +% sections so that we can refer to them unambiguously in the pdf +% outlines by their "section number". We avoid collisions with chapter +% numbers by starting them at 10000. (If a document ever has 10000 +% chapters, we're in trouble anyway, I'm sure.) +\newcount\unnumberedno \unnumberedno = 10000 +\newcount\chapno +\newcount\secno \secno=0 +\newcount\subsecno \subsecno=0 +\newcount\subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount\appendixno \appendixno = `\@ +% +% \def\appendixletter{\char\the\appendixno} +% We do the following ugly conditional instead of the above simple +% construct for the sake of pdftex, which needs the actual +% letter in the expansion, not just typeset. +% +\def\appendixletter{% + \ifnum\appendixno=`A A% + \else\ifnum\appendixno=`B B% + \else\ifnum\appendixno=`C C% + \else\ifnum\appendixno=`D D% + \else\ifnum\appendixno=`E E% + \else\ifnum\appendixno=`F F% + \else\ifnum\appendixno=`G G% + \else\ifnum\appendixno=`H H% + \else\ifnum\appendixno=`I I% + \else\ifnum\appendixno=`J J% + \else\ifnum\appendixno=`K K% + \else\ifnum\appendixno=`L L% + \else\ifnum\appendixno=`M M% + \else\ifnum\appendixno=`N N% + \else\ifnum\appendixno=`O O% + \else\ifnum\appendixno=`P P% + \else\ifnum\appendixno=`Q Q% + \else\ifnum\appendixno=`R R% + \else\ifnum\appendixno=`S S% + \else\ifnum\appendixno=`T T% + \else\ifnum\appendixno=`U U% + \else\ifnum\appendixno=`V V% + \else\ifnum\appendixno=`W W% + \else\ifnum\appendixno=`X X% + \else\ifnum\appendixno=`Y Y% + \else\ifnum\appendixno=`Z Z% + % The \the is necessary, despite appearances, because \appendixletter is + % expanded while writing the .toc file. \char\appendixno is not + % expandable, thus it is written literally, thus all appendixes come out + % with the same letter (or @) in the toc without it. + \else\char\the\appendixno + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} + +% Each @chapter defines these (using marks) as the number+name, number +% and name of the chapter. Page headings and footings can use +% these. @section does likewise. +\def\thischapter{} +\def\thischapternum{} +\def\thischaptername{} +\def\thissection{} +\def\thissectionnum{} +\def\thissectionname{} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} + +% we only have subsub. +\chardef\maxseclevel = 3 +% +% A numbered section within an unnumbered changes to unnumbered too. +% To achieve this, remember the "biggest" unnum. sec. we are currently in: +\chardef\unnlevel = \maxseclevel +% +% Trace whether the current chapter is an appendix or not: +% \chapheadtype is "N" or "A", unnumbered chapters are ignored. +\def\chapheadtype{N} + +% Choose a heading macro +% #1 is heading type +% #2 is heading level +% #3 is text for heading +\def\genhead#1#2#3{% + % Compute the abs. sec. level: + \absseclevel=#2 + \advance\absseclevel by \secbase + % Make sure \absseclevel doesn't fall outside the range: + \ifnum \absseclevel < 0 + \absseclevel = 0 + \else + \ifnum \absseclevel > 3 + \absseclevel = 3 + \fi + \fi + % The heading type: + \def\headtype{#1}% + \if \headtype U% + \ifnum \absseclevel < \unnlevel + \chardef\unnlevel = \absseclevel + \fi + \else + % Check for appendix sections: + \ifnum \absseclevel = 0 + \edef\chapheadtype{\headtype}% + \else + \if \headtype A\if \chapheadtype N% + \errmessage{@appendix... within a non-appendix chapter}% + \fi\fi + \fi + % Check for numbered within unnumbered: + \ifnum \absseclevel > \unnlevel + \def\headtype{U}% + \else + \chardef\unnlevel = 3 + \fi + \fi + % Now print the heading: + \if \headtype U% + \ifcase\absseclevel + \unnumberedzzz{#3}% + \or \unnumberedseczzz{#3}% + \or \unnumberedsubseczzz{#3}% + \or \unnumberedsubsubseczzz{#3}% + \fi + \else + \if \headtype A% + \ifcase\absseclevel + \appendixzzz{#3}% + \or \appendixsectionzzz{#3}% + \or \appendixsubseczzz{#3}% + \or \appendixsubsubseczzz{#3}% + \fi + \else + \ifcase\absseclevel + \chapterzzz{#3}% + \or \seczzz{#3}% + \or \numberedsubseczzz{#3}% + \or \numberedsubsubseczzz{#3}% + \fi + \fi + \fi + \suppressfirstparagraphindent +} + +% an interface: +\def\numhead{\genhead N} +\def\apphead{\genhead A} +\def\unnmhead{\genhead U} + +% @chapter, @appendix, @unnumbered. Increment top-level counter, reset +% all lower-level sectioning counters to zero. +% +% Also set \chaplevelprefix, which we prepend to @float sequence numbers +% (e.g., figures), q.v. By default (before any chapter), that is empty. +\let\chaplevelprefix = \empty +% +\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz#1{% + % section resetting is \global in case the chapter is in a group, such + % as an @include file. + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\chapno by 1 + % + % Used for \float. + \gdef\chaplevelprefix{\the\chapno.}% + \resetallfloatnos + % + % \putwordChapter can contain complex things in translations. + \toks0=\expandafter{\putwordChapter}% + \message{\the\toks0 \space \the\chapno}% + % + % Write the actual heading. + \chapmacro{#1}{Ynumbered}{\the\chapno}% + % + % So @section and the like are numbered underneath this chapter. + \global\let\section = \numberedsec + \global\let\subsection = \numberedsubsec + \global\let\subsubsection = \numberedsubsubsec +} + +\outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz +% +\def\appendixzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\appendixno by 1 + \gdef\chaplevelprefix{\appendixletter.}% + \resetallfloatnos + % + % \putwordAppendix can contain complex things in translations. + \toks0=\expandafter{\putwordAppendix}% + \message{\the\toks0 \space \appendixletter}% + % + \chapmacro{#1}{Yappendix}{\appendixletter}% + % + \global\let\section = \appendixsec + \global\let\subsection = \appendixsubsec + \global\let\subsubsection = \appendixsubsubsec +} + +% normally unnmhead0 calls unnumberedzzz: +\outer\parseargdef\unnumbered{\unnmhead0{#1}} +\def\unnumberedzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\unnumberedno by 1 + % + % Since an unnumbered has no number, no prefix for figures. + \global\let\chaplevelprefix = \empty + \resetallfloatnos + % + % This used to be simply \message{#1}, but TeX fully expands the + % argument to \message. Therefore, if #1 contained @-commands, TeX + % expanded them. For example, in `@unnumbered The @cite{Book}', TeX + % expanded @cite (which turns out to cause errors because \cite is meant + % to be executed, not expanded). + % + % Anyway, we don't want the fully-expanded definition of @cite to appear + % as a result of the \message, we just want `@cite' itself. We use + % \the<toks register> to achieve this: TeX expands \the<toks> only once, + % simply yielding the contents of <toks register>. (We also do this for + % the toc entries.) + \toks0 = {#1}% + \message{(\the\toks0)}% + % + \chapmacro{#1}{Ynothing}{\the\unnumberedno}% + % + \global\let\section = \unnumberedsec + \global\let\subsection = \unnumberedsubsec + \global\let\subsubsection = \unnumberedsubsubsec +} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\parseargdef\centerchap{% + \let\centerparametersmaybe = \centerparameters + \unnmhead0{#1}% + \let\centerparametersmaybe = \relax +} + +% @top is like @unnumbered. +\let\top\unnumbered + +% Sections. +% +\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz +\def\seczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% +} + +% normally calls appendixsectionzzz: +\outer\parseargdef\appendixsection{\apphead1{#1}} +\def\appendixsectionzzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% +} +\let\appendixsec\appendixsection + +% normally calls unnumberedseczzz: +\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} +\def\unnumberedseczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% +} + +% Subsections. +% +% normally calls numberedsubseczzz: +\outer\parseargdef\numberedsubsec{\numhead2{#1}} +\def\numberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% +} + +% normally calls appendixsubseczzz: +\outer\parseargdef\appendixsubsec{\apphead2{#1}} +\def\appendixsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno}% +} + +% normally calls unnumberedsubseczzz: +\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} +\def\unnumberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno}% +} + +% Subsubsections. +% +% normally numberedsubsubseczzz: +\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} +\def\numberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynumbered}% + {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% normally appendixsubsubseczzz: +\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} +\def\appendixsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% normally unnumberedsubsubseczzz: +\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} +\def\unnumberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\let\section = \numberedsec +\let\subsection = \numberedsubsec +\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +\def\majorheading{% + {\advance\chapheadingskip by 10pt \chapbreak }% + \parsearg\chapheadingzzz +} + +\def\chapheading{\chapbreak \parsearg\chapheadingzzz} +\def\chapheadingzzz#1{% + \vbox{\chapfonts \raggedtitlesettings #1\par}% + \nobreak\bigskip \nobreak + \suppressfirstparagraphindent +} + +% @heading, @subheading, @subsubheading. +\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +% Parameter controlling skip before chapter headings (if needed) +\newskip\chapheadingskip + +% Define plain chapter starts, and page on/off switching for it. +\def\chapbreak{\dobreak \chapheadingskip {-4000}} + +% Start a new page +\def\chappager{\par\vfill\supereject} + +% \chapoddpage - start on an odd page for a new chapter +% Because \domark is called before \chapoddpage, the filler page will +% get the headings for the next chapter, which is wrong. But we don't +% care -- we just disable all headings on the filler page. +\def\chapoddpage{% + \chappager + \ifodd\pageno \else + \begingroup + \headingsoff + \null + \chappager + \endgroup + \fi +} + +\parseargdef\setchapternewpage{\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\def\HEADINGSon{\HEADINGSsinglechapoff}} + +\def\CHAPPAGon{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{% +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +% \chapmacro - Chapter opening. +% +% #1 is the text, #2 is the section type (Ynumbered, Ynothing, +% Yappendix, Yomitfromtoc), #3 the chapter number. +% Not used for @heading series. +% +% To test against our argument. +\def\Ynothingkeyword{Ynothing} +\def\Yappendixkeyword{Yappendix} +\def\Yomitfromtockeyword{Yomitfromtoc} +% +\def\chapmacro#1#2#3{% + \expandafter\ifx\thisenv\titlepage\else + \checkenv{}% chapters, etc., should not start inside an environment. + \fi + % Insert the first mark before the heading break (see notes for \domark). + \let\prevchapterdefs=\currentchapterdefs + \let\prevsectiondefs=\currentsectiondefs + \gdef\currentsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}% + \gdef\thissection{}}% + % + \def\temptype{#2}% + \ifx\temptype\Ynothingkeyword + \gdef\currentchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{\thischaptername}}% + \else\ifx\temptype\Yomitfromtockeyword + \gdef\currentchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{}}% + \else\ifx\temptype\Yappendixkeyword + \toks0={#1}% + \xdef\currentchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\appendixletter}% + % \noexpand\putwordAppendix avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordAppendix{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \else + \toks0={#1}% + \xdef\currentchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\the\chapno}% + % \noexpand\putwordChapter avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordChapter{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \fi\fi\fi + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert the chapter heading break. + \pchapsepmacro + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \let\prevchapterdefs=\currentchapterdefs + \let\prevsectiondefs=\currentsectiondefs + \domark + % + {% + \chapfonts \rm + \let\footnote=\errfootnoteheading % give better error message + % + % Have to define \currentsection before calling \donoderef, because the + % xref code eventually uses it. On the other hand, it has to be called + % after \pchapsepmacro, or the headline will change too soon. + \gdef\currentsection{#1}% + % + % Only insert the separating space if we have a chapter/appendix + % number, and don't print the unnumbered ``number''. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unnchap}% + \else\ifx\temptype\Yomitfromtockeyword + \setbox0 = \hbox{}% contents like unnumbered, but no toc entry + \def\toctype{omit}% + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% + \def\toctype{app}% + \else + \setbox0 = \hbox{#3\enspace}% + \def\toctype{numchap}% + \fi\fi\fi + % + % Write the toc entry for this chapter. Must come before the + % \donoderef, because we include the current node name in the toc + % entry, and \donoderef resets it to empty. + \writetocentry{\toctype}{#1}{#3}% + % + % For pdftex, we have to write out the node definition (aka, make + % the pdfdest) after any page break, but before the actual text has + % been typeset. If the destination for the pdf outline is after the + % text, then jumping from the outline may wind up with the text not + % being visible, for instance under high magnification. + \donoderef{#2}% + % + % Typeset the actual heading. + \nobreak % Avoid page breaks at the interline glue. + \vbox{\raggedtitlesettings \hangindent=\wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerparameters{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt +} + + +% Section titles. These macros combine the section number parts and +% call the generic \sectionheading to do the printing. +% +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip{-1000}} + +% Subsection titles. +\newskip\subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} + +% Subsubsection titles. +\def\subsubsecheadingskip{\subsecheadingskip} +\def\subsubsecheadingbreak{\subsecheadingbreak} + + +% Print any size, any type, section title. +% +% #1 is the text of the title, +% #2 is the section level (sec/subsec/subsubsec), +% #3 is the section type (Ynumbered, Ynothing, Yappendix, Yomitfromtoc), +% #4 is the section number. +% +\def\seckeyword{sec} +% +\def\sectionheading#1#2#3#4{% + {% + \def\sectionlevel{#2}% + \def\temptype{#3}% + % + % It is ok for the @heading series commands to appear inside an + % environment (it's been historically allowed, though the logic is + % dubious), but not the others. + \ifx\temptype\Yomitfromtockeyword\else + \checkenv{}% non-@*heading should not be in an environment. + \fi + \let\footnote=\errfootnoteheading + % + % Switch to the right set of fonts. + \csname #2fonts\endcsname \rm + % + % Insert first mark before the heading break (see notes for \domark). + \let\prevsectiondefs=\currentsectiondefs + \ifx\temptype\Ynothingkeyword + \ifx\sectionlevel\seckeyword + \gdef\currentsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}% + \gdef\thissection{\thissectionname}}% + \fi + \else\ifx\temptype\Yomitfromtockeyword + % Don't redefine \thissection. + \else\ifx\temptype\Yappendixkeyword + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\currentsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \else + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\currentsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \fi\fi\fi + % + % Go into vertical mode. Usually we'll already be there, but we + % don't want the following whatsit to end up in a preceding paragraph + % if the document didn't happen to have a blank line. + \par + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert space above the heading. + \csname #2headingbreak\endcsname + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \global\let\prevsectiondefs=\currentsectiondefs + \domark + % + % Only insert the space after the number if we have a section number. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unn}% + \gdef\currentsection{#1}% + \else\ifx\temptype\Yomitfromtockeyword + % for @headings -- no section number, don't include in toc, + % and don't redefine \currentsection. + \setbox0 = \hbox{}% + \def\toctype{omit}% + \let\sectionlevel=\empty + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{#4\enspace}% + \def\toctype{app}% + \gdef\currentsection{#1}% + \else + \setbox0 = \hbox{#4\enspace}% + \def\toctype{num}% + \gdef\currentsection{#1}% + \fi\fi\fi + % + % Write the toc entry (before \donoderef). See comments in \chapmacro. + \writetocentry{\toctype\sectionlevel}{#1}{#4}% + % + % Write the node reference (= pdf destination for pdftex). + % Again, see comments in \chapmacro. + \donoderef{#3}% + % + % Interline glue will be inserted when the vbox is completed. + % That glue will be a valid breakpoint for the page, since it'll be + % preceded by a whatsit (usually from the \donoderef, or from the + % \writetocentry if there was no node). We don't want to allow that + % break, since then the whatsits could end up on page n while the + % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. + \nobreak + % + % Output the actual section heading. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright + \hangindent=\wd0 % zero if no section number + \unhbox0 #1}% + }% + % Add extra space after the heading -- half of whatever came above it. + % Don't allow stretch, though. + \kern .5 \csname #2headingskip\endcsname + % + % Do not let the kern be a potential breakpoint, as it would be if it + % was followed by glue. + \nobreak + % + % We'll almost certainly start a paragraph next, so don't let that + % glue accumulate. (Not a breakpoint because it's preceded by a + % discardable item.) However, when a paragraph is not started next + % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out + % or the negative glue will cause weirdly wrong output, typically + % obscuring the section heading with something else. + \vskip-\parskip + % + % This is so the last item on the main vertical list is a known + % \penalty > 10000, so \startdefun, etc., can recognize the situation + % and do the needful. + \penalty 10001 +} + + +\message{toc,} +% Table of contents. +\newwrite\tocfile + +% Write an entry to the toc file, opening it if necessary. +% Called from @chapter, etc. +% +% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} +% We append the current node name (if any) and page number as additional +% arguments for the \{chap,sec,...}entry macros which will eventually +% read this. The node name is used in the pdf outlines as the +% destination to jump to. +% +% We open the .toc file for writing here instead of at @setfilename (or +% any other fixed time) so that @contents can be anywhere in the document. +% But if #1 is `omit', then we don't do anything. This is used for the +% table of contents chapter openings themselves. +% +\newif\iftocfileopened +\def\omitkeyword{omit}% +% +\def\writetocentry#1#2#3{% + \edef\writetoctype{#1}% + \ifx\writetoctype\omitkeyword \else + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + % + \iflinks + {\atdummies + \edef\temp{% + \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% + \temp + }% + \fi + \fi + % + % Tell \shipout to create a pdf destination on each page, if we're + % writing pdf. These are used in the table of contents. We can't + % just write one on every page because the title pages are numbered + % 1 and 2 (the page numbers aren't printed), and so are the first + % two pages of the document. Thus, we'd have two destinations named + % `1', and two named `2'. + \ifpdforxetex + \global\pdfmakepagedesttrue + \fi +} + + +% These characters do not print properly in the Computer Modern roman +% fonts, so we must take special care. This is more or less redundant +% with the Texinfo input format setup at the end of this file. +% +\def\activecatcodes{% + \catcode`\"=\active + \catcode`\$=\active + \catcode`\<=\active + \catcode`\>=\active + \catcode`\\=\active + \catcode`\^=\active + \catcode`\_=\active + \catcode`\|=\active + \catcode`\~=\active +} + + +% Read the toc file, which is essentially Texinfo input. +\def\readtocfile{% + \setupdatafile + \activecatcodes + \input \tocreadfilename +} + +\newskip\contentsrightmargin \contentsrightmargin=1in +\newcount\savepageno +\newcount\lastnegativepageno \lastnegativepageno = -1 + +% Prepare to read what we've written to \tocfile. +% +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \chapmacro{#1}{Yomitfromtoc}{}% + % + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \raggedbottom % Worry more about breakpoints than the bottom. + \entryrightmargin=\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi + \def\thistitle{}% no title in double-sided headings + % Record where the Roman numerals started. + \ifnum\romancount=0 \global\romancount=\pagecount \fi +} + +% redefined for the two-volume lispref. We always output on +% \jobname.toc even if this is redefined. +% +\def\tocreadfilename{\jobname.toc} + +% Normal (long) toc. +% +\def\contents{% + \startcontents{\putwordTOC}% + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \ifeof 1 \else + \pdfmakeoutlines + \fi + \closein 1 + \endgroup + \contentsendroman +} + +% And just the chapters. +\def\summarycontents{% + \startcontents{\putwordShortTOC}% + % + \let\partentry = \shortpartentry + \let\numchapentry = \shortchapentry + \let\appentry = \shortchapentry + \let\unnchapentry = \shortunnchapentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf + \let\sl=\shortcontsl \let\tt=\shortconttt + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\numsecentry##1##2##3##4{} + \let\appsecentry = \numsecentry + \let\unnsecentry = \numsecentry + \let\numsubsecentry = \numsecentry + \let\appsubsecentry = \numsecentry + \let\unnsubsecentry = \numsecentry + \let\numsubsubsecentry = \numsecentry + \let\appsubsubsecentry = \numsecentry + \let\unnsubsubsecentry = \numsecentry + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \closein 1 + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \endgroup + \contentsendroman +} +\let\shortcontents = \summarycontents + +% Get ready to use Arabic numerals again +\def\contentsendroman{% + \lastnegativepageno = \pageno + \global\pageno = \savepageno + % + % If \romancount > \arabiccount, the contents are at the end of the + % document. Otherwise, advance where the Arabic numerals start for + % the page numbers. + \ifnum\romancount>\arabiccount\else\global\arabiccount=\pagecount\fi +} + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g., `A' for an appendix, or `3' for a chapter. +% +\def\shortchaplabel#1{% + % This space should be enough, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % But use \hss just in case. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + % + % We'd like to right-justify chapter numbers, but that looks strange + % with appendix letters. And right-justifying numbers and + % left-justifying letters looks strange when there is less than 10 + % chapters. Have to read the whole toc once to know how many chapters + % there are before deciding ... + \hbox to 1em{#1\hss}% +} + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Parts, in the main contents. Replace the part number, which doesn't +% exist, with an empty box. Let's hope all the numbers have the same width. +% Also ignore the page number, which is conventionally not printed. +\def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}} +\def\partentry#1#2#3#4{% + % Add stretch and a bonus for breaking the page before the part heading. + % This reduces the chance of the page being broken immediately after the + % part heading, before a following chapter heading. + \vskip 0pt plus 5\baselineskip + \penalty-300 + \vskip 0pt plus -5\baselineskip + \dochapentry{\numeralbox\labelspace#1}{}% +} +% +% Parts, in the short toc. +\def\shortpartentry#1#2#3#4{% + \penalty-300 + \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip + \shortchapentry{{\bf #1}}{\numeralbox}{}{}% +} + +% Chapters, in the main contents. +\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} + +% Chapters, in the short toc. +% See comments in \dochapentry re vbox and related settings. +\def\shortchapentry#1#2#3#4{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% +} + +% Appendices, in the main contents. +% Need the word Appendix, and a fixed-size box. +% +\def\appendixbox#1{% + % We use M since it's probably the widest letter. + \setbox0 = \hbox{\putwordAppendix{} M}% + \hbox to \wd0{\putwordAppendix{} #1\hss}} +% +\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\hskip.7em#1}{#4}} + +% Unnumbered chapters. +\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} +\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} + +% Sections. +\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} +\let\appsecentry=\numsecentry +\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} + +% Subsections. +\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} +\let\appsubsecentry=\numsubsecentry +\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} + +% And subsubsections. +\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} +\let\appsubsubsecentry=\numsubsubsecentry +\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} + +% This parameter controls the indentation of the various levels. +% Same as \defaultparindent. +\newdimen\tocindent \tocindent = 15pt + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + % Move the page numbers slightly to the right + \advance\entryrightmargin by -0.05em + \chapentryfonts + \tocentry{#1}{\dopageno\bgroup#2\egroup}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +% We use the same \entry macro as for the index entries. +\let\tocentry = \entry + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\def\subsecentryfonts{\textfonts} +\def\subsubsecentryfonts{\textfonts} + + +\message{environments,} +% @foo ... @end foo. + +% @tex ... @end tex escapes into raw TeX temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain @ character. + +\envdef\tex{% + \setregularquotes + \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 + \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 + \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie + \catcode `\%=14 + \catcode `\+=\other + \catcode `\"=\other + \catcode `\|=\other + \catcode `\<=\other + \catcode `\>=\other + \catcode `\`=\other + \catcode `\'=\other + % + % ' is active in math mode (mathcode"8000). So reset it, and all our + % other math active characters (just in case), to plain's definitions. + \mathactive + % + % Inverse of the list at the beginning of the file. + \let\b=\ptexb + \let\bullet=\ptexbullet + \let\c=\ptexc + \let\,=\ptexcomma + \let\.=\ptexdot + \let\dots=\ptexdots + \let\equiv=\ptexequiv + \let\!=\ptexexclam + \let\i=\ptexi + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \let\{=\ptexlbrace + \let\+=\tabalign + \let\}=\ptexrbrace + \let\/=\ptexslash + \let\sp=\ptexsp + \let\*=\ptexstar + %\let\sup=\ptexsup % do not redefine, we want @sup to work in math mode + \let\t=\ptext + \expandafter \let\csname top\endcsname=\ptextop % we've made it outer + \let\frenchspacing=\plainfrenchspacing + % + \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% + \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% + \def\@{@}% +} +% There is no need to define \Etex. + +% Define @lisp ... @end lisp. +% @lisp environment forms a group so it can rebind things, +% including the definition of @end lisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip. +% +\def\aboveenvbreak{{% + % =10000 instead of <10000 because of a special case in \itemzzz and + % \sectionheading, q.v. + \ifnum \lastpenalty=10000 \else + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + \ifnum\lastpenalty<10000 + % Penalize breaking before the environment, because preceding text + % often leads into it. + \penalty100 + \fi + \vskip\envskipamount + \fi + \fi +}} + +\def\afterenvbreak{{% + % =10000 instead of <10000 because of a special case in \itemzzz and + % \sectionheading, q.v. + \ifnum \lastpenalty=10000 \else + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + % it's not a good place to break if the last penalty was \nobreak + % or better ... + \ifnum\lastpenalty<10000 \penalty-50 \fi + \vskip\envskipamount + \fi + \fi +}} + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will +% also clear it, so that its embedded environments do the narrowing again. +\let\nonarrowing=\relax + +% @cartouche ... @end cartouche: draw rectangle w/rounded corners around +% environment contents. + +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +% only require the font if @cartouche is actually used +\def\cartouchefontdefs{% + \font\circle=lcircle10\relax + \circthick=\fontdimen8\circle +} +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip + + +\envdef\cartouche{% + \cartouchefontdefs + \ifhmode\par\fi % can't be in the midst of a paragraph. + \startsavinginserts + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt % we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either + % side, and for 6pt waste from + % each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % + % If this cartouche directly follows a sectioning command, we need the + % \parskip glue (backspaced over by default) or the cartouche can + % collide with the section heading. + \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi + % + \setbox\groupbox=\vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \kern3pt + \hsize=\cartinner + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip + \comment % For explanation, see the end of def\group. +} +\def\Ecartouche{% + \ifhmode\par\fi + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup + \addgroupbox + \checkinserts +} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\newdimen\nonfillparindent +\def\nonfillstart{% + \aboveenvbreak + \ifdim\hfuzz < 12pt \hfuzz = 12pt \fi % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + % Turn off paragraph indentation but redefine \indent to emulate + % the normal \indent. + \nonfillparindent=\parindent + \parindent = 0pt + \let\indent\nonfillindent + % + \emergencystretch = 0pt % don't try to avoid overfull boxes + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \let\exdent=\nofillexdent +} + +\begingroup +\obeyspaces +% We want to swallow spaces (but not other tokens) after the fake +% @indent in our nonfill-environments, where spaces are normally +% active and set to @tie, resulting in them not being ignored after +% @indent. +\gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}% +\gdef\nonfillindentcheck{% +\ifx\temp % +\expandafter\nonfillindentgobble% +\else% +\leavevmode\nonfillindentbox% +\fi% +}% +\endgroup +\def\nonfillindentgobble#1{\nonfillindent} +\def\nonfillindentbox{\hbox to \nonfillparindent{\hss}} + +% If you want all examples etc. small: @set dispenvsize small. +% If you want even small examples the full size: @set dispenvsize nosmall. +% This affects the following displayed environments: +% @example, @display, @format, @lisp, @verbatim +% +\def\smallword{small} +\def\nosmallword{nosmall} +\let\SETdispenvsize\relax +\def\setnormaldispenv{% + \ifx\SETdispenvsize\smallword + % end paragraph for sake of leading, in case document has no blank + % line. This is redundant with what happens in \aboveenvbreak, but + % we need to do it before changing the fonts, and it's inconvenient + % to change the fonts afterward. + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} +\def\setsmalldispenv{% + \ifx\SETdispenvsize\nosmallword + \else + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} + +% We often define two environments, @foo and @smallfoo. +% Let's do it in one command. #1 is the env name, #2 the definition. +\def\makedispenvdef#1#2{% + \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}% + \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}% + \expandafter\let\csname E#1\endcsname \afterenvbreak + \expandafter\let\csname Esmall#1\endcsname \afterenvbreak +} + +% Define two environment synonyms (#1 and #2) for an environment. +\def\maketwodispenvdef#1#2#3{% + \makedispenvdef{#1}{#3}% + \makedispenvdef{#2}{#3}% +} +% +% @lisp: indented, narrowed, typewriter font; +% @example: same as @lisp. +% +% @smallexample and @smalllisp: use smaller fonts. +% Originally contributed by Pavel@xerox. +% +\maketwodispenvdef{lisp}{example}{% + \nonfillstart + \tt\setcodequotes + \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. + \parsearg\gobble +} +% @display/@smalldisplay: same as @lisp except keep current font. +% +\makedispenvdef{display}{% + \nonfillstart + \gobble +} + +% @format/@smallformat: same as @display except don't narrow margins. +% +\makedispenvdef{format}{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} + +% @flushleft: same as @format, but doesn't obey \SETdispenvsize. +\envdef\flushleft{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} +\let\Eflushleft = \afterenvbreak + +% @flushright. +% +\envdef\flushright{% + \let\nonarrowing = t% + \nonfillstart + \advance\leftskip by 0pt plus 1fill\relax + \gobble +} +\let\Eflushright = \afterenvbreak + + +% @raggedright does more-or-less normal line breaking but no right +% justification. From plain.tex. +\envdef\raggedright{% + \rightskip0pt plus2.4em \spaceskip.3333em \xspaceskip.5em\relax +} +\let\Eraggedright\par + +\envdef\raggedleft{% + \parindent=0pt \leftskip0pt plus2em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedleft\par + +\envdef\raggedcenter{% + \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedcenter\par + + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. We keep \parskip nonzero in general, since +% we're doing normal filling. So, when using \aboveenvbreak and +% \afterenvbreak, temporarily make \parskip 0. +% +\makedispenvdef{quotation}{\quotationstart} +% +\def\quotationstart{% + \indentedblockstart % same as \indentedblock, but increase right margin too. + \ifx\nonarrowing\relax + \advance\rightskip by \lispnarrowing + \fi + \parsearg\quotationlabel +} + +% We have retained a nonzero parskip for the environment, since we're +% doing normal filling. +% +\def\Equotation{% + \par + \ifx\quotationauthor\thisisundefined\else + % indent a bit. + \leftline{\kern 2\leftskip \sl ---\quotationauthor}% + \fi + {\parskip=0pt \afterenvbreak}% +} +\def\Esmallquotation{\Equotation} + +% If we're given an argument, typeset it in bold with a colon after. +\def\quotationlabel#1{% + \def\temp{#1}% + \ifx\temp\empty \else + {\bf #1: }% + \fi +} + +% @indentedblock is like @quotation, but indents only on the left and +% has no optional argument. +% +\makedispenvdef{indentedblock}{\indentedblockstart} +% +\def\indentedblockstart{% + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \parindent=0pt + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \exdentamount = \lispnarrowing + \else + \let\nonarrowing = \relax + \fi +} + +% Keep a nonzero parskip for the environment, since we're doing normal filling. +% +\def\Eindentedblock{% + \par + {\parskip=0pt \afterenvbreak}% +} +\def\Esmallindentedblock{\Eindentedblock} + + +% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>} +% If we want to allow any <char> as delimiter, +% we need the curly braces so that makeinfo sees the @verb command, eg: +% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org +% +% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. +% +% [Knuth] p.344; only we need to do the other characters Texinfo sets +% active too. Otherwise, they get lost as the first character on a +% verbatim line. +\def\dospecials{% + \do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% + \do\<\do\>\do\|\do\@\do+\do\"% + % Don't do the quotes -- if we do, @set txicodequoteundirected and + % @set txicodequotebacktick will not have effect on @verb and + % @verbatim, and ?` and !` ligatures won't get disabled. + %\do\`\do\'% +} +% +% [Knuth] p. 380 +\def\uncatcodespecials{% + \def\do##1{\catcode`##1=\other}\dospecials} +% +% Setup for the @verb command. +% +% Eight spaces for a tab +\begingroup + \catcode`\^^I=\active + \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} +\endgroup +% +\def\setupverb{% + \tt % easiest (and conventionally used) font for verbatim + \def\par{\leavevmode\endgraf}% + \setcodequotes + \tabeightspaces + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces +} + +% Setup for the @verbatim environment +% +% Real tab expansion. +\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount +% +% We typeset each line of the verbatim in an \hbox, so we can handle +% tabs. +\newbox\verbbox +\def\starttabbox{\setbox\verbbox=\hbox\bgroup} +% +\begingroup + \catcode`\^^I=\active + \gdef\tabexpand{% + \catcode`\^^I=\active + \def^^I{\leavevmode\egroup + \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab + \divide\dimen\verbbox by\tabw + \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw + \advance\dimen\verbbox by\tabw % advance to next multiple of \tabw + \wd\verbbox=\dimen\verbbox + \leavevmode\box\verbbox \starttabbox + }% + } +\endgroup + +% start the verbatim environment. +\def\setupverbatim{% + \let\nonarrowing = t% + \nonfillstart + \tt % easiest (and conventionally used) font for verbatim + \def\par{\egroup\leavevmode\box\verbbox\endgraf\starttabbox}% + \tabexpand + \setcodequotes + % Respect line breaks, + % print special symbols as themselves, and + % make each space count. + % Must do in this order: + \obeylines \uncatcodespecials \sepspaces +} + +% Do the @verb magic: verbatim text is quoted by unique +% delimiter characters. Before first delimiter expect a +% right brace, after last delimiter expect closing brace: +% +% \def\doverb'{'<char>#1<char>'}'{#1} +% +% [Knuth] p. 382; only eat outer {} +\begingroup + \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other + \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] +\endgroup +% +\def\verb{\begingroup\setupverb\doverb} +% +% +% Do the @verbatim magic: define the macro \doverbatim so that +% the (first) argument ends when '@end verbatim' is reached, ie: +% +% \def\doverbatim#1@end verbatim{#1} +% +% For Texinfo it's a lot easier than for LaTeX, +% because texinfo's \verbatim doesn't stop at '\end{verbatim}': +% we need not redefine '\', '{' and '}'. +% +% Inspired by LaTeX's verbatim command set [latex.ltx] +% +\begingroup + \catcode`\ =\active + \obeylines % + % ignore everything up to the first ^^M, that's the newline at the end + % of the @verbatim input line itself. Otherwise we get an extra blank + % line in the output. + \xdef\doverbatim#1^^M#2@end verbatim{% + \starttabbox#2\egroup\noexpand\end\gobble verbatim}% + % We really want {...\end verbatim} in the body of the macro, but + % without the active space; thus we have to use \xdef and \gobble. + % The \egroup ends the \verbbox started at the end of the last line in + % the block. +\endgroup +% +\envdef\verbatim{% + \setnormaldispenv\setupverbatim\doverbatim +} +\let\Everbatim = \afterenvbreak + + +% @verbatiminclude FILE - insert text of file in verbatim environment. +% +\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} +% +\def\doverbatiminclude#1{% + {% + \makevalueexpandable + \setupverbatim + {% + \indexnofonts % Allow `@@' and other weird things in file names. + \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}% + \edef\tmp{\noexpand\input #1 } + \expandafter + }\expandafter\starttabbox\tmp\egroup + \afterenvbreak + }% +} + +% @copying ... @end copying. +% Save the text away for @insertcopying later. +% +% We save the uninterpreted tokens, rather than creating a box. +% Saving the text in a box would be much easier, but then all the +% typesetting commands (@smallbook, font changes, etc.) have to be done +% beforehand -- and a) we want @copying to be done first in the source +% file; b) letting users define the frontmatter in as flexible order as +% possible is desirable. +% +\def\copying{\checkenv{}\begingroup\scanargctxt\docopying} +\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} +% +\def\insertcopying{% + \begingroup + \parindent = 0pt % paragraph indentation looks wrong on title page + \scanexp\copyingtext + \endgroup +} + + +\message{defuns,} +% @defun etc. + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deflastargmargin \deflastargmargin=18pt +\newcount\defunpenalty + +% Start the processing of @deffn: +\def\startdefun{% + \ifnum\lastpenalty<10000 + \medbreak + \defunpenalty=10003 % Will keep this @deffn together with the + % following @def command, see below. + \else + % If there are two @def commands in a row, we'll have a \nobreak, + % which is there to keep the function description together with its + % header. But if there's nothing but headers, we need to allow a + % break somewhere. Check specifically for penalty 10002, inserted + % by \printdefunline, instead of 10000, since the sectioning + % commands also insert a nobreak penalty, and we don't want to allow + % a break between a section heading and a defun. + % + % As a further refinement, we avoid "club" headers by signalling + % with penalty of 10003 after the very first @deffn in the + % sequence (see above), and penalty of 10002 after any following + % @def command. + \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi + % + % Similarly, after a section heading, do not allow a break. + % But do insert the glue. + \medskip % preceded by discardable penalty, so not a breakpoint + \fi + % + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent +} + +\def\dodefunx#1{% + % First, check whether we are in the right environment: + \checkenv#1% + % + % As above, allow line break if we have multiple x headers in a row. + % It's not a great place, though. + \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi + % + % And now, it's time to reuse the body of the original defun: + \expandafter\gobbledefun#1% +} +\def\gobbledefun#1\startdefun{} + +% \printdefunline \deffnheader{text} +% +\def\printdefunline#1#2{% + \begingroup + % call \deffnheader: + #1#2 \endheader + % common ending: + \interlinepenalty = 10000 + \advance\rightskip by 0pt plus 1fil\relax + \endgraf + \nobreak\vskip -\parskip + \penalty\defunpenalty % signal to \startdefun and \dodefunx + % Some of the @defun-type tags do not enable magic parentheses, + % rendering the following check redundant. But we don't optimize. + \checkparencounts + \endgroup +} + +\def\Edefun{\endgraf\medbreak} + +% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; +% the only thing remaining is to define \deffnheader. +% +\def\makedefun#1{% + \expandafter\let\csname E#1\endcsname = \Edefun + \edef\temp{\noexpand\domakedefun + \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% + \temp +} + +% \domakedefun \deffn \deffnx \deffnheader { (defn. of \deffnheader) } +% +% Define \deffn and \deffnx, without parameters. +% \deffnheader has to be defined explicitly. +% +\def\domakedefun#1#2#3{% + \envdef#1{% + \startdefun + \doingtypefnfalse % distinguish typed functions from all else + \parseargusing\activeparens{\printdefunline#3}% + }% + \def#2{\dodefunx#1}% + \def#3% +} + +\newif\ifdoingtypefn % doing typed function? +\newif\ifrettypeownline % typeset return type on its own line? + +% @deftypefnnewline on|off says whether the return type of typed functions +% are printed on their own line. This affects @deftypefn, @deftypefun, +% @deftypeop, and @deftypemethod. +% +\parseargdef\deftypefnnewline{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxideftypefnnl\endcsname + = \empty + \else\ifx\temp\offword + \expandafter\let\csname SETtxideftypefnnl\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @txideftypefnnl value `\temp', + must be on|off}% + \fi\fi +} + +% \dosubind {index}{topic}{subtopic} +% +% If SUBTOPIC is present, precede it with a space, and call \doind. +% (At some time during the 20th century, this made a two-level entry in an +% index such as the operation index. Nobody seemed to notice the change in +% behaviour though.) +\def\dosubind#1#2#3{% + \def\thirdarg{#3}% + \ifx\thirdarg\empty + \doind{#1}{#2}% + \else + \doind{#1}{#2\space#3}% + \fi +} + +% Untyped functions: + +% @deffn category name args +\makedefun{deffn}{\deffngeneral{}} + +% @deffn category class name args +\makedefun{defop}#1 {\defopon{#1\ \putwordon}} + +% \defopon {category on}class name args +\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deffngeneral {subind}category name args +% +\def\deffngeneral#1#2 #3 #4\endheader{% + \dosubind{fn}{\code{#3}}{#1}% + \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% +} + +% Typed functions: + +% @deftypefn category type name args +\makedefun{deftypefn}{\deftypefngeneral{}} + +% @deftypeop category class type name args +\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} + +% \deftypeopon {category on}class type name args +\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deftypefngeneral {subind}category type name args +% +\def\deftypefngeneral#1#2 #3 #4 #5\endheader{% + \dosubind{fn}{\code{#4}}{#1}% + \doingtypefntrue + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +% Typed variables: + +% @deftypevr category type var args +\makedefun{deftypevr}{\deftypecvgeneral{}} + +% @deftypecv category class type var args +\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} + +% \deftypecvof {category of}class type var args +\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } + +% \deftypecvgeneral {subind}category type var args +% +\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% + \dosubind{vr}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +% Untyped variables: + +% @defvr category var args +\makedefun{defvr}#1 {\deftypevrheader{#1} {} } + +% @defcv category class var args +\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} + +% \defcvof {category of}class var args +\def\defcvof#1#2 {\deftypecvof{#1}#2 {} } + +% Types: + +% @deftp category name args +\makedefun{deftp}#1 #2 #3\endheader{% + \doind{tp}{\code{#2}}% + \defname{#1}{}{#2}\defunargs{#3\unskip}% +} + +% Remaining @defun-like shortcuts: +\makedefun{defun}{\deffnheader{\putwordDeffunc} } +\makedefun{defmac}{\deffnheader{\putwordDefmac} } +\makedefun{defspec}{\deffnheader{\putwordDefspec} } +\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } +\makedefun{defvar}{\defvrheader{\putwordDefvar} } +\makedefun{defopt}{\defvrheader{\putwordDefopt} } +\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } +\makedefun{defmethod}{\defopon\putwordMethodon} +\makedefun{deftypemethod}{\deftypeopon\putwordMethodon} +\makedefun{defivar}{\defcvof\putwordInstanceVariableof} +\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} + +% \defname, which formats the name of the @def (not the args). +% #1 is the category, such as "Function". +% #2 is the return type, if any. +% #3 is the function name. +% +% We are followed by (but not passed) the arguments, if any. +% +\def\defname#1#2#3{% + \par + % Get the values of \leftskip and \rightskip as they were outside the @def... + \advance\leftskip by -\defbodyindent + % + % Determine if we are typesetting the return type of a typed function + % on a line by itself. + \rettypeownlinefalse + \ifdoingtypefn % doing a typed function specifically? + % then check user option for putting return type on its own line: + \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else + \rettypeownlinetrue + \fi + \fi + % + % How we'll format the category name. Putting it in brackets helps + % distinguish it from the body text that may end up on the next line + % just below it. + \def\temp{#1}% + \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} + % + % Figure out line sizes for the paragraph shape. We'll always have at + % least two. + \tempnum = 2 + % + % The first line needs space for \box0; but if \rightskip is nonzero, + % we need only space for the part of \box0 which exceeds it: + \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip + % + % If doing a return type on its own line, we'll have another line. + \ifrettypeownline + \advance\tempnum by 1 + \def\maybeshapeline{0in \hsize}% + \else + \def\maybeshapeline{}% + \fi + % + % The continuations: + \dimen2=\hsize \advance\dimen2 by -\defargsindent + % + % The final paragraph shape: + \parshape \tempnum 0in \dimen0 \maybeshapeline \defargsindent \dimen2 + % + % Put the category name at the right margin. + \noindent + \hbox to 0pt{% + \hfil\box0 \kern-\hsize + % \hsize has to be shortened this way: + \kern\leftskip + % Intentionally do not respect \rightskip, since we need the space. + }% + % + % Allow all lines to be underfull without complaint: + \tolerance=10000 \hbadness=10000 + \exdentamount=\defbodyindent + {% + % defun fonts. We use typewriter by default (used to be bold) because: + % . we're printing identifiers, they should be in tt in principle. + % . in languages with many accents, such as Czech or French, it's + % common to leave accents off identifiers. The result looks ok in + % tt, but exceedingly strange in rm. + % . we don't want -- and --- to be treated as ligatures. + % . this still does not fix the ?` and !` ligatures, but so far no + % one has made identifiers using them :). + \df \tt + \def\temp{#2}% text of the return type + \ifx\temp\empty\else + \tclose{\temp}% typeset the return type + \ifrettypeownline + % put return type on its own line; prohibit line break following: + \hfil\vadjust{\nobreak}\break + \else + \space % type on same line, so just followed by a space + \fi + \fi % no return type + #3% output function name + }% + {\rm\enskip}% hskip 0.5 em of \rmfont + % + \boldbrax + % arguments will be output next, if any. +} + +% Print arguments in slanted roman (not ttsl), inconsistently with using +% tt for the name. This is because literal text is sometimes needed in +% the argument list (groff manual), and ttsl and tt are not very +% distinguishable. Prevent hyphenation at `-' chars. +% +\def\defunargs#1{% + % use sl by default (not ttsl), + % tt for the names. + \df \sl \hyphenchar\font=0 + % + % On the other hand, if an argument has two dashes (for instance), we + % want a way to get ttsl. We used to recommend @var for that, so + % leave the code in, but it's strange for @var to lead to typewriter. + % Nowadays we recommend @code, since the difference between a ttsl hyphen + % and a tt hyphen is pretty tiny. @code also disables ?` !`. + \def\var##1{{\setregularquotes\ttslanted{##1}}}% + #1% + \sl\hyphenchar\font=45 +} + +% We want ()&[] to print specially on the defun line. +% +\def\activeparens{% + \catcode`\(=\active \catcode`\)=\active + \catcode`\[=\active \catcode`\]=\active + \catcode`\&=\active +} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +{ + \activeparens + \global\let(=\lparen \global\let)=\rparen + \global\let[=\lbrack \global\let]=\rbrack + \global\let& = \& + + \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + \gdef\magicamp{\let&=\amprm} +} +\let\ampchar\& + +\newcount\parencount + +% If we encounter &foo, then turn on ()-hacking afterwards +\newif\ifampseen +\def\amprm#1 {\ampseentrue{\bf\ }} + +\def\parenfont{% + \ifampseen + % At the first level, print parens in roman, + % otherwise use the default font. + \ifnum \parencount=1 \rm \fi + \else + % The \sf parens (in \boldbrax) actually are a little bolder than + % the contained text. This is especially needed for [ and ] . + \sf + \fi +} +\def\infirstlevel#1{% + \ifampseen + \ifnum\parencount=1 + #1% + \fi + \fi +} +\def\bfafterword#1 {#1 \bf} + +\def\opnr{% + \global\advance\parencount by 1 + {\parenfont(}% + \infirstlevel \bfafterword +} +\def\clnr{% + {\parenfont)}% + \infirstlevel \sl + \global\advance\parencount by -1 +} + +\newcount\brackcount +\def\lbrb{% + \global\advance\brackcount by 1 + {\bf[}% +} +\def\rbrb{% + {\bf]}% + \global\advance\brackcount by -1 +} + +\def\checkparencounts{% + \ifnum\parencount=0 \else \badparencount \fi + \ifnum\brackcount=0 \else \badbrackcount \fi +} +% these should not use \errmessage; the glibc manual, at least, actually +% has such constructs (when documenting function pointers). +\def\badparencount{% + \message{Warning: unbalanced parentheses in @def...}% + \global\parencount=0 +} +\def\badbrackcount{% + \message{Warning: unbalanced square brackets in @def...}% + \global\brackcount=0 +} + + +\message{macros,} +% @macro. + +% To do this right we need a feature of e-TeX, \scantokens, +% which we arrange to emulate with a temporary file in ordinary TeX. +\ifx\eTeXversion\thisisundefined + \newwrite\macscribble + \def\scantokens#1{% + \toks0={#1}% + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{\the\toks0}% + \immediate\closeout\macscribble + \input \jobname.tmp + } +\fi + +\let\E=\expandafter + +% Used at the time of macro expansion. +% Argument is macro body with arguments substituted +\def\scanmacro#1{% + \newlinechar`\^^M + % expand the expansion of \eatleadingcr twice to maybe remove a leading + % newline (and \else and \fi tokens), then call \eatspaces on the result. + \def\xeatspaces##1{% + \E\E\E\E\E\E\E\eatspaces\E\E\E\E\E\E\E{\eatleadingcr##1% + }}% + \def\xempty##1{}% + % + % Process the macro body under the current catcode regime. + \scantokens{#1@comment}% + % + % The \comment is to remove the \newlinechar added by \scantokens, and + % can be noticed by \parsearg. Note \c isn't used because this means cedilla + % in math mode. +} + +% Used for copying and captions +\def\scanexp#1{% + \expandafter\scanmacro\expandafter{#1}% +} + +\newcount\paramno % Count of parameters +\newtoks\macname % Macro name +\newif\ifrecursive % Is it recursive? + +% List of all defined macros in the form +% \commondummyword\macro1\commondummyword\macro2... +% Currently is also contains all @aliases; the list can be split +% if there is a need. +\def\macrolist{} + +% Add the macro to \macrolist +\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} +\def\addtomacrolistxxx#1{% + \toks0 = \expandafter{\macrolist\commondummyword#1}% + \xdef\macrolist{\the\toks0}% +} + +% Utility routines. +% This does \let #1 = #2, with \csnames; that is, +% \let \csname#1\endcsname = \csname#2\endcsname +% (except of course we have to play expansion games). +% +\def\cslet#1#2{% + \expandafter\let + \csname#1\expandafter\endcsname + \csname#2\endcsname +} + +% Trim leading and trailing spaces off a string. +% Concepts from aro-bend problem 15 (see CTAN). +{\catcode`\@=11 +\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} +\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} +\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} +\def\unbrace#1{#1} +\unbrace{\gdef\trim@@@ #1 } #2@{#1} +} + +{\catcode`\^^M=\other% +\gdef\eatleadingcr#1{\if\noexpand#1\noexpand^^M\else\E#1\fi}}% +% Warning: this won't work for a delimited argument +% or for an empty argument + +% Trim a single trailing ^^M off a string. +{\catcode`\^^M=\other \catcode`\Q=3% +\gdef\eatcr #1{\eatcra #1Q^^MQ}% +\gdef\eatcra#1^^MQ{\eatcrb#1Q}% +\gdef\eatcrb#1Q#2Q{#1}% +} + +% Macro bodies are absorbed as an argument in a context where +% all characters are catcode 10, 11 or 12, except \ which is active +% (as in normal texinfo). It is necessary to change the definition of \ +% to recognize macro arguments; this is the job of \mbodybackslash. +% +% Non-ASCII encodings make 8-bit characters active, so un-activate +% them to avoid their expansion. Must do this non-globally, to +% confine the change to the current group. +% +% It's necessary to have hard CRs when the macro is executed. This is +% done by making ^^M (\endlinechar) catcode 12 when reading the macro +% body, and then making it the \newlinechar in \scanmacro. +% +\def\scanctxt{% used as subroutine + \catcode`\"=\other + \catcode`\+=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\^=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\~=\other + \passthroughcharstrue +} + +\def\scanargctxt{% used for copying and captions, not macros. + \scanctxt + \catcode`\@=\other + \catcode`\\=\other + \catcode`\^^M=\other +} + +\def\macrobodyctxt{% used for @macro definitions + \scanctxt + \catcode`\ =\other + \catcode`\@=\other + \catcode`\{=\other + \catcode`\}=\other + \catcode`\^^M=\other + \usembodybackslash +} + +% Used when scanning braced macro arguments. Note, however, that catcode +% changes here are ineffectual if the macro invocation was nested inside +% an argument to another Texinfo command. +\def\macroargctxt{% + \scanctxt + \catcode`\ =\active + \catcode`\@=\other + \catcode`\^^M=\other + \catcode`\\=\active +} + +\def\macrolineargctxt{% used for whole-line arguments without braces + \scanctxt + \catcode`\@=\other + \catcode`\{=\other + \catcode`\}=\other +} + +% \mbodybackslash is the definition of \ in @macro bodies. +% It maps \foo\ => \csname macarg.foo\endcsname => #N +% where N is the macro parameter number. +% We define \csname macarg.\endcsname to be \realbackslash, so +% \\ in macro replacement text gets you a backslash. +% +{\catcode`@=0 @catcode`@\=@active + @gdef@usembodybackslash{@let\=@mbodybackslash} + @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} +} +\expandafter\def\csname macarg.\endcsname{\realbackslash} + +\def\margbackslash#1{\char`\#1 } + +\def\macro{\recursivefalse\parsearg\macroxxx} +\def\rmacro{\recursivetrue\parsearg\macroxxx} + +\def\macroxxx#1{% + \getargs{#1}% now \macname is the macname and \argl the arglist + \ifx\argl\empty % no arguments + \paramno=0\relax + \else + \expandafter\parsemargdef \argl;% + \if\paramno>256\relax + \ifx\eTeXversion\thisisundefined + \errhelp = \EMsimple + \errmessage{You need eTeX to compile a file with macros with more than 256 arguments} + \fi + \fi + \fi + \if1\csname ismacro.\the\macname\endcsname + \message{Warning: redefining \the\macname}% + \else + \expandafter\ifx\csname \the\macname\endcsname \relax + \else \errmessage{Macro name \the\macname\space already defined}\fi + \global\cslet{macsave.\the\macname}{\the\macname}% + \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% + \addtomacrolist{\the\macname}% + \fi + \begingroup \macrobodyctxt + \ifrecursive \expandafter\parsermacbody + \else \expandafter\parsemacbody + \fi} + +\parseargdef\unmacro{% + \if1\csname ismacro.#1\endcsname + \global\cslet{#1}{macsave.#1}% + \global\expandafter\let \csname ismacro.#1\endcsname=0% + % Remove the macro name from \macrolist: + \begingroup + \expandafter\let\csname#1\endcsname \relax + \let\commondummyword\unmacrodo + \xdef\macrolist{\macrolist}% + \endgroup + \else + \errmessage{Macro #1 not defined}% + \fi +} + +% Called by \do from \dounmacro on each macro. The idea is to omit any +% macro definitions that have been changed to \relax. +% +\def\unmacrodo#1{% + \ifx #1\relax + % remove this + \else + \noexpand\commondummyword \noexpand#1% + \fi +} + +% \getargs -- Parse the arguments to a @macro line. Set \macname to +% the name of the macro, and \argl to the braced argument list. +\def\getargs#1{\getargsxxx#1{}} +\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} +\def\getmacname#1 #2\relax{\macname={#1}} +\def\getmacargs#1{\def\argl{#1}} +% This made use of the feature that if the last token of a +% <parameter list> is #, then the preceding argument is delimited by +% an opening brace, and that opening brace is not consumed. + +% Parse the optional {params} list to @macro or @rmacro. +% Set \paramno to the number of arguments, +% and \paramlist to a parameter text for the macro (e.g. #1,#2,#3 for a +% three-param macro.) Define \macarg.BLAH for each BLAH in the params +% list to some hook where the argument is to be expanded. If there are +% less than 10 arguments that hook is to be replaced by ##N where N +% is the position in that list, that is to say the macro arguments are to be +% defined `a la TeX in the macro body. +% +% That gets used by \mbodybackslash (above). +% +% If there are 10 or more arguments, a different technique is used: see +% \parsemmanyargdef. +% +\def\parsemargdef#1;{% + \paramno=0\def\paramlist{}% + \let\hash\relax + % \hash is redefined to `#' later to get it into definitions + \let\xeatspaces\relax + \let\xempty\relax + \parsemargdefxxx#1,;,% + \ifnum\paramno<10\relax\else + \paramno0\relax + \parsemmanyargdef@@#1,;,% 10 or more arguments + \fi +} +\def\parsemargdefxxx#1,{% + \if#1;\let\next=\relax + \else \let\next=\parsemargdefxxx + \advance\paramno by 1 + \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname + {\xeatspaces{\hash\the\paramno\noexpand\xempty{}}}% + \edef\paramlist{\paramlist\hash\the\paramno,}% + \fi\next} +% the \xempty{} is to give \eatleadingcr an argument in the case of an +% empty macro argument. + +% \parsemacbody, \parsermacbody +% +% Read recursive and nonrecursive macro bodies. (They're different since +% rec and nonrec macros end differently.) +% +% We are in \macrobodyctxt, and the \xdef causes backslashshes in the macro +% body to be transformed. +% Set \macrobody to the body of the macro, and call \defmacro. +% +{\catcode`\ =\other\long\gdef\parsemacbody#1@end macro{% +\xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}% +{\catcode`\ =\other\long\gdef\parsermacbody#1@end rmacro{% +\xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}% + +% Make @ a letter, so that we can make private-to-Texinfo macro names. +\edef\texiatcatcode{\the\catcode`\@} +\catcode `@=11\relax + +%%%%%%%%%%%%%% Code for > 10 arguments only %%%%%%%%%%%%%%%%%% + +% If there are 10 or more arguments, a different technique is used, where the +% hook remains in the body, and when macro is to be expanded the body is +% processed again to replace the arguments. +% +% In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the +% argument N value and then \edef the body (nothing else will expand because of +% the catcode regime under which the body was input). +% +% If you compile with TeX (not eTeX), and you have macros with 10 or more +% arguments, no macro can have more than 256 arguments (else error). +% +% In case that there are 10 or more arguments we parse again the arguments +% list to set new definitions for the \macarg.BLAH macros corresponding to +% each BLAH argument. It was anyhow needed to parse already once this list +% in order to count the arguments, and as macros with at most 9 arguments +% are by far more frequent than macro with 10 or more arguments, defining +% twice the \macarg.BLAH macros does not cost too much processing power. +\def\parsemmanyargdef@@#1,{% + \if#1;\let\next=\relax + \else + \let\next=\parsemmanyargdef@@ + \edef\tempb{\eatspaces{#1}}% + \expandafter\def\expandafter\tempa + \expandafter{\csname macarg.\tempb\endcsname}% + % Note that we need some extra \noexpand\noexpand, this is because we + % don't want \the to be expanded in the \parsermacbody as it uses an + % \xdef . + \expandafter\edef\tempa + {\noexpand\noexpand\noexpand\the\toks\the\paramno}% + \advance\paramno by 1\relax + \fi\next} + + +\let\endargs@\relax +\let\nil@\relax +\def\nilm@{\nil@}% +\long\def\nillm@{\nil@}% + +% This macro is expanded during the Texinfo macro expansion, not during its +% definition. It gets all the arguments' values and assigns them to macros +% macarg.ARGNAME +% +% #1 is the macro name +% #2 is the list of argument names +% #3 is the list of argument values +\def\getargvals@#1#2#3{% + \def\macargdeflist@{}% + \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion. + \def\paramlist{#2,\nil@}% + \def\macroname{#1}% + \begingroup + \macroargctxt + \def\argvaluelist{#3,\nil@}% + \def\@tempa{#3}% + \ifx\@tempa\empty + \setemptyargvalues@ + \else + \getargvals@@ + \fi +} +\def\getargvals@@{% + \ifx\paramlist\nilm@ + % Some sanity check needed here that \argvaluelist is also empty. + \ifx\argvaluelist\nillm@ + \else + \errhelp = \EMsimple + \errmessage{Too many arguments in macro `\macroname'!}% + \fi + \let\next\macargexpandinbody@ + \else + \ifx\argvaluelist\nillm@ + % No more arguments values passed to macro. Set remaining named-arg + % macros to empty. + \let\next\setemptyargvalues@ + \else + % pop current arg name into \@tempb + \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}% + \expandafter\@tempa\expandafter{\paramlist}% + % pop current argument value into \@tempc + \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}% + \expandafter\@tempa\expandafter{\argvaluelist}% + % Here \@tempb is the current arg name and \@tempc is the current arg value. + % First place the new argument macro definition into \@tempd + \expandafter\macname\expandafter{\@tempc}% + \expandafter\let\csname macarg.\@tempb\endcsname\relax + \expandafter\def\expandafter\@tempe\expandafter{% + \csname macarg.\@tempb\endcsname}% + \edef\@tempd{\long\def\@tempe{\the\macname}}% + \push@\@tempd\macargdeflist@ + \let\next\getargvals@@ + \fi + \fi + \next +} + +\def\push@#1#2{% + \expandafter\expandafter\expandafter\def + \expandafter\expandafter\expandafter#2% + \expandafter\expandafter\expandafter{% + \expandafter#1#2}% +} + +% Replace arguments by their values in the macro body, and place the result +% in macro \@tempa. +% +\def\macvalstoargs@{% + % To do this we use the property that token registers that are \the'ed + % within an \edef expand only once. So we are going to place all argument + % values into respective token registers. + % + % First we save the token context, and initialize argument numbering. + \begingroup + \paramno0\relax + % Then, for each argument number #N, we place the corresponding argument + % value into a new token list register \toks#N + \expandafter\putargsintokens@\saveparamlist@,;,% + % Then, we expand the body so that argument are replaced by their + % values. The trick for values not to be expanded themselves is that they + % are within tokens and that tokens expand only once in an \edef . + \edef\@tempc{\csname mac.\macroname .body\endcsname}% + % Now we restore the token stack pointer to free the token list registers + % which we have used, but we make sure that expanded body is saved after + % group. + \expandafter + \endgroup + \expandafter\def\expandafter\@tempa\expandafter{\@tempc}% + } + +% Define the named-macro outside of this group and then close this group. +% +\def\macargexpandinbody@{% + \expandafter + \endgroup + \macargdeflist@ + % First the replace in body the macro arguments by their values, the result + % is in \@tempa . + \macvalstoargs@ + % Then we point at the \norecurse or \gobble (for recursive) macro value + % with \@tempb . + \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname + % Depending on whether it is recursive or not, we need some tailing + % \egroup . + \ifx\@tempb\gobble + \let\@tempc\relax + \else + \let\@tempc\egroup + \fi + % And now we do the real job: + \edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}% + \@tempd +} + +\def\putargsintokens@#1,{% + \if#1;\let\next\relax + \else + \let\next\putargsintokens@ + % First we allocate the new token list register, and give it a temporary + % alias \@tempb . + \toksdef\@tempb\the\paramno + % Then we place the argument value into that token list register. + \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname + \expandafter\@tempb\expandafter{\@tempa}% + \advance\paramno by 1\relax + \fi + \next +} + +% Trailing missing arguments are set to empty. +% +\def\setemptyargvalues@{% + \ifx\paramlist\nilm@ + \let\next\macargexpandinbody@ + \else + \expandafter\setemptyargvaluesparser@\paramlist\endargs@ + \let\next\setemptyargvalues@ + \fi + \next +} + +\def\setemptyargvaluesparser@#1,#2\endargs@{% + \expandafter\def\expandafter\@tempa\expandafter{% + \expandafter\def\csname macarg.#1\endcsname{}}% + \push@\@tempa\macargdeflist@ + \def\paramlist{#2}% +} + +% #1 is the element target macro +% #2 is the list macro +% #3,#4\endargs@ is the list value +\def\pop@#1#2#3,#4\endargs@{% + \def#1{#3}% + \def#2{#4}% +} +\long\def\longpop@#1#2#3,#4\endargs@{% + \long\def#1{#3}% + \long\def#2{#4}% +} + + +%%%%%%%%%%%%%% End of code for > 10 arguments %%%%%%%%%%%%%%%%%% + + +% This defines a Texinfo @macro or @rmacro, called by \parsemacbody. +% \macrobody has the body of the macro in it, with placeholders for +% its parameters, looking like "\xeatspaces{\hash 1}". +% \paramno is the number of parameters +% \paramlist is a TeX parameter text, e.g. "#1,#2,#3," +% There are four cases: macros of zero, one, up to nine, and many arguments. +% \xdef is used so that macro definitions will survive the file +% they're defined in: @include reads the file inside a group. +% +\def\defmacro{% + \let\hash=##% convert placeholders to macro parameter chars + \ifnum\paramno=1 + \def\xeatspaces##1{##1}% + % This removes the pair of braces around the argument. We don't + % use \eatspaces, because this can cause ends of lines to be lost + % when the argument to \eatspaces is read, leading to line-based + % commands like "@itemize" not being read correctly. + \else + \let\xeatspaces\relax % suppress expansion + \fi + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup + \noexpand\spaceisspace + \noexpand\endlineisspace + \noexpand\expandafter % skip any whitespace after the macro name. + \expandafter\noexpand\csname\the\macname @@@\endcsname}% + \expandafter\xdef\csname\the\macname @@@\endcsname{% + \egroup + \noexpand\scanmacro{\macrobody}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname @@@\endcsname}% + \expandafter\xdef\csname\the\macname @@@\endcsname##1{% + \egroup + \noexpand\scanmacro{\macrobody}% + }% + \else % at most 9 + \ifnum\paramno<10\relax + % @MACNAME sets the context for reading the macro argument + % @MACNAME@@ gets the argument, processes backslashes and appends a + % comma. + % @MACNAME@@@ removes braces surrounding the argument list. + % @MACNAME@@@@ scans the macro body with arguments substituted. + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup + \noexpand\expandafter % This \expandafter skip any spaces after the + \noexpand\macroargctxt % macro before we change the catcode of space. + \noexpand\expandafter + \expandafter\noexpand\csname\the\macname @@\endcsname}% + \expandafter\xdef\csname\the\macname @@\endcsname##1{% + \noexpand\passargtomacro + \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}% + \expandafter\xdef\csname\the\macname @@@\endcsname##1{% + \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname @@@@\endcsname\paramlist{% + \egroup\noexpand\scanmacro{\macrobody}}% + \else % 10 or more: + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\getargvals@{\the\macname}{\argl}% + }% + \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody + \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble + \fi + \fi} + +\catcode `\@\texiatcatcode\relax % end private-to-Texinfo catcodes + +\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +{\catcode`\@=0 \catcode`\\=13 % We need to manipulate \ so use @ as escape +@catcode`@_=11 % private names +@catcode`@!=11 % used as argument separator + +% \passargtomacro#1#2 - +% Call #1 with a list of tokens #2, with any doubled backslashes in #2 +% compressed to one. +% +% This implementation works by expansion, and not execution (so we cannot use +% \def or similar). This reduces the risk of this failing in contexts where +% complete expansion is done with no execution (for example, in writing out to +% an auxiliary file for an index entry). +% +% State is kept in the input stream: the argument passed to +% @look_ahead, @gobble_and_check_finish and @add_segment is +% +% THE_MACRO ARG_RESULT ! {PENDING_BS} NEXT_TOKEN (... rest of input) +% +% where: +% THE_MACRO - name of the macro we want to call +% ARG_RESULT - argument list we build to pass to that macro +% PENDING_BS - either a backslash or nothing +% NEXT_TOKEN - used to look ahead in the input stream to see what's coming next + +@gdef@passargtomacro#1#2{% + @add_segment #1!{}@relax#2\@_finish\% +} +@gdef@_finish{@_finishx} @global@let@_finishx@relax + +% #1 - THE_MACRO ARG_RESULT +% #2 - PENDING_BS +% #3 - NEXT_TOKEN +% #4 used to look ahead +% +% If the next token is not a backslash, process the rest of the argument; +% otherwise, remove the next token. +@gdef@look_ahead#1!#2#3#4{% + @ifx#4\% + @expandafter@gobble_and_check_finish + @else + @expandafter@add_segment + @fi#1!{#2}#4#4% +} + +% #1 - THE_MACRO ARG_RESULT +% #2 - PENDING_BS +% #3 - NEXT_TOKEN +% #4 should be a backslash, which is gobbled. +% #5 looks ahead +% +% Double backslash found. Add a single backslash, and look ahead. +@gdef@gobble_and_check_finish#1!#2#3#4#5{% + @add_segment#1\!{}#5#5% +} + +@gdef@is_fi{@fi} + +% #1 - THE_MACRO ARG_RESULT +% #2 - PENDING_BS +% #3 - NEXT_TOKEN +% #4 is input stream until next backslash +% +% Input stream is either at the start of the argument, or just after a +% backslash sequence, either a lone backslash, or a doubled backslash. +% NEXT_TOKEN contains the first token in the input stream: if it is \finish, +% finish; otherwise, append to ARG_RESULT the segment of the argument up until +% the next backslash. PENDING_BACKSLASH contains a backslash to represent +% a backslash just before the start of the input stream that has not been +% added to ARG_RESULT. +@gdef@add_segment#1!#2#3#4\{% +@ifx#3@_finish + @call_the_macro#1!% +@else + % append the pending backslash to the result, followed by the next segment + @expandafter@is_fi@look_ahead#1#2#4!{\}@fi + % this @fi is discarded by @look_ahead. + % we can't get rid of it with \expandafter because we don't know how + % long #4 is. +} + +% #1 - THE_MACRO +% #2 - ARG_RESULT +% #3 discards the res of the conditional in @add_segment, and @is_fi ends the +% conditional. +@gdef@call_the_macro#1#2!#3@fi{@is_fi #1{#2}} + +} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% \braceorline MAC is used for a one-argument macro MAC. It checks +% whether the next non-whitespace character is a {. It sets the context +% for reading the argument (slightly different in the two cases). Then, +% to read the argument, in the whole-line case, it then calls the regular +% \parsearg MAC; in the lbrace case, it calls \passargtomacro MAC. +% +\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} +\def\braceorlinexxx{% + \ifx\nchar\bgroup + \macroargctxt + \expandafter\passargtomacro + \else + \macrolineargctxt\expandafter\parsearg + \fi \macnamexxx} + + +% @alias. +% We need some trickery to remove the optional spaces around the equal +% sign. Make them active and then expand them all to nothing. +% +\def\alias{\parseargusing\obeyspaces\aliasxxx} +\def\aliasxxx #1{\aliasyyy#1\relax} +\def\aliasyyy #1=#2\relax{% + {% + \expandafter\let\obeyedspace=\empty + \addtomacrolist{#1}% + \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% + }% + \next +} + + +\message{cross references,} + +\newwrite\auxfile +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is relatively simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{% + \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% @node's only job in TeX is to define \lastnode, which is used in +% cross-references. The @node line might or might not have commas, and +% might or might not have spaces before the first comma, like: +% @node foo , bar , ... +% We don't want such trailing spaces in the node name. +% +\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} +% +% also remove a trailing comma, in case of something like this: +% @node Help-Cross, , , Cross-refs +\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} +\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}\omittopnode} + +% Used so that the @top node doesn't have to be wrapped in an @ifnottex +% conditional. +% \doignore goes to more effort to skip nested conditionals but we don't need +% that here. +\def\omittopnode{% + \ifx\lastnode\wordTop + \expandafter\ignorenode\fi +} +\def\wordTop{Top} + +% Until the next @node or @bye command, divert output to a box that is not +% output. +\def\ignorenode{\setbox\dummybox\vbox\bgroup\def\node{\egroup\node}% +\ignorenodebye +} + +{\let\bye\relax +\gdef\ignorenodebye{\let\bye\ignorenodebyedef} +\gdef\ignorenodebyedef{\egroup(`Top' node ignored)\bye}} +% The redefinition of \bye here is because it is declared \outer + +\let\lastnode=\empty + +% Write a cross-reference definition for the current node. #1 is the +% type (Ynumbered, Yappendix, Ynothing). +% +\def\donoderef#1{% + \ifx\lastnode\empty\else + \setref{\lastnode}{#1}% + \global\let\lastnode=\empty + \fi +} + +% @anchor{NAME} -- define xref target at arbitrary point. +% +\newcount\savesfregister +% +\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} +\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} +\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} + +% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an +% anchor), which consists of three parts: +% 1) NAME-title - the current sectioning name taken from \currentsection, +% or the anchor name. +% 2) NAME-snt - section number and type, passed as the SNT arg, or +% empty for anchors. +% 3) NAME-pg - the page number. +% +% This is called from \donoderef, \anchor, and \dofloat. In the case of +% floats, there is an additional part, which is not written here: +% 4) NAME-lof - the text as it should appear in a @listoffloats. +% +\def\setref#1#2{% + \pdfmkdest{#1}% + \iflinks + {% + \requireauxfile + \atdummies % preserve commands, but don't expand them + % match definition in \xrdef, \refx, \xrefX. + \def\value##1{##1}% + \edef\writexrdef##1##2{% + \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef + ##1}{##2}}% these are parameters of \writexrdef + }% + \toks0 = \expandafter{\currentsection}% + \immediate \writexrdef{title}{\the\toks0 }% + \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. + \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout + }% + \fi +} + +% @xrefautosectiontitle on|off says whether @section(ing) names are used +% automatically in xrefs, if the third arg is not explicitly specified. +% This was provided as a "secret" @set xref-automatic-section-title +% variable, now it's official. +% +\parseargdef\xrefautomaticsectiontitle{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETxref-automatic-section-title\endcsname + = \empty + \else\ifx\temp\offword + \expandafter\let\csname SETxref-automatic-section-title\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @xrefautomaticsectiontitle value `\temp', + must be on|off}% + \fi\fi +} + +% +% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is +% the node name, #2 the name of the Info cross-reference, #3 the printed +% node name, #4 the name of the Info file, #5 the name of the printed +% manual. All but the node name can be omitted. +% +\def\pxref{\putwordsee{} \xrefXX} +\def\xref{\putwordSee{} \xrefXX} +\def\ref{\xrefXX} + +\def\xrefXX#1{\def\xrefXXarg{#1}\futurelet\tokenafterxref\xrefXXX} +\def\xrefXXX{\expandafter\xrefX\expandafter[\xrefXXarg,,,,,,,]} +% +\newbox\toprefbox +\newbox\printedrefnamebox +\newbox\infofilenamebox +\newbox\printedmanualbox +% +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \unsepspaces + % + % Get args without leading/trailing spaces. + \def\printedrefname{\ignorespaces #3}% + \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}% + % + \def\infofilename{\ignorespaces #4}% + \setbox\infofilenamebox = \hbox{\infofilename\unskip}% + % + \def\printedmanual{\ignorespaces #5}% + \setbox\printedmanualbox = \hbox{\printedmanual\unskip}% + % + % If the printed reference name (arg #3) was not explicitly given in + % the @xref, figure out what we want to use. + \ifdim \wd\printedrefnamebox = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax + % Not auto section-title: use node name inside the square brackets. + \def\printedrefname{\ignorespaces #1}% + \else + % Auto section-title: use chapter/section title inside + % the square brackets if we have it. + \ifdim \wd\printedmanualbox > 0pt + % It is in another manual, so we don't have it; use node name. + \def\printedrefname{\ignorespaces #1}% + \else + \ifhavexrefs + % We (should) know the real title if we have the xref values. + \def\printedrefname{\refx{#1-title}}% + \else + % Otherwise just copy the Info node name. + \def\printedrefname{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % Make link in pdf output. + \ifpdf + % For pdfTeX and LuaTeX + {\indexnofonts + \makevalueexpandable + \turnoffactive + % This expands tokens, so do it after making catcode changes, so _ + % etc. don't get their TeX definitions. This ignores all spaces in + % #4, including (wrongly) those in the middle of the filename. + \getfilename{#4}% + % + % This (wrongly) does not take account of leading or trailing + % spaces in #1, which should be ignored. + \setpdfdestname{#1}% + % + \ifx\pdfdestname\empty + \def\pdfdestname{Top}% no empty targets + \fi + % + \leavevmode + \startlink attr{/Border [0 0 0]}% + \ifnum\filenamelength>0 + goto file{\the\filename.pdf} name{\pdfdestname}% + \else + goto name{\pdfmkpgn{\pdfdestname}}% + \fi + }% + \setcolor{\linkcolor}% + \else + \ifx\XeTeXrevision\thisisundefined + \else + % For XeTeX + {\indexnofonts + \makevalueexpandable + \turnoffactive + % This expands tokens, so do it after making catcode changes, so _ + % etc. don't get their TeX definitions. This ignores all spaces in + % #4, including (wrongly) those in the middle of the filename. + \getfilename{#4}% + % + % This (wrongly) does not take account of leading or trailing + % spaces in #1, which should be ignored. + \setpdfdestname{#1}% + % + \ifx\pdfdestname\empty + \def\pdfdestname{Top}% no empty targets + \fi + % + \leavevmode + \ifnum\filenamelength>0 + % With default settings, + % XeTeX (xdvipdfmx) replaces link destination names with integers. + % In this case, the replaced destination names of + % remote PDFs are no longer known. In order to avoid a replacement, + % you can use xdvipdfmx's command line option `-C 0x0010'. + % If you use XeTeX 0.99996+ (TeX Live 2016+), + % this command line option is no longer necessary + % because we can use the `dvipdfmx:config' special. + \special{pdf:bann << /Border [0 0 0] /Type /Annot /Subtype /Link /A + << /S /GoToR /F (\the\filename.pdf) /D (\pdfdestname) >> >>}% + \else + \special{pdf:bann << /Border [0 0 0] /Type /Annot /Subtype /Link /A + << /S /GoTo /D (\pdfdestname) >> >>}% + \fi + }% + \setcolor{\linkcolor}% + \fi + \fi + {% + % Have to otherify everything special to allow the \csname to + % include an _ in the xref name, etc. + \indexnofonts + \turnoffactive + \def\value##1{##1}% + \expandafter\global\expandafter\let\expandafter\Xthisreftitle + \csname XR#1-title\endcsname + }% + % + % Float references are printed completely differently: "Figure 1.2" + % instead of "[somenode], p.3". \iffloat distinguishes them by + % \Xthisreftitle being set to a magic string. + \iffloat\Xthisreftitle + % If the user specified the print name (third arg) to the ref, + % print it instead of our usual "Figure 1.2". + \ifdim\wd\printedrefnamebox = 0pt + \refx{#1-snt}% + \else + \printedrefname + \fi + % + % If the user also gave the printed manual name (fifth arg), append + % "in MANUALNAME". + \ifdim \wd\printedmanualbox > 0pt + \space \putwordin{} \cite{\printedmanual}% + \fi + \else + % node/anchor (non-float) references. + % + % If we use \unhbox to print the node names, TeX does not insert + % empty discretionaries after hyphens, which means that it will not + % find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, + % this is a loss. Therefore, we give the text of the node name + % again, so it is as if TeX is seeing it for the first time. + % + \ifdim \wd\printedmanualbox > 0pt + % Cross-manual reference with a printed manual name. + % + \crossmanualxref{\cite{\printedmanual\unskip}}% + % + \else\ifdim \wd\infofilenamebox > 0pt + % Cross-manual reference with only an info filename (arg 4), no + % printed manual name (arg 5). This is essentially the same as + % the case above; we output the filename, since we have nothing else. + % + \crossmanualxref{\code{\infofilename\unskip}}% + % + \else + % Reference within this manual. + % + % Only output a following space if the -snt ref is nonempty, as the ref + % will be empty for @unnumbered and @anchor. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + % + % output the `[mynode]' via the macro below so it can be overridden. + \xrefprintnodename\printedrefname + % + \expandafter\ifx\csname SETtxiomitxrefpg\endcsname\relax + % But we always want a comma and a space: + ,\space + % + % output the `page 3'. + \turnoffactive \putwordpage\tie\refx{#1-pg}% + % Add a , if xref followed by a space + \if\space\noexpand\tokenafterxref ,% + \else\ifx\ \tokenafterxref ,% @TAB + \else\ifx\*\tokenafterxref ,% @* + \else\ifx\ \tokenafterxref ,% @SPACE + \else\ifx\ + \tokenafterxref ,% @NL + \else\ifx\tie\tokenafterxref ,% @tie + \fi\fi\fi\fi\fi\fi + \fi + \fi\fi + \fi + \endlink +\endgroup} + +% Output a cross-manual xref to #1. Used just above (twice). +% +% Only include the text "Section ``foo'' in" if the foo is neither +% missing or Top. Thus, @xref{,,,foo,The Foo Manual} outputs simply +% "see The Foo Manual", the idea being to refer to the whole manual. +% +% But, this being TeX, we can't easily compare our node name against the +% string "Top" while ignoring the possible spaces before and after in +% the input. By adding the arbitrary 7sp below, we make it much less +% likely that a real node name would have the same width as "Top" (e.g., +% in a monospaced font). Hopefully it will never happen in practice. +% +% For the same basic reason, we retypeset the "Top" at every +% reference, since the current font is indeterminate. +% +\def\crossmanualxref#1{% + \setbox\toprefbox = \hbox{Top\kern7sp}% + \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}% + \ifdim \wd2 > 7sp % nonempty? + \ifdim \wd2 = \wd\toprefbox \else % same as Top? + \putwordSection{} ``\printedrefname'' \putwordin{}\space + \fi + \fi + #1% +} + +% This macro is called from \xrefX for the `[nodename]' part of xref +% output. It's a separate macro only so it can be changed more easily, +% since square brackets don't work well in some documents. Particularly +% one that Bob is working on :). +% +\def\xrefprintnodename#1{[#1]} + +% Things referred to by \setref. +% +\def\Ynothing{} +\def\Yomitfromtoc{} +\def\Ynumbered{% + \ifnum\secno=0 + \putwordChapter@tie \the\chapno + \else \ifnum\subsecno=0 + \putwordSection@tie \the\chapno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno + \else + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} +\def\Yappendix{% + \ifnum\secno=0 + \putwordAppendix@tie @char\the\appendixno{}% + \else \ifnum\subsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno + \else + \putwordSection@tie + @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} + +% \refx{NAME} - reference a cross-reference string named NAME. +\def\refx#1{% + \requireauxfile + {% + \indexnofonts + \turnoffactive + \def\value##1{##1}% + \expandafter\global\expandafter\let\expandafter\thisrefX + \csname XR#1\endcsname + }% + \ifx\thisrefX\relax + % If not defined, say something at least. + \angleleft un\-de\-fined\angleright + \iflinks + \ifhavexrefs + {\toks0 = {#1}% avoid expansion of possibly-complex value + \message{\linenumber Undefined cross reference `\the\toks0'.}}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \fi + \else + % It's defined, so just use it. + \thisrefX + \fi +} + +% This is the macro invoked by entries in the aux file. Define a control +% sequence for a cross-reference target (we prepend XR to the control sequence +% name to avoid collisions). The value is the page number. If this is a float +% type, we have more work to do. +% +\def\xrdef#1#2{% + {% Expand the node or anchor name to remove control sequences. + % \turnoffactive stops 8-bit characters being changed to commands + % like @'e. \refx does the same to retrieve the value in the definition. + \indexnofonts + \turnoffactive + \def\value##1{##1}% + \xdef\safexrefname{#1}% + }% + % + \bgroup + \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% + \egroup + % We put the \gdef inside a group to avoid the definitions building up on + % TeX's save stack, which can cause it to run out of space for aux files with + % thousands of lines. \gdef doesn't use the save stack, but \csname does + % when it defines an unknown control sequence as \relax. + % + % Was that xref control sequence that we just defined for a float? + \expandafter\iffloat\csname XR\safexrefname\endcsname + % it was a float, and we have the (safe) float type in \iffloattype. + \expandafter\let\expandafter\floatlist + \csname floatlist\iffloattype\endcsname + % + % Is this the first time we've seen this float type? + \expandafter\ifx\floatlist\relax + \toks0 = {\do}% yes, so just \do + \else + % had it before, so preserve previous elements in list. + \toks0 = \expandafter{\floatlist\do}% + \fi + % + % Remember this xref in the control sequence \floatlistFLOATTYPE, + % for later use in \listoffloats. + \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 + {\safexrefname}}% + \fi +} + +% If working on a large document in chapters, it is convenient to +% be able to disable indexing, cross-referencing, and contents, for test runs. +% This is done with @novalidate at the beginning of the file. +% +\newif\iflinks \linkstrue % by default we want the aux files. +\let\novalidate = \linksfalse + +% Used when writing to the aux file, or when using data from it. +\def\requireauxfile{% + \iflinks + \tryauxfile + % Open the new aux file. TeX will close it automatically at exit. + \immediate\openout\auxfile=\jobname.aux + \fi + \global\let\requireauxfile=\relax % Only do this once. +} + +% Read the last existing aux file, if any. No error if none exists. +% +\def\tryauxfile{% + \openin 1 \jobname.aux + \ifeof 1 \else + \readdatafile{aux}% + \global\havexrefstrue + \fi + \closein 1 +} + +\def\setupdatafile{% + \catcode`\^^@=\other + \catcode`\^^A=\other + \catcode`\^^B=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\^^K=\other + \catcode`\^^L=\other + \catcode`\^^N=\other + \catcode`\^^P=\other + \catcode`\^^Q=\other + \catcode`\^^R=\other + \catcode`\^^S=\other + \catcode`\^^T=\other + \catcode`\^^U=\other + \catcode`\^^V=\other + \catcode`\^^W=\other + \catcode`\^^X=\other + \catcode`\^^Z=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + \catcode`\^=\other + % + % Special characters. Should be turned off anyway, but... + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\active + \catcode`\|=\active + \catcode`\<=\active + \catcode`\>=\active + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + \catcode`\%=\other + \catcode`+=\other % avoid \+ for paranoia even though we've turned it off + % + \catcode`\\=\active + % + % @ is our escape character in .aux files, and we need braces. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\@=0 +} + +\def\readdatafile#1{% +\begingroup + \setupdatafile + \input\jobname.#1 +\endgroup} + + +\message{insertions,} +% including footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for Info output only. +\let\footnotestyle=\comment + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \dofootnote +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset (and anything else that uses +% \parseargline) fails inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\gdef\dofootnote{% + \insert\footins\bgroup + % + % Nested footnotes are not supported in TeX, that would take a lot + % more work. (\startsavinginserts does not suffice.) + \let\footnote=\errfootnotenest + % + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \hsize=\txipagewidth + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + \smallfonts \rm + % + % Because we use hanging indentation in footnotes, a @noindent appears + % to exdent this text, so make it be a no-op. makeinfo does not use + % hanging indentation so @noindent can still be needed within footnote + % text after an @example or the like (not that this is good style). + \let\noindent = \relax + % + % Hang the footnote text off the number. Use \everypar in case the + % footnote extends for more than one paragraph. + \everypar = {\hang}% + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + % + % Invoke rest of plain TeX footnote routine. + \futurelet\next\fo@t +} +}%end \catcode `\@=11 + +\def\errfootnotenest{% + \errhelp=\EMsimple + \errmessage{Nested footnotes not supported in texinfo.tex, + even though they work in makeinfo; sorry} +} + +\def\errfootnoteheading{% + \errhelp=\EMsimple + \errmessage{Footnotes in chapters, sections, etc., are not supported} +} + +% In case a @footnote appears in a vbox, save the footnote text and create +% the real \insert just after the vbox finished. Otherwise, the insertion +% would be lost. +% Similarly, if a @footnote appears inside an alignment, save the footnote +% text to a box and make the \insert when a row of the table is finished. +% And the same can be done for other insert classes. --kasal, 16nov03. +% +% Replace the \insert primitive by a cheating macro. +% Deeper inside, just make sure that the saved insertions are not spilled +% out prematurely. +% +\def\startsavinginserts{% + \ifx \insert\ptexinsert + \let\insert\saveinsert + \else + \let\checkinserts\relax + \fi +} + +% This \insert replacement works for both \insert\footins{foo} and +% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. +% +\def\saveinsert#1{% + \edef\next{\noexpand\savetobox \makeSAVEname#1}% + \afterassignment\next + % swallow the left brace + \let\temp = +} +\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} +\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} + +\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} + +\def\placesaveins#1{% + \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname + {\box#1}% +} + +% eat @SAVE -- beware, all of them have catcode \other: +{ + \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) + \gdef\gobblesave @SAVE{} +} + +% initialization: +\def\newsaveins #1{% + \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% + \next +} +\def\newsaveinsX #1{% + \csname newbox\endcsname #1% + \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts + \checksaveins #1}% +} + +% initialize: +\let\checkinserts\empty +\newsaveins\footins +\newsaveins\margin + + +% @image. We use the macros from epsf.tex to support this. +% If epsf.tex is not installed and @image is used, we complain. +% +% Check for and read epsf.tex up front. If we read it only at @image +% time, we might be inside a group, and then its definitions would get +% undone and the next image would fail. +\openin 1 = epsf.tex +\ifeof 1 \else + % Do not bother showing banner with epsf.tex v2.7k (available in + % doc/epsf.tex and on ctan). + \def\epsfannounce{\toks0 = }% + \input epsf.tex +\fi +\closein 1 +% +% We will only complain once about lack of epsf.tex. +\newif\ifwarnednoepsf +\newhelp\noepsfhelp{epsf.tex must be installed for images to + work. It is also included in the Texinfo distribution, or you can get + it from https://ctan.org/texarchive/macros/texinfo/texinfo/doc/epsf.tex.} +% +\def\image#1{% + \ifx\epsfbox\thisisundefined + \ifwarnednoepsf \else + \errhelp = \noepsfhelp + \errmessage{epsf.tex not found, images will be ignored}% + \global\warnednoepsftrue + \fi + \else + \imagexxx #1,,,,,\finish + \fi +} +% +% Arguments to @image: +% #1 is (mandatory) image filename; we tack on .eps extension. +% #2 is (optional) width, #3 is (optional) height. +% #4 is (ignored optional) html alt text. +% #5 is (ignored optional) extension. +% #6 is just the usual extra ignored arg for parsing stuff. +\newif\ifimagevmode +\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup + \catcode`\^^M = 5 % in case we're inside an example + \normalturnoffactive % allow _ et al. in names + \makevalueexpandable + % If the image is by itself, center it. + \ifvmode + \imagevmodetrue + \else \ifx\centersub\centerV + % for @center @image, we need a vbox so we can have our vertical space + \imagevmodetrue + \vbox\bgroup % vbox has better behavior than vtop herev + \fi\fi + % + \ifimagevmode + \nobreak\medskip + % Usually we'll have text after the image which will insert + % \parskip glue, so insert it here too to equalize the space + % above and below. + \nobreak\vskip\parskip + \nobreak + \fi + % + % Leave vertical mode so that indentation from an enclosing + % environment such as @quotation is respected. + % However, if we're at the top level, we don't want the + % normal paragraph indentation. + % On the other hand, if we are in the case of @center @image, we don't + % want to start a paragraph, which will create a hsize-width box and + % eradicate the centering. + \ifx\centersub\centerV\else \noindent \fi + % + % Output the image. + \ifpdf + % For pdfTeX and LuaTeX <= 0.80 + \dopdfimage{#1}{#2}{#3}% + \else + \ifx\XeTeXrevision\thisisundefined + % For epsf.tex + % \epsfbox itself resets \epsf?size at each figure. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt \epsfysize=#3\relax \fi + \epsfbox{#1.eps}% + \else + % For XeTeX + \doxeteximage{#1}{#2}{#3}% + \fi + \fi + % + \ifimagevmode + \medskip % space after a standalone image + \fi + \ifx\centersub\centerV \egroup \fi +\endgroup} + + +% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, +% etc. We don't actually implement floating yet, we always include the +% float "here". But it seemed the best name for the future. +% +\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} + +% There may be a space before second and/or third parameter; delete it. +\def\eatcommaspace#1, {#1,} + +% #1 is the optional FLOATTYPE, the text label for this float, typically +% "Figure", "Table", "Example", etc. Can't contain commas. If omitted, +% this float will not be numbered and cannot be referred to. +% +% #2 is the optional xref label. Also must be present for the float to +% be referable. +% +% #3 is the optional positioning argument; for now, it is ignored. It +% will somehow specify the positions allowed to float to (here, top, bottom). +% +% We keep a separate counter for each FLOATTYPE, which we reset at each +% chapter-level command. +\let\resetallfloatnos=\empty +% +\def\dofloat#1,#2,#3,#4\finish{% + \let\thiscaption=\empty + \let\thisshortcaption=\empty + % + % don't lose footnotes inside @float. + % + % BEWARE: when the floats start float, we have to issue warning whenever an + % insert appears inside a float which could possibly float. --kasal, 26may04 + % + \startsavinginserts + % + % We can't be used inside a paragraph. + \par + % + \vtop\bgroup + \def\floattype{#1}% + \def\floatlabel{#2}% + \def\floatloc{#3}% we do nothing with this yet. + % + \ifx\floattype\empty + \let\safefloattype=\empty + \else + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + \fi + % + % If label is given but no type, we handle that as the empty type. + \ifx\floatlabel\empty \else + % We want each FLOATTYPE to be numbered separately (Figure 1, + % Table 1, Figure 2, ...). (And if no label, no number.) + % + \expandafter\getfloatno\csname\safefloattype floatno\endcsname + \global\advance\floatno by 1 + % + {% + % This magic value for \currentsection is output by \setref as the + % XREFLABEL-title value. \xrefX uses it to distinguish float + % labels (which have a completely different output format) from + % node and anchor labels. And \xrdef uses it to construct the + % lists of floats. + % + \edef\currentsection{\floatmagic=\safefloattype}% + \setref{\floatlabel}{Yfloat}% + }% + \fi + % + % start with \parskip glue, I guess. + \vskip\parskip + % + % Don't suppress indentation if a float happens to start a section. + \restorefirstparagraphindent +} + +% we have these possibilities: +% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap +% @float Foo,lbl & no caption: Foo 1.1 +% @float Foo & @caption{Cap}: Foo: Cap +% @float Foo & no caption: Foo +% @float ,lbl & Caption{Cap}: 1.1: Cap +% @float ,lbl & no caption: 1.1 +% @float & @caption{Cap}: Cap +% @float & no caption: +% +\def\Efloat{% + \let\floatident = \empty + % + % In all cases, if we have a float type, it comes first. + \ifx\floattype\empty \else \def\floatident{\floattype}\fi + % + % If we have an xref label, the number comes next. + \ifx\floatlabel\empty \else + \ifx\floattype\empty \else % if also had float type, need tie first. + \appendtomacro\floatident{\tie}% + \fi + % the number. + \appendtomacro\floatident{\chaplevelprefix\the\floatno}% + \fi + % + % Start the printed caption with what we've constructed in + % \floatident, but keep it separate; we need \floatident again. + \let\captionline = \floatident + % + \ifx\thiscaption\empty \else + \ifx\floatident\empty \else + \appendtomacro\captionline{: }% had ident, so need a colon between + \fi + % + % caption text. + \appendtomacro\captionline{\scanexp\thiscaption}% + \fi + % + % If we have anything to print, print it, with space before. + % Eventually this needs to become an \insert. + \ifx\captionline\empty \else + \vskip.5\parskip + \captionline + % + % Space below caption. + \vskip\parskip + \fi + % + % If have an xref label, write the list of floats info. Do this + % after the caption, to avoid chance of it being a breakpoint. + \ifx\floatlabel\empty \else + % Write the text that goes in the lof to the aux file as + % \floatlabel-lof. Besides \floatident, we include the short + % caption if specified, else the full caption if specified, else nothing. + {% + \requireauxfile + \atdummies + % + \ifx\thisshortcaption\empty + \def\gtemp{\thiscaption}% + \else + \def\gtemp{\thisshortcaption}% + \fi + \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident + \ifx\gtemp\empty \else : \gtemp \fi}}% + }% + \fi + \egroup % end of \vtop + % + \checkinserts +} + +% Append the tokens #2 to the definition of macro #1, not expanding either. +% +\def\appendtomacro#1#2{% + \expandafter\def\expandafter#1\expandafter{#1#2}% +} + +% @caption, @shortcaption +% +\def\caption{\docaption\thiscaption} +\def\shortcaption{\docaption\thisshortcaption} +\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} +\def\defcaption#1#2{\egroup \def#1{#2}} + +% The parameter is the control sequence identifying the counter we are +% going to use. Create it if it doesn't exist and assign it to \floatno. +\def\getfloatno#1{% + \ifx#1\relax + % Haven't seen this figure type before. + \csname newcount\endcsname #1% + % + % Remember to reset this floatno at the next chap. + \expandafter\gdef\expandafter\resetallfloatnos + \expandafter{\resetallfloatnos #1=0 }% + \fi + \let\floatno#1% +} + +% \setref calls this to get the XREFLABEL-snt value. We want an @xref +% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we +% first read the @float command. +% +\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% + +% Magic string used for the XREFLABEL-title value, so \xrefX can +% distinguish floats from other xref types. +\def\floatmagic{!!float!!} + +% #1 is the control sequence we are passed; we expand into a conditional +% which is true if #1 represents a float ref. That is, the magic +% \currentsection value which we \setref above. +% +\def\iffloat#1{\expandafter\doiffloat#1==\finish} +% +% #1 is (maybe) the \floatmagic string. If so, #2 will be the +% (safe) float type for this float. We set \iffloattype to #2. +% +\def\doiffloat#1=#2=#3\finish{% + \def\temp{#1}% + \def\iffloattype{#2}% + \ifx\temp\floatmagic +} + +% @listoffloats FLOATTYPE - print a list of floats like a table of contents. +% +\parseargdef\listoffloats{% + \def\floattype{#1}% floattype + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + % + % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. + \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax + \ifhavexrefs + % if the user said @listoffloats foo but never @float foo. + \message{\linenumber No `\safefloattype' floats to list.}% + \fi + \else + \begingroup + \leftskip=\tocindent % indent these entries like a toc + \let\do=\listoffloatsdo + \csname floatlist\safefloattype\endcsname + \endgroup + \fi +} + +% This is called on each entry in a list of floats. We're passed the +% xref label, in the form LABEL-title, which is how we save it in the +% aux file. We strip off the -title and look up \XRLABEL-lof, which +% has the text we're supposed to typeset here. +% +% Figures without xref labels will not be included in the list (since +% they won't appear in the aux file). +% +\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} +\def\listoffloatsdoentry#1-title\finish{{% + % Can't fully expand XR#1-lof because it can contain anything. Just + % pass the control sequence. On the other hand, XR#1-pg is just the + % page number, and we want to fully expand that so we can get a link + % in pdf output. + \toksA = \expandafter{\csname XR#1-lof\endcsname}% + % + % use the same \entry macro we use to generate the TOC and index. + \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% + \writeentry +}} + + +\message{localization,} + +% For single-language documents, @documentlanguage is usually given very +% early, just after @documentencoding. Single argument is the language +% (de) or locale (de_DE) abbreviation. +% +{ + \catcode`\_ = \active + \globaldefs=1 +\parseargdef\documentlanguage{% + \tex % read txi-??.tex file in plain TeX. + % Read the file by the name they passed if it exists. + \let_ = \normalunderscore % normal _ character for filename test + \openin 1 txi-#1.tex + \ifeof 1 + \documentlanguagetrywithoutunderscore #1_\finish + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 + \endgroup % end raw TeX +} +% +% If they passed de_DE, and txi-de_DE.tex doesn't exist, +% try txi-de.tex. +% +\gdef\documentlanguagetrywithoutunderscore#1_#2\finish{% + \openin 1 txi-#1.tex + \ifeof 1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 +} +}% end of special _ catcode +% +\newhelp\nolanghelp{The given language definition file cannot be found or +is empty. Maybe you need to install it? Putting it in the current +directory should work if nowhere else does.} + +% This macro is called from txi-??.tex files; the first argument is the +% \language name to set (without the "\lang@" prefix), the second and +% third args are \{left,right}hyphenmin. +% +% The language names to pass are determined when the format is built. +% See the etex.log file created at that time, e.g., +% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log. +% +% With TeX Live 2008, etex now includes hyphenation patterns for all +% available languages. This means we can support hyphenation in +% Texinfo, at least to some extent. (This still doesn't solve the +% accented characters problem.) +% +\catcode`@=11 +\def\txisetlanguage#1#2#3{% + % do not set the language if the name is undefined in the current TeX. + \expandafter\ifx\csname lang@#1\endcsname \relax + \message{no patterns for #1}% + \else + \global\language = \csname lang@#1\endcsname + \fi + % but there is no harm in adjusting the hyphenmin values regardless. + \global\lefthyphenmin = #2\relax + \global\righthyphenmin = #3\relax +} + +% XeTeX and LuaTeX can handle Unicode natively. +% Their default I/O uses UTF-8 sequences instead of a byte-wise operation. +% Other TeX engines' I/O (pdfTeX, etc.) is byte-wise. +% +\newif\iftxinativeunicodecapable +\newif\iftxiusebytewiseio + +\ifx\XeTeXrevision\thisisundefined + \ifx\luatexversion\thisisundefined + \txinativeunicodecapablefalse + \txiusebytewiseiotrue + \else + \txinativeunicodecapabletrue + \txiusebytewiseiofalse + \fi +\else + \txinativeunicodecapabletrue + \txiusebytewiseiofalse +\fi + +% Set I/O by bytes instead of UTF-8 sequence for XeTeX and LuaTex +% for non-UTF-8 (byte-wise) encodings. +% +\def\setbytewiseio{% + \ifx\XeTeXrevision\thisisundefined + \else + \XeTeXdefaultencoding "bytes" % For subsequent files to be read + \XeTeXinputencoding "bytes" % For document root file + % Unfortunately, there seems to be no corresponding XeTeX command for + % output encoding. This is a problem for auxiliary index and TOC files. + % The only solution would be perhaps to write out @U{...} sequences in + % place of non-ASCII characters. + \fi + + \ifx\luatexversion\thisisundefined + \else + \directlua{ + local utf8_char, byte, gsub = unicode.utf8.char, string.byte, string.gsub + local function convert_char (char) + return utf8_char(byte(char)) + end + + local function convert_line (line) + return gsub(line, ".", convert_char) + end + + callback.register("process_input_buffer", convert_line) + + local function convert_line_out (line) + local line_out = "" + for c in string.utfvalues(line) do + line_out = line_out .. string.char(c) + end + return line_out + end + + callback.register("process_output_buffer", convert_line_out) + } + \fi + + \txiusebytewiseiotrue +} + + +% Helpers for encodings. +% Set the catcode of characters 128 through 255 to the specified number. +% +\def\setnonasciicharscatcode#1{% + \count255=128 + \loop\ifnum\count255<256 + \global\catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +\def\setnonasciicharscatcodenonglobal#1{% + \count255=128 + \loop\ifnum\count255<256 + \catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +% @documentencoding sets the definition of non-ASCII characters +% according to the specified encoding. +% +\def\documentencoding{\parseargusing\filenamecatcodes\documentencodingzzz} +\def\documentencodingzzz#1{% + % + % Encoding being declared for the document. + \def\declaredencoding{\csname #1.enc\endcsname}% + % + % Supported encodings: names converted to tokens in order to be able + % to compare them with \ifx. + \def\ascii{\csname US-ASCII.enc\endcsname}% + \def\latnine{\csname ISO-8859-15.enc\endcsname}% + \def\latone{\csname ISO-8859-1.enc\endcsname}% + \def\lattwo{\csname ISO-8859-2.enc\endcsname}% + \def\utfeight{\csname UTF-8.enc\endcsname}% + % + \ifx \declaredencoding \ascii + \asciichardefs + % + \else \ifx \declaredencoding \lattwo + \iftxinativeunicodecapable + \setbytewiseio + \fi + \setnonasciicharscatcode\active + \lattwochardefs + % + \else \ifx \declaredencoding \latone + \iftxinativeunicodecapable + \setbytewiseio + \fi + \setnonasciicharscatcode\active + \latonechardefs + % + \else \ifx \declaredencoding \latnine + \iftxinativeunicodecapable + \setbytewiseio + \fi + \setnonasciicharscatcode\active + \latninechardefs + % + \else \ifx \declaredencoding \utfeight + \iftxinativeunicodecapable + % For native Unicode handling (XeTeX and LuaTeX) + \nativeunicodechardefs + \else + % For treating UTF-8 as byte sequences (TeX, eTeX and pdfTeX) + \setnonasciicharscatcode\active + % since we already invoked \utfeightchardefs at the top level + % (below), do not re-invoke it, otherwise our check for duplicated + % definitions gets triggered. Making non-ascii chars active is + % sufficient. + \fi + % + \else + \message{Ignoring unknown document encoding: #1.}% + % + \fi % utfeight + \fi % latnine + \fi % latone + \fi % lattwo + \fi % ascii + % + \ifx\XeTeXrevision\thisisundefined + \else + \ifx \declaredencoding \utfeight + \else + \ifx \declaredencoding \ascii + \else + \message{Warning: XeTeX with non-UTF-8 encodings cannot handle % + non-ASCII characters in auxiliary files.}% + \fi + \fi + \fi +} + +% emacs-page +% A message to be logged when using a character that isn't available +% the default font encoding (OT1). +% +\def\missingcharmsg#1{\message{Character missing, sorry: #1.}} + +% Take account of \c (plain) vs. \, (Texinfo) difference. +\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} + +% First, make active non-ASCII characters in order for them to be +% correctly categorized when TeX reads the replacement text of +% macros containing the character definitions. +\setnonasciicharscatcode\active +% + +\def\gdefchar#1#2{% +\gdef#1{% + \ifpassthroughchars + \string#1% + \else + #2% + \fi +}} + +% Latin1 (ISO-8859-1) character definitions. +\def\latonechardefs{% + \gdefchar^^a0{\tie} + \gdefchar^^a1{\exclamdown} + \gdefchar^^a2{{\tcfont \char162}} % cent + \gdefchar^^a3{\pounds{}} + \gdefchar^^a4{{\tcfont \char164}} % currency + \gdefchar^^a5{{\tcfont \char165}} % yen + \gdefchar^^a6{{\tcfont \char166}} % broken bar + \gdefchar^^a7{\S} + \gdefchar^^a8{\"{}} + \gdefchar^^a9{\copyright{}} + \gdefchar^^aa{\ordf} + \gdefchar^^ab{\guillemetleft{}} + \gdefchar^^ac{\ensuremath\lnot} + \gdefchar^^ad{\-} + \gdefchar^^ae{\registeredsymbol{}} + \gdefchar^^af{\={}} + % + \gdefchar^^b0{\textdegree} + \gdefchar^^b1{$\pm$} + \gdefchar^^b2{$^2$} + \gdefchar^^b3{$^3$} + \gdefchar^^b4{\'{}} + \gdefchar^^b5{$\mu$} + \gdefchar^^b6{\P} + \gdefchar^^b7{\ensuremath\cdot} + \gdefchar^^b8{\cedilla\ } + \gdefchar^^b9{$^1$} + \gdefchar^^ba{\ordm} + \gdefchar^^bb{\guillemetright{}} + \gdefchar^^bc{$1\over4$} + \gdefchar^^bd{$1\over2$} + \gdefchar^^be{$3\over4$} + \gdefchar^^bf{\questiondown} + % + \gdefchar^^c0{\`A} + \gdefchar^^c1{\'A} + \gdefchar^^c2{\^A} + \gdefchar^^c3{\~A} + \gdefchar^^c4{\"A} + \gdefchar^^c5{\ringaccent A} + \gdefchar^^c6{\AE} + \gdefchar^^c7{\cedilla C} + \gdefchar^^c8{\`E} + \gdefchar^^c9{\'E} + \gdefchar^^ca{\^E} + \gdefchar^^cb{\"E} + \gdefchar^^cc{\`I} + \gdefchar^^cd{\'I} + \gdefchar^^ce{\^I} + \gdefchar^^cf{\"I} + % + \gdefchar^^d0{\DH} + \gdefchar^^d1{\~N} + \gdefchar^^d2{\`O} + \gdefchar^^d3{\'O} + \gdefchar^^d4{\^O} + \gdefchar^^d5{\~O} + \gdefchar^^d6{\"O} + \gdefchar^^d7{$\times$} + \gdefchar^^d8{\O} + \gdefchar^^d9{\`U} + \gdefchar^^da{\'U} + \gdefchar^^db{\^U} + \gdefchar^^dc{\"U} + \gdefchar^^dd{\'Y} + \gdefchar^^de{\TH} + \gdefchar^^df{\ss} + % + \gdefchar^^e0{\`a} + \gdefchar^^e1{\'a} + \gdefchar^^e2{\^a} + \gdefchar^^e3{\~a} + \gdefchar^^e4{\"a} + \gdefchar^^e5{\ringaccent a} + \gdefchar^^e6{\ae} + \gdefchar^^e7{\cedilla c} + \gdefchar^^e8{\`e} + \gdefchar^^e9{\'e} + \gdefchar^^ea{\^e} + \gdefchar^^eb{\"e} + \gdefchar^^ec{\`{\dotless i}} + \gdefchar^^ed{\'{\dotless i}} + \gdefchar^^ee{\^{\dotless i}} + \gdefchar^^ef{\"{\dotless i}} + % + \gdefchar^^f0{\dh} + \gdefchar^^f1{\~n} + \gdefchar^^f2{\`o} + \gdefchar^^f3{\'o} + \gdefchar^^f4{\^o} + \gdefchar^^f5{\~o} + \gdefchar^^f6{\"o} + \gdefchar^^f7{$\div$} + \gdefchar^^f8{\o} + \gdefchar^^f9{\`u} + \gdefchar^^fa{\'u} + \gdefchar^^fb{\^u} + \gdefchar^^fc{\"u} + \gdefchar^^fd{\'y} + \gdefchar^^fe{\th} + \gdefchar^^ff{\"y} +} + +% Latin9 (ISO-8859-15) encoding character definitions. +\def\latninechardefs{% + % Encoding is almost identical to Latin1. + \latonechardefs + % + \gdefchar^^a4{\euro{}} + \gdefchar^^a6{\v S} + \gdefchar^^a8{\v s} + \gdefchar^^b4{\v Z} + \gdefchar^^b8{\v z} + \gdefchar^^bc{\OE} + \gdefchar^^bd{\oe} + \gdefchar^^be{\"Y} +} + +% Latin2 (ISO-8859-2) character definitions. +\def\lattwochardefs{% + \gdefchar^^a0{\tie} + \gdefchar^^a1{\ogonek{A}} + \gdefchar^^a2{\u{}} + \gdefchar^^a3{\L} + \gdefchar^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdefchar^^a5{\v L} + \gdefchar^^a6{\'S} + \gdefchar^^a7{\S} + \gdefchar^^a8{\"{}} + \gdefchar^^a9{\v S} + \gdefchar^^aa{\cedilla S} + \gdefchar^^ab{\v T} + \gdefchar^^ac{\'Z} + \gdefchar^^ad{\-} + \gdefchar^^ae{\v Z} + \gdefchar^^af{\dotaccent Z} + % + \gdefchar^^b0{\textdegree{}} + \gdefchar^^b1{\ogonek{a}} + \gdefchar^^b2{\ogonek{ }} + \gdefchar^^b3{\l} + \gdefchar^^b4{\'{}} + \gdefchar^^b5{\v l} + \gdefchar^^b6{\'s} + \gdefchar^^b7{\v{}} + \gdefchar^^b8{\cedilla\ } + \gdefchar^^b9{\v s} + \gdefchar^^ba{\cedilla s} + \gdefchar^^bb{\v t} + \gdefchar^^bc{\'z} + \gdefchar^^bd{\H{}} + \gdefchar^^be{\v z} + \gdefchar^^bf{\dotaccent z} + % + \gdefchar^^c0{\'R} + \gdefchar^^c1{\'A} + \gdefchar^^c2{\^A} + \gdefchar^^c3{\u A} + \gdefchar^^c4{\"A} + \gdefchar^^c5{\'L} + \gdefchar^^c6{\'C} + \gdefchar^^c7{\cedilla C} + \gdefchar^^c8{\v C} + \gdefchar^^c9{\'E} + \gdefchar^^ca{\ogonek{E}} + \gdefchar^^cb{\"E} + \gdefchar^^cc{\v E} + \gdefchar^^cd{\'I} + \gdefchar^^ce{\^I} + \gdefchar^^cf{\v D} + % + \gdefchar^^d0{\DH} + \gdefchar^^d1{\'N} + \gdefchar^^d2{\v N} + \gdefchar^^d3{\'O} + \gdefchar^^d4{\^O} + \gdefchar^^d5{\H O} + \gdefchar^^d6{\"O} + \gdefchar^^d7{$\times$} + \gdefchar^^d8{\v R} + \gdefchar^^d9{\ringaccent U} + \gdefchar^^da{\'U} + \gdefchar^^db{\H U} + \gdefchar^^dc{\"U} + \gdefchar^^dd{\'Y} + \gdefchar^^de{\cedilla T} + \gdefchar^^df{\ss} + % + \gdefchar^^e0{\'r} + \gdefchar^^e1{\'a} + \gdefchar^^e2{\^a} + \gdefchar^^e3{\u a} + \gdefchar^^e4{\"a} + \gdefchar^^e5{\'l} + \gdefchar^^e6{\'c} + \gdefchar^^e7{\cedilla c} + \gdefchar^^e8{\v c} + \gdefchar^^e9{\'e} + \gdefchar^^ea{\ogonek{e}} + \gdefchar^^eb{\"e} + \gdefchar^^ec{\v e} + \gdefchar^^ed{\'{\dotless{i}}} + \gdefchar^^ee{\^{\dotless{i}}} + \gdefchar^^ef{\v d} + % + \gdefchar^^f0{\dh} + \gdefchar^^f1{\'n} + \gdefchar^^f2{\v n} + \gdefchar^^f3{\'o} + \gdefchar^^f4{\^o} + \gdefchar^^f5{\H o} + \gdefchar^^f6{\"o} + \gdefchar^^f7{$\div$} + \gdefchar^^f8{\v r} + \gdefchar^^f9{\ringaccent u} + \gdefchar^^fa{\'u} + \gdefchar^^fb{\H u} + \gdefchar^^fc{\"u} + \gdefchar^^fd{\'y} + \gdefchar^^fe{\cedilla t} + \gdefchar^^ff{\dotaccent{}} +} + +% UTF-8 character definitions. +% +% This code to support UTF-8 is based on LaTeX's utf8.def, with some +% changes for Texinfo conventions. It is included here under the GPL by +% permission from Frank Mittelbach and the LaTeX team. +% +\newcount\countUTFx +\newcount\countUTFy +\newcount\countUTFz + +\gdef\UTFviiiTwoOctets#1#2{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\endcsname} +% +\gdef\UTFviiiThreeOctets#1#2#3{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} +% +\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} + +\gdef\UTFviiiDefined#1{% + \ifx #1\relax + \message{\linenumber Unicode char \string #1 not defined for Texinfo}% + \else + \expandafter #1% + \fi +} + +% Give non-ASCII bytes the active definitions for processing UTF-8 sequences +\begingroup + \catcode`\~13 + \catcode`\$12 + \catcode`\"12 + + % Loop from \countUTFx to \countUTFy, performing \UTFviiiTmp + % substituting ~ and $ with a character token of that value. + \def\UTFviiiLoop{% + \global\catcode\countUTFx\active + \uccode`\~\countUTFx + \uccode`\$\countUTFx + \uppercase\expandafter{\UTFviiiTmp}% + \advance\countUTFx by 1 + \ifnum\countUTFx < \countUTFy + \expandafter\UTFviiiLoop + \fi} + + % For bytes other than the first in a UTF-8 sequence. Not expected to + % be expanded except when writing to auxiliary files. + \countUTFx = "80 + \countUTFy = "C2 + \def\UTFviiiTmp{% + \gdef~{% + \ifpassthroughchars $\fi}}% + \UTFviiiLoop + + \countUTFx = "C2 + \countUTFy = "E0 + \def\UTFviiiTmp{% + \gdef~{% + \ifpassthroughchars $% + \else\expandafter\UTFviiiTwoOctets\expandafter$\fi}}% + \UTFviiiLoop + + \countUTFx = "E0 + \countUTFy = "F0 + \def\UTFviiiTmp{% + \gdef~{% + \ifpassthroughchars $% + \else\expandafter\UTFviiiThreeOctets\expandafter$\fi}}% + \UTFviiiLoop + + \countUTFx = "F0 + \countUTFy = "F4 + \def\UTFviiiTmp{% + \gdef~{% + \ifpassthroughchars $% + \else\expandafter\UTFviiiFourOctets\expandafter$\fi + }}% + \UTFviiiLoop +\endgroup + +\def\globallet{\global\let} % save some \expandafter's below + +% @U{xxxx} to produce U+xxxx, if we support it. +\def\U#1{% + \expandafter\ifx\csname uni:#1\endcsname \relax + \iftxinativeunicodecapable + % All Unicode characters can be used if native Unicode handling is + % active. However, if the font does not have the glyph, + % letters are missing. + \begingroup + \uccode`\.="#1\relax + \uppercase{.} + \endgroup + \else + \errhelp = \EMsimple + \errmessage{Unicode character U+#1 not supported, sorry}% + \fi + \else + \csname uni:#1\endcsname + \fi +} + +% These macros are used here to construct the name of a control +% sequence to be defined. +\def\UTFviiiTwoOctetsName#1#2{% + \csname u8:#1\string #2\endcsname}% +\def\UTFviiiThreeOctetsName#1#2#3{% + \csname u8:#1\string #2\string #3\endcsname}% +\def\UTFviiiFourOctetsName#1#2#3#4{% + \csname u8:#1\string #2\string #3\string #4\endcsname}% + +% For UTF-8 byte sequences (TeX, e-TeX and pdfTeX), +% provide a definition macro to replace a Unicode character; +% this gets used by the @U command +% +\begingroup + \catcode`\"=12 + \catcode`\<=12 + \catcode`\.=12 + \catcode`\,=12 + \catcode`\;=12 + \catcode`\!=12 + \catcode`\~=13 + \gdef\DeclareUnicodeCharacterUTFviii#1#2{% + \countUTFz = "#1\relax + \begingroup + \parseXMLCharref + + % Give \u8:... its definition. The sequence of seven \expandafter's + % expands after the \gdef three times, e.g. + % + % 1. \UTFviiTwoOctetsName B1 B2 + % 2. \csname u8:B1 \string B2 \endcsname + % 3. \u8: B1 B2 (a single control sequence token) + % + \expandafter\expandafter + \expandafter\expandafter + \expandafter\expandafter + \expandafter\gdef \UTFviiiTmp{#2}% + % + \expandafter\ifx\csname uni:#1\endcsname \relax \else + \message{Internal error, already defined: #1}% + \fi + % + % define an additional control sequence for this code point. + \expandafter\globallet\csname uni:#1\endcsname \UTFviiiTmp + \endgroup} + % + % Given the value in \countUTFz as a Unicode code point, set \UTFviiiTmp + % to the corresponding UTF-8 sequence. + \gdef\parseXMLCharref{% + \ifnum\countUTFz < "A0\relax + \errhelp = \EMsimple + \errmessage{Cannot define Unicode char value < 00A0}% + \else\ifnum\countUTFz < "800\relax + \parseUTFviiiA,% + \parseUTFviiiB C\UTFviiiTwoOctetsName.,% + \else\ifnum\countUTFz < "10000\relax + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiB E\UTFviiiThreeOctetsName.{,;}% + \else + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiA!% + \parseUTFviiiB F\UTFviiiFourOctetsName.{!,;}% + \fi\fi\fi + } + + % Extract a byte from the end of the UTF-8 representation of \countUTFx. + % It must be a non-initial byte in the sequence. + % Change \uccode of #1 for it to be used in \parseUTFviiiB as one + % of the bytes. + \gdef\parseUTFviiiA#1{% + \countUTFx = \countUTFz + \divide\countUTFz by 64 + \countUTFy = \countUTFz % Save to be the future value of \countUTFz. + \multiply\countUTFz by 64 + + % \countUTFz is now \countUTFx with the last 5 bits cleared. Subtract + % in order to get the last five bits. + \advance\countUTFx by -\countUTFz + + % Convert this to the byte in the UTF-8 sequence. + \advance\countUTFx by 128 + \uccode `#1\countUTFx + \countUTFz = \countUTFy} + + % Used to put a UTF-8 byte sequence into \UTFviiiTmp + % #1 is the increment for \countUTFz to yield a the first byte of the UTF-8 + % sequence. + % #2 is one of the \UTFviii*OctetsName macros. + % #3 is always a full stop (.) + % #4 is a template for the other bytes in the sequence. The values for these + % bytes is substituted in here with \uppercase using the \uccode's. + \gdef\parseUTFviiiB#1#2#3#4{% + \advance\countUTFz by "#10\relax + \uccode `#3\countUTFz + \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} +\endgroup + +% For native Unicode handling (XeTeX and LuaTeX), +% provide a definition macro that sets a catcode to `other' non-globally +% +\def\DeclareUnicodeCharacterNativeOther#1#2{% + \catcode"#1=\other +} + +% https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_M +% U+0000..U+007F = https://en.wikipedia.org/wiki/Basic_Latin_(Unicode_block) +% U+0080..U+00FF = https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block) +% U+0100..U+017F = https://en.wikipedia.org/wiki/Latin_Extended-A +% U+0180..U+024F = https://en.wikipedia.org/wiki/Latin_Extended-B +% +% Many of our renditions are less than wonderful, and all the missing +% characters are available somewhere. Loading the necessary fonts +% awaits user request. We can't truly support Unicode without +% reimplementing everything that's been done in LaTeX for many years, +% plus probably using luatex or xetex, and who knows what else. +% We won't be doing that here in this simple file. But we can try to at +% least make most of the characters not bomb out. +% +\def\unicodechardefs{% + \DeclareUnicodeCharacter{00A0}{\tie}% + \DeclareUnicodeCharacter{00A1}{\exclamdown}% + \DeclareUnicodeCharacter{00A2}{{\tcfont \char162}}% 0242=cent + \DeclareUnicodeCharacter{00A3}{\pounds{}}% + \DeclareUnicodeCharacter{00A4}{{\tcfont \char164}}% 0244=currency + \DeclareUnicodeCharacter{00A5}{{\tcfont \char165}}% 0245=yen + \DeclareUnicodeCharacter{00A6}{{\tcfont \char166}}% 0246=brokenbar + \DeclareUnicodeCharacter{00A7}{\S}% + \DeclareUnicodeCharacter{00A8}{\"{ }}% + \DeclareUnicodeCharacter{00A9}{\copyright{}}% + \DeclareUnicodeCharacter{00AA}{\ordf}% + \DeclareUnicodeCharacter{00AB}{\guillemetleft{}}% + \DeclareUnicodeCharacter{00AC}{\ensuremath\lnot}% + \DeclareUnicodeCharacter{00AD}{\-}% + \DeclareUnicodeCharacter{00AE}{\registeredsymbol{}}% + \DeclareUnicodeCharacter{00AF}{\={ }}% + % + \DeclareUnicodeCharacter{00B0}{\ringaccent{ }}% + \DeclareUnicodeCharacter{00B1}{\ensuremath\pm}% + \DeclareUnicodeCharacter{00B2}{$^2$}% + \DeclareUnicodeCharacter{00B3}{$^3$}% + \DeclareUnicodeCharacter{00B4}{\'{ }}% + \DeclareUnicodeCharacter{00B5}{$\mu$}% + \DeclareUnicodeCharacter{00B6}{\P}% + \DeclareUnicodeCharacter{00B7}{\ensuremath\cdot}% + \DeclareUnicodeCharacter{00B8}{\cedilla{ }}% + \DeclareUnicodeCharacter{00B9}{$^1$}% + \DeclareUnicodeCharacter{00BA}{\ordm}% + \DeclareUnicodeCharacter{00BB}{\guillemetright{}}% + \DeclareUnicodeCharacter{00BC}{$1\over4$}% + \DeclareUnicodeCharacter{00BD}{$1\over2$}% + \DeclareUnicodeCharacter{00BE}{$3\over4$}% + \DeclareUnicodeCharacter{00BF}{\questiondown}% + % + \DeclareUnicodeCharacter{00C0}{\`A}% + \DeclareUnicodeCharacter{00C1}{\'A}% + \DeclareUnicodeCharacter{00C2}{\^A}% + \DeclareUnicodeCharacter{00C3}{\~A}% + \DeclareUnicodeCharacter{00C4}{\"A}% + \DeclareUnicodeCharacter{00C5}{\AA}% + \DeclareUnicodeCharacter{00C6}{\AE}% + \DeclareUnicodeCharacter{00C7}{\cedilla{C}}% + \DeclareUnicodeCharacter{00C8}{\`E}% + \DeclareUnicodeCharacter{00C9}{\'E}% + \DeclareUnicodeCharacter{00CA}{\^E}% + \DeclareUnicodeCharacter{00CB}{\"E}% + \DeclareUnicodeCharacter{00CC}{\`I}% + \DeclareUnicodeCharacter{00CD}{\'I}% + \DeclareUnicodeCharacter{00CE}{\^I}% + \DeclareUnicodeCharacter{00CF}{\"I}% + % + \DeclareUnicodeCharacter{00D0}{\DH}% + \DeclareUnicodeCharacter{00D1}{\~N}% + \DeclareUnicodeCharacter{00D2}{\`O}% + \DeclareUnicodeCharacter{00D3}{\'O}% + \DeclareUnicodeCharacter{00D4}{\^O}% + \DeclareUnicodeCharacter{00D5}{\~O}% + \DeclareUnicodeCharacter{00D6}{\"O}% + \DeclareUnicodeCharacter{00D7}{\ensuremath\times}% + \DeclareUnicodeCharacter{00D8}{\O}% + \DeclareUnicodeCharacter{00D9}{\`U}% + \DeclareUnicodeCharacter{00DA}{\'U}% + \DeclareUnicodeCharacter{00DB}{\^U}% + \DeclareUnicodeCharacter{00DC}{\"U}% + \DeclareUnicodeCharacter{00DD}{\'Y}% + \DeclareUnicodeCharacter{00DE}{\TH}% + \DeclareUnicodeCharacter{00DF}{\ss}% + % + \DeclareUnicodeCharacter{00E0}{\`a}% + \DeclareUnicodeCharacter{00E1}{\'a}% + \DeclareUnicodeCharacter{00E2}{\^a}% + \DeclareUnicodeCharacter{00E3}{\~a}% + \DeclareUnicodeCharacter{00E4}{\"a}% + \DeclareUnicodeCharacter{00E5}{\aa}% + \DeclareUnicodeCharacter{00E6}{\ae}% + \DeclareUnicodeCharacter{00E7}{\cedilla{c}}% + \DeclareUnicodeCharacter{00E8}{\`e}% + \DeclareUnicodeCharacter{00E9}{\'e}% + \DeclareUnicodeCharacter{00EA}{\^e}% + \DeclareUnicodeCharacter{00EB}{\"e}% + \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}}% + \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}}% + \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}}% + \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}}% + % + \DeclareUnicodeCharacter{00F0}{\dh}% + \DeclareUnicodeCharacter{00F1}{\~n}% + \DeclareUnicodeCharacter{00F2}{\`o}% + \DeclareUnicodeCharacter{00F3}{\'o}% + \DeclareUnicodeCharacter{00F4}{\^o}% + \DeclareUnicodeCharacter{00F5}{\~o}% + \DeclareUnicodeCharacter{00F6}{\"o}% + \DeclareUnicodeCharacter{00F7}{\ensuremath\div}% + \DeclareUnicodeCharacter{00F8}{\o}% + \DeclareUnicodeCharacter{00F9}{\`u}% + \DeclareUnicodeCharacter{00FA}{\'u}% + \DeclareUnicodeCharacter{00FB}{\^u}% + \DeclareUnicodeCharacter{00FC}{\"u}% + \DeclareUnicodeCharacter{00FD}{\'y}% + \DeclareUnicodeCharacter{00FE}{\th}% + \DeclareUnicodeCharacter{00FF}{\"y}% + % + \DeclareUnicodeCharacter{0100}{\=A}% + \DeclareUnicodeCharacter{0101}{\=a}% + \DeclareUnicodeCharacter{0102}{\u{A}}% + \DeclareUnicodeCharacter{0103}{\u{a}}% + \DeclareUnicodeCharacter{0104}{\ogonek{A}}% + \DeclareUnicodeCharacter{0105}{\ogonek{a}}% + \DeclareUnicodeCharacter{0106}{\'C}% + \DeclareUnicodeCharacter{0107}{\'c}% + \DeclareUnicodeCharacter{0108}{\^C}% + \DeclareUnicodeCharacter{0109}{\^c}% + \DeclareUnicodeCharacter{010A}{\dotaccent{C}}% + \DeclareUnicodeCharacter{010B}{\dotaccent{c}}% + \DeclareUnicodeCharacter{010C}{\v{C}}% + \DeclareUnicodeCharacter{010D}{\v{c}}% + \DeclareUnicodeCharacter{010E}{\v{D}}% + \DeclareUnicodeCharacter{010F}{d'}% + % + \DeclareUnicodeCharacter{0110}{\DH}% + \DeclareUnicodeCharacter{0111}{\dh}% + \DeclareUnicodeCharacter{0112}{\=E}% + \DeclareUnicodeCharacter{0113}{\=e}% + \DeclareUnicodeCharacter{0114}{\u{E}}% + \DeclareUnicodeCharacter{0115}{\u{e}}% + \DeclareUnicodeCharacter{0116}{\dotaccent{E}}% + \DeclareUnicodeCharacter{0117}{\dotaccent{e}}% + \DeclareUnicodeCharacter{0118}{\ogonek{E}}% + \DeclareUnicodeCharacter{0119}{\ogonek{e}}% + \DeclareUnicodeCharacter{011A}{\v{E}}% + \DeclareUnicodeCharacter{011B}{\v{e}}% + \DeclareUnicodeCharacter{011C}{\^G}% + \DeclareUnicodeCharacter{011D}{\^g}% + \DeclareUnicodeCharacter{011E}{\u{G}}% + \DeclareUnicodeCharacter{011F}{\u{g}}% + % + \DeclareUnicodeCharacter{0120}{\dotaccent{G}}% + \DeclareUnicodeCharacter{0121}{\dotaccent{g}}% + \DeclareUnicodeCharacter{0122}{\cedilla{G}}% + \DeclareUnicodeCharacter{0123}{\cedilla{g}}% + \DeclareUnicodeCharacter{0124}{\^H}% + \DeclareUnicodeCharacter{0125}{\^h}% + \DeclareUnicodeCharacter{0126}{\missingcharmsg{H WITH STROKE}}% + \DeclareUnicodeCharacter{0127}{\missingcharmsg{h WITH STROKE}}% + \DeclareUnicodeCharacter{0128}{\~I}% + \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}}% + \DeclareUnicodeCharacter{012A}{\=I}% + \DeclareUnicodeCharacter{012B}{\={\dotless{i}}}% + \DeclareUnicodeCharacter{012C}{\u{I}}% + \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}}% + \DeclareUnicodeCharacter{012E}{\ogonek{I}}% + \DeclareUnicodeCharacter{012F}{\ogonek{i}}% + % + \DeclareUnicodeCharacter{0130}{\dotaccent{I}}% + \DeclareUnicodeCharacter{0131}{\dotless{i}}% + \DeclareUnicodeCharacter{0132}{IJ}% + \DeclareUnicodeCharacter{0133}{ij}% + \DeclareUnicodeCharacter{0134}{\^J}% + \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}}% + \DeclareUnicodeCharacter{0136}{\cedilla{K}}% + \DeclareUnicodeCharacter{0137}{\cedilla{k}}% + \DeclareUnicodeCharacter{0138}{\ensuremath\kappa}% + \DeclareUnicodeCharacter{0139}{\'L}% + \DeclareUnicodeCharacter{013A}{\'l}% + \DeclareUnicodeCharacter{013B}{\cedilla{L}}% + \DeclareUnicodeCharacter{013C}{\cedilla{l}}% + \DeclareUnicodeCharacter{013D}{L'}% should kern + \DeclareUnicodeCharacter{013E}{l'}% should kern + \DeclareUnicodeCharacter{013F}{L\U{00B7}}% + % + \DeclareUnicodeCharacter{0140}{l\U{00B7}}% + \DeclareUnicodeCharacter{0141}{\L}% + \DeclareUnicodeCharacter{0142}{\l}% + \DeclareUnicodeCharacter{0143}{\'N}% + \DeclareUnicodeCharacter{0144}{\'n}% + \DeclareUnicodeCharacter{0145}{\cedilla{N}}% + \DeclareUnicodeCharacter{0146}{\cedilla{n}}% + \DeclareUnicodeCharacter{0147}{\v{N}}% + \DeclareUnicodeCharacter{0148}{\v{n}}% + \DeclareUnicodeCharacter{0149}{'n}% + \DeclareUnicodeCharacter{014A}{\missingcharmsg{ENG}}% + \DeclareUnicodeCharacter{014B}{\missingcharmsg{eng}}% + \DeclareUnicodeCharacter{014C}{\=O}% + \DeclareUnicodeCharacter{014D}{\=o}% + \DeclareUnicodeCharacter{014E}{\u{O}}% + \DeclareUnicodeCharacter{014F}{\u{o}}% + % + \DeclareUnicodeCharacter{0150}{\H{O}}% + \DeclareUnicodeCharacter{0151}{\H{o}}% + \DeclareUnicodeCharacter{0152}{\OE}% + \DeclareUnicodeCharacter{0153}{\oe}% + \DeclareUnicodeCharacter{0154}{\'R}% + \DeclareUnicodeCharacter{0155}{\'r}% + \DeclareUnicodeCharacter{0156}{\cedilla{R}}% + \DeclareUnicodeCharacter{0157}{\cedilla{r}}% + \DeclareUnicodeCharacter{0158}{\v{R}}% + \DeclareUnicodeCharacter{0159}{\v{r}}% + \DeclareUnicodeCharacter{015A}{\'S}% + \DeclareUnicodeCharacter{015B}{\'s}% + \DeclareUnicodeCharacter{015C}{\^S}% + \DeclareUnicodeCharacter{015D}{\^s}% + \DeclareUnicodeCharacter{015E}{\cedilla{S}}% + \DeclareUnicodeCharacter{015F}{\cedilla{s}}% + % + \DeclareUnicodeCharacter{0160}{\v{S}}% + \DeclareUnicodeCharacter{0161}{\v{s}}% + \DeclareUnicodeCharacter{0162}{\cedilla{T}}% + \DeclareUnicodeCharacter{0163}{\cedilla{t}}% + \DeclareUnicodeCharacter{0164}{\v{T}}% + \DeclareUnicodeCharacter{0165}{\v{t}}% + \DeclareUnicodeCharacter{0166}{\missingcharmsg{H WITH STROKE}}% + \DeclareUnicodeCharacter{0167}{\missingcharmsg{h WITH STROKE}}% + \DeclareUnicodeCharacter{0168}{\~U}% + \DeclareUnicodeCharacter{0169}{\~u}% + \DeclareUnicodeCharacter{016A}{\=U}% + \DeclareUnicodeCharacter{016B}{\=u}% + \DeclareUnicodeCharacter{016C}{\u{U}}% + \DeclareUnicodeCharacter{016D}{\u{u}}% + \DeclareUnicodeCharacter{016E}{\ringaccent{U}}% + \DeclareUnicodeCharacter{016F}{\ringaccent{u}}% + % + \DeclareUnicodeCharacter{0170}{\H{U}}% + \DeclareUnicodeCharacter{0171}{\H{u}}% + \DeclareUnicodeCharacter{0172}{\ogonek{U}}% + \DeclareUnicodeCharacter{0173}{\ogonek{u}}% + \DeclareUnicodeCharacter{0174}{\^W}% + \DeclareUnicodeCharacter{0175}{\^w}% + \DeclareUnicodeCharacter{0176}{\^Y}% + \DeclareUnicodeCharacter{0177}{\^y}% + \DeclareUnicodeCharacter{0178}{\"Y}% + \DeclareUnicodeCharacter{0179}{\'Z}% + \DeclareUnicodeCharacter{017A}{\'z}% + \DeclareUnicodeCharacter{017B}{\dotaccent{Z}}% + \DeclareUnicodeCharacter{017C}{\dotaccent{z}}% + \DeclareUnicodeCharacter{017D}{\v{Z}}% + \DeclareUnicodeCharacter{017E}{\v{z}}% + \DeclareUnicodeCharacter{017F}{\missingcharmsg{LONG S}}% + % + \DeclareUnicodeCharacter{01C4}{D\v{Z}}% + \DeclareUnicodeCharacter{01C5}{D\v{z}}% + \DeclareUnicodeCharacter{01C6}{d\v{z}}% + \DeclareUnicodeCharacter{01C7}{LJ}% + \DeclareUnicodeCharacter{01C8}{Lj}% + \DeclareUnicodeCharacter{01C9}{lj}% + \DeclareUnicodeCharacter{01CA}{NJ}% + \DeclareUnicodeCharacter{01CB}{Nj}% + \DeclareUnicodeCharacter{01CC}{nj}% + \DeclareUnicodeCharacter{01CD}{\v{A}}% + \DeclareUnicodeCharacter{01CE}{\v{a}}% + \DeclareUnicodeCharacter{01CF}{\v{I}}% + % + \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}}% + \DeclareUnicodeCharacter{01D1}{\v{O}}% + \DeclareUnicodeCharacter{01D2}{\v{o}}% + \DeclareUnicodeCharacter{01D3}{\v{U}}% + \DeclareUnicodeCharacter{01D4}{\v{u}}% + % + \DeclareUnicodeCharacter{01E2}{\={\AE}}% + \DeclareUnicodeCharacter{01E3}{\={\ae}}% + \DeclareUnicodeCharacter{01E6}{\v{G}}% + \DeclareUnicodeCharacter{01E7}{\v{g}}% + \DeclareUnicodeCharacter{01E8}{\v{K}}% + \DeclareUnicodeCharacter{01E9}{\v{k}}% + % + \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}}% + \DeclareUnicodeCharacter{01F1}{DZ}% + \DeclareUnicodeCharacter{01F2}{Dz}% + \DeclareUnicodeCharacter{01F3}{dz}% + \DeclareUnicodeCharacter{01F4}{\'G}% + \DeclareUnicodeCharacter{01F5}{\'g}% + \DeclareUnicodeCharacter{01F8}{\`N}% + \DeclareUnicodeCharacter{01F9}{\`n}% + \DeclareUnicodeCharacter{01FC}{\'{\AE}}% + \DeclareUnicodeCharacter{01FD}{\'{\ae}}% + \DeclareUnicodeCharacter{01FE}{\'{\O}}% + \DeclareUnicodeCharacter{01FF}{\'{\o}}% + % + \DeclareUnicodeCharacter{021E}{\v{H}}% + \DeclareUnicodeCharacter{021F}{\v{h}}% + % + \DeclareUnicodeCharacter{0226}{\dotaccent{A}}% + \DeclareUnicodeCharacter{0227}{\dotaccent{a}}% + \DeclareUnicodeCharacter{0228}{\cedilla{E}}% + \DeclareUnicodeCharacter{0229}{\cedilla{e}}% + \DeclareUnicodeCharacter{022E}{\dotaccent{O}}% + \DeclareUnicodeCharacter{022F}{\dotaccent{o}}% + % + \DeclareUnicodeCharacter{0232}{\=Y}% + \DeclareUnicodeCharacter{0233}{\=y}% + \DeclareUnicodeCharacter{0237}{\dotless{j}}% + % + \DeclareUnicodeCharacter{02BC}{'}% + % + \DeclareUnicodeCharacter{02DB}{\ogonek{ }}% + % + % Greek letters upper case + \DeclareUnicodeCharacter{0391}{{\it A}}% + \DeclareUnicodeCharacter{0392}{{\it B}}% + \DeclareUnicodeCharacter{0393}{\ensuremath{\mit\Gamma}}% + \DeclareUnicodeCharacter{0394}{\ensuremath{\mit\Delta}}% + \DeclareUnicodeCharacter{0395}{{\it E}}% + \DeclareUnicodeCharacter{0396}{{\it Z}}% + \DeclareUnicodeCharacter{0397}{{\it H}}% + \DeclareUnicodeCharacter{0398}{\ensuremath{\mit\Theta}}% + \DeclareUnicodeCharacter{0399}{{\it I}}% + \DeclareUnicodeCharacter{039A}{{\it K}}% + \DeclareUnicodeCharacter{039B}{\ensuremath{\mit\Lambda}}% + \DeclareUnicodeCharacter{039C}{{\it M}}% + \DeclareUnicodeCharacter{039D}{{\it N}}% + \DeclareUnicodeCharacter{039E}{\ensuremath{\mit\Xi}}% + \DeclareUnicodeCharacter{039F}{{\it O}}% + \DeclareUnicodeCharacter{03A0}{\ensuremath{\mit\Pi}}% + \DeclareUnicodeCharacter{03A1}{{\it P}}% + %\DeclareUnicodeCharacter{03A2}{} % none - corresponds to final sigma + \DeclareUnicodeCharacter{03A3}{\ensuremath{\mit\Sigma}}% + \DeclareUnicodeCharacter{03A4}{{\it T}}% + \DeclareUnicodeCharacter{03A5}{\ensuremath{\mit\Upsilon}}% + \DeclareUnicodeCharacter{03A6}{\ensuremath{\mit\Phi}}% + \DeclareUnicodeCharacter{03A7}{{\it X}}% + \DeclareUnicodeCharacter{03A8}{\ensuremath{\mit\Psi}}% + \DeclareUnicodeCharacter{03A9}{\ensuremath{\mit\Omega}}% + % + % Vowels with accents + \DeclareUnicodeCharacter{0390}{\ensuremath{\ddot{\acute\iota}}}% + \DeclareUnicodeCharacter{03AC}{\ensuremath{\acute\alpha}}% + \DeclareUnicodeCharacter{03AD}{\ensuremath{\acute\epsilon}}% + \DeclareUnicodeCharacter{03AE}{\ensuremath{\acute\eta}}% + \DeclareUnicodeCharacter{03AF}{\ensuremath{\acute\iota}}% + \DeclareUnicodeCharacter{03B0}{\ensuremath{\acute{\ddot\upsilon}}}% + % + % Standalone accent + \DeclareUnicodeCharacter{0384}{\ensuremath{\acute{\ }}}% + % + % Greek letters lower case + \DeclareUnicodeCharacter{03B1}{\ensuremath\alpha}% + \DeclareUnicodeCharacter{03B2}{\ensuremath\beta}% + \DeclareUnicodeCharacter{03B3}{\ensuremath\gamma}% + \DeclareUnicodeCharacter{03B4}{\ensuremath\delta}% + \DeclareUnicodeCharacter{03B5}{\ensuremath\epsilon}% + \DeclareUnicodeCharacter{03B6}{\ensuremath\zeta}% + \DeclareUnicodeCharacter{03B7}{\ensuremath\eta}% + \DeclareUnicodeCharacter{03B8}{\ensuremath\theta}% + \DeclareUnicodeCharacter{03B9}{\ensuremath\iota}% + \DeclareUnicodeCharacter{03BA}{\ensuremath\kappa}% + \DeclareUnicodeCharacter{03BB}{\ensuremath\lambda}% + \DeclareUnicodeCharacter{03BC}{\ensuremath\mu}% + \DeclareUnicodeCharacter{03BD}{\ensuremath\nu}% + \DeclareUnicodeCharacter{03BE}{\ensuremath\xi}% + \DeclareUnicodeCharacter{03BF}{{\it o}}% omicron + \DeclareUnicodeCharacter{03C0}{\ensuremath\pi}% + \DeclareUnicodeCharacter{03C1}{\ensuremath\rho}% + \DeclareUnicodeCharacter{03C2}{\ensuremath\varsigma}% + \DeclareUnicodeCharacter{03C3}{\ensuremath\sigma}% + \DeclareUnicodeCharacter{03C4}{\ensuremath\tau}% + \DeclareUnicodeCharacter{03C5}{\ensuremath\upsilon}% + \DeclareUnicodeCharacter{03C6}{\ensuremath\phi}% + \DeclareUnicodeCharacter{03C7}{\ensuremath\chi}% + \DeclareUnicodeCharacter{03C8}{\ensuremath\psi}% + \DeclareUnicodeCharacter{03C9}{\ensuremath\omega}% + % + % More Greek vowels with accents + \DeclareUnicodeCharacter{03CA}{\ensuremath{\ddot\iota}}% + \DeclareUnicodeCharacter{03CB}{\ensuremath{\ddot\upsilon}}% + \DeclareUnicodeCharacter{03CC}{\ensuremath{\acute o}}% + \DeclareUnicodeCharacter{03CD}{\ensuremath{\acute\upsilon}}% + \DeclareUnicodeCharacter{03CE}{\ensuremath{\acute\omega}}% + % + % Variant Greek letters + \DeclareUnicodeCharacter{03D1}{\ensuremath\vartheta}% + \DeclareUnicodeCharacter{03D6}{\ensuremath\varpi}% + \DeclareUnicodeCharacter{03F1}{\ensuremath\varrho}% + % + \DeclareUnicodeCharacter{1E02}{\dotaccent{B}}% + \DeclareUnicodeCharacter{1E03}{\dotaccent{b}}% + \DeclareUnicodeCharacter{1E04}{\udotaccent{B}}% + \DeclareUnicodeCharacter{1E05}{\udotaccent{b}}% + \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}}% + \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}}% + \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}}% + \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}}% + \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}}% + \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}}% + \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}}% + \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}}% + % + \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}}% + \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}}% + % + \DeclareUnicodeCharacter{1E20}{\=G}% + \DeclareUnicodeCharacter{1E21}{\=g}% + \DeclareUnicodeCharacter{1E22}{\dotaccent{H}}% + \DeclareUnicodeCharacter{1E23}{\dotaccent{h}}% + \DeclareUnicodeCharacter{1E24}{\udotaccent{H}}% + \DeclareUnicodeCharacter{1E25}{\udotaccent{h}}% + \DeclareUnicodeCharacter{1E26}{\"H}% + \DeclareUnicodeCharacter{1E27}{\"h}% + % + \DeclareUnicodeCharacter{1E30}{\'K}% + \DeclareUnicodeCharacter{1E31}{\'k}% + \DeclareUnicodeCharacter{1E32}{\udotaccent{K}}% + \DeclareUnicodeCharacter{1E33}{\udotaccent{k}}% + \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}}% + \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}}% + \DeclareUnicodeCharacter{1E36}{\udotaccent{L}}% + \DeclareUnicodeCharacter{1E37}{\udotaccent{l}}% + \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}}% + \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}}% + \DeclareUnicodeCharacter{1E3E}{\'M}% + \DeclareUnicodeCharacter{1E3F}{\'m}% + % + \DeclareUnicodeCharacter{1E40}{\dotaccent{M}}% + \DeclareUnicodeCharacter{1E41}{\dotaccent{m}}% + \DeclareUnicodeCharacter{1E42}{\udotaccent{M}}% + \DeclareUnicodeCharacter{1E43}{\udotaccent{m}}% + \DeclareUnicodeCharacter{1E44}{\dotaccent{N}}% + \DeclareUnicodeCharacter{1E45}{\dotaccent{n}}% + \DeclareUnicodeCharacter{1E46}{\udotaccent{N}}% + \DeclareUnicodeCharacter{1E47}{\udotaccent{n}}% + \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}}% + \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}}% + % + \DeclareUnicodeCharacter{1E54}{\'P}% + \DeclareUnicodeCharacter{1E55}{\'p}% + \DeclareUnicodeCharacter{1E56}{\dotaccent{P}}% + \DeclareUnicodeCharacter{1E57}{\dotaccent{p}}% + \DeclareUnicodeCharacter{1E58}{\dotaccent{R}}% + \DeclareUnicodeCharacter{1E59}{\dotaccent{r}}% + \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}}% + \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}}% + \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}}% + \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}}% + % + \DeclareUnicodeCharacter{1E60}{\dotaccent{S}}% + \DeclareUnicodeCharacter{1E61}{\dotaccent{s}}% + \DeclareUnicodeCharacter{1E62}{\udotaccent{S}}% + \DeclareUnicodeCharacter{1E63}{\udotaccent{s}}% + \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}}% + \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}}% + \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}}% + \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}}% + \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}}% + \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}}% + % + \DeclareUnicodeCharacter{1E7C}{\~V}% + \DeclareUnicodeCharacter{1E7D}{\~v}% + \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}}% + \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}}% + % + \DeclareUnicodeCharacter{1E80}{\`W}% + \DeclareUnicodeCharacter{1E81}{\`w}% + \DeclareUnicodeCharacter{1E82}{\'W}% + \DeclareUnicodeCharacter{1E83}{\'w}% + \DeclareUnicodeCharacter{1E84}{\"W}% + \DeclareUnicodeCharacter{1E85}{\"w}% + \DeclareUnicodeCharacter{1E86}{\dotaccent{W}}% + \DeclareUnicodeCharacter{1E87}{\dotaccent{w}}% + \DeclareUnicodeCharacter{1E88}{\udotaccent{W}}% + \DeclareUnicodeCharacter{1E89}{\udotaccent{w}}% + \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}}% + \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}}% + \DeclareUnicodeCharacter{1E8C}{\"X}% + \DeclareUnicodeCharacter{1E8D}{\"x}% + \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}}% + \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}}% + % + \DeclareUnicodeCharacter{1E90}{\^Z}% + \DeclareUnicodeCharacter{1E91}{\^z}% + \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}}% + \DeclareUnicodeCharacter{1E93}{\udotaccent{z}}% + \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}}% + \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}}% + \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}}% + \DeclareUnicodeCharacter{1E97}{\"t}% + \DeclareUnicodeCharacter{1E98}{\ringaccent{w}}% + \DeclareUnicodeCharacter{1E99}{\ringaccent{y}}% + % + \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}}% + \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}}% + % + \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}}% + \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}}% + \DeclareUnicodeCharacter{1EBC}{\~E}% + \DeclareUnicodeCharacter{1EBD}{\~e}% + % + \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}}% + \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}}% + \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}}% + \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}}% + % + \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}}% + \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}}% + % + \DeclareUnicodeCharacter{1EF2}{\`Y}% + \DeclareUnicodeCharacter{1EF3}{\`y}% + \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}}% + % + \DeclareUnicodeCharacter{1EF8}{\~Y}% + \DeclareUnicodeCharacter{1EF9}{\~y}% + % + % Punctuation + \DeclareUnicodeCharacter{2013}{--}% + \DeclareUnicodeCharacter{2014}{---}% + \DeclareUnicodeCharacter{2018}{\quoteleft{}}% + \DeclareUnicodeCharacter{2019}{\quoteright{}}% + \DeclareUnicodeCharacter{201A}{\quotesinglbase{}}% + \DeclareUnicodeCharacter{201C}{\quotedblleft{}}% + \DeclareUnicodeCharacter{201D}{\quotedblright{}}% + \DeclareUnicodeCharacter{201E}{\quotedblbase{}}% + \DeclareUnicodeCharacter{2020}{\ensuremath\dagger}% + \DeclareUnicodeCharacter{2021}{\ensuremath\ddagger}% + \DeclareUnicodeCharacter{2022}{\bullet{}}% + \DeclareUnicodeCharacter{202F}{\thinspace}% + \DeclareUnicodeCharacter{2026}{\dots{}}% + \DeclareUnicodeCharacter{2039}{\guilsinglleft{}}% + \DeclareUnicodeCharacter{203A}{\guilsinglright{}}% + % + \DeclareUnicodeCharacter{20AC}{\euro{}}% + % + \DeclareUnicodeCharacter{2192}{\expansion{}}% + \DeclareUnicodeCharacter{21D2}{\result{}}% + % + % Mathematical symbols + \DeclareUnicodeCharacter{2200}{\ensuremath\forall}% + \DeclareUnicodeCharacter{2203}{\ensuremath\exists}% + \DeclareUnicodeCharacter{2208}{\ensuremath\in}% + \DeclareUnicodeCharacter{2212}{\minus{}}% + \DeclareUnicodeCharacter{2217}{\ast}% + \DeclareUnicodeCharacter{221E}{\ensuremath\infty}% + \DeclareUnicodeCharacter{2225}{\ensuremath\parallel}% + \DeclareUnicodeCharacter{2227}{\ensuremath\wedge}% + \DeclareUnicodeCharacter{2229}{\ensuremath\cap}% + \DeclareUnicodeCharacter{2261}{\equiv{}}% + \DeclareUnicodeCharacter{2264}{\ensuremath\leq}% + \DeclareUnicodeCharacter{2265}{\ensuremath\geq}% + \DeclareUnicodeCharacter{2282}{\ensuremath\subset}% + \DeclareUnicodeCharacter{2287}{\ensuremath\supseteq}% + % + \DeclareUnicodeCharacter{2016}{\ensuremath\Vert}% + \DeclareUnicodeCharacter{2032}{\ensuremath\prime}% + \DeclareUnicodeCharacter{210F}{\ensuremath\hbar}% + \DeclareUnicodeCharacter{2111}{\ensuremath\Im}% + \DeclareUnicodeCharacter{2113}{\ensuremath\ell}% + \DeclareUnicodeCharacter{2118}{\ensuremath\wp}% + \DeclareUnicodeCharacter{211C}{\ensuremath\Re}% + \DeclareUnicodeCharacter{2135}{\ensuremath\aleph}% + \DeclareUnicodeCharacter{2190}{\ensuremath\leftarrow}% + \DeclareUnicodeCharacter{2191}{\ensuremath\uparrow}% + \DeclareUnicodeCharacter{2193}{\ensuremath\downarrow}% + \DeclareUnicodeCharacter{2194}{\ensuremath\leftrightarrow}% + \DeclareUnicodeCharacter{2195}{\ensuremath\updownarrow}% + \DeclareUnicodeCharacter{2196}{\ensuremath\nwarrow}% + \DeclareUnicodeCharacter{2197}{\ensuremath\nearrow}% + \DeclareUnicodeCharacter{2198}{\ensuremath\searrow}% + \DeclareUnicodeCharacter{2199}{\ensuremath\swarrow}% + \DeclareUnicodeCharacter{21A6}{\ensuremath\mapsto}% + \DeclareUnicodeCharacter{21A9}{\ensuremath\hookleftarrow}% + \DeclareUnicodeCharacter{21AA}{\ensuremath\hookrightarrow}% + \DeclareUnicodeCharacter{21BC}{\ensuremath\leftharpoonup}% + \DeclareUnicodeCharacter{21BD}{\ensuremath\leftharpoondown}% + \DeclareUnicodeCharacter{21C0}{\ensuremath\rightharpoonup}% + \DeclareUnicodeCharacter{21C1}{\ensuremath\rightharpoondown}% + \DeclareUnicodeCharacter{21CC}{\ensuremath\rightleftharpoons}% + \DeclareUnicodeCharacter{21D0}{\ensuremath\Leftarrow}% + \DeclareUnicodeCharacter{21D1}{\ensuremath\Uparrow}% + \DeclareUnicodeCharacter{21D3}{\ensuremath\Downarrow}% + \DeclareUnicodeCharacter{21D4}{\ensuremath\Leftrightarrow}% + \DeclareUnicodeCharacter{21D5}{\ensuremath\Updownarrow}% + \DeclareUnicodeCharacter{2202}{\ensuremath\partial}% + \DeclareUnicodeCharacter{2205}{\ensuremath\emptyset}% + \DeclareUnicodeCharacter{2207}{\ensuremath\nabla}% + \DeclareUnicodeCharacter{2209}{\ensuremath\notin}% + \DeclareUnicodeCharacter{220B}{\ensuremath\owns}% + \DeclareUnicodeCharacter{220F}{\ensuremath\prod}% + \DeclareUnicodeCharacter{2210}{\ensuremath\coprod}% + \DeclareUnicodeCharacter{2211}{\ensuremath\sum}% + \DeclareUnicodeCharacter{2213}{\ensuremath\mp}% + \DeclareUnicodeCharacter{2218}{\ensuremath\circ}% + \DeclareUnicodeCharacter{221A}{\ensuremath\surd}% + \DeclareUnicodeCharacter{221D}{\ensuremath\propto}% + \DeclareUnicodeCharacter{2220}{\ensuremath\angle}% + \DeclareUnicodeCharacter{2223}{\ensuremath\mid}% + \DeclareUnicodeCharacter{2228}{\ensuremath\vee}% + \DeclareUnicodeCharacter{222A}{\ensuremath\cup}% + \DeclareUnicodeCharacter{222B}{\ensuremath\smallint}% + \DeclareUnicodeCharacter{222E}{\ensuremath\oint}% + \DeclareUnicodeCharacter{223C}{\ensuremath\sim}% + \DeclareUnicodeCharacter{2240}{\ensuremath\wr}% + \DeclareUnicodeCharacter{2243}{\ensuremath\simeq}% + \DeclareUnicodeCharacter{2245}{\ensuremath\cong}% + \DeclareUnicodeCharacter{2248}{\ensuremath\approx}% + \DeclareUnicodeCharacter{224D}{\ensuremath\asymp}% + \DeclareUnicodeCharacter{2250}{\ensuremath\doteq}% + \DeclareUnicodeCharacter{2260}{\ensuremath\neq}% + \DeclareUnicodeCharacter{226A}{\ensuremath\ll}% + \DeclareUnicodeCharacter{226B}{\ensuremath\gg}% + \DeclareUnicodeCharacter{227A}{\ensuremath\prec}% + \DeclareUnicodeCharacter{227B}{\ensuremath\succ}% + \DeclareUnicodeCharacter{2283}{\ensuremath\supset}% + \DeclareUnicodeCharacter{2286}{\ensuremath\subseteq}% + \DeclareUnicodeCharacter{228E}{\ensuremath\uplus}% + \DeclareUnicodeCharacter{2291}{\ensuremath\sqsubseteq}% + \DeclareUnicodeCharacter{2292}{\ensuremath\sqsupseteq}% + \DeclareUnicodeCharacter{2293}{\ensuremath\sqcap}% + \DeclareUnicodeCharacter{2294}{\ensuremath\sqcup}% + \DeclareUnicodeCharacter{2295}{\ensuremath\oplus}% + \DeclareUnicodeCharacter{2296}{\ensuremath\ominus}% + \DeclareUnicodeCharacter{2297}{\ensuremath\otimes}% + \DeclareUnicodeCharacter{2298}{\ensuremath\oslash}% + \DeclareUnicodeCharacter{2299}{\ensuremath\odot}% + \DeclareUnicodeCharacter{22A2}{\ensuremath\vdash}% + \DeclareUnicodeCharacter{22A3}{\ensuremath\dashv}% + \DeclareUnicodeCharacter{22A4}{\ensuremath\ptextop}% + \DeclareUnicodeCharacter{22A5}{\ensuremath\bot}% + \DeclareUnicodeCharacter{22A8}{\ensuremath\models}% + \DeclareUnicodeCharacter{22C0}{\ensuremath\bigwedge}% + \DeclareUnicodeCharacter{22C1}{\ensuremath\bigvee}% + \DeclareUnicodeCharacter{22C2}{\ensuremath\bigcap}% + \DeclareUnicodeCharacter{22C3}{\ensuremath\bigcup}% + \DeclareUnicodeCharacter{22C4}{\ensuremath\diamond}% + \DeclareUnicodeCharacter{22C5}{\ensuremath\cdot}% + \DeclareUnicodeCharacter{22C6}{\ensuremath\star}% + \DeclareUnicodeCharacter{22C8}{\ensuremath\bowtie}% + \DeclareUnicodeCharacter{2308}{\ensuremath\lceil}% + \DeclareUnicodeCharacter{2309}{\ensuremath\rceil}% + \DeclareUnicodeCharacter{230A}{\ensuremath\lfloor}% + \DeclareUnicodeCharacter{230B}{\ensuremath\rfloor}% + \DeclareUnicodeCharacter{2322}{\ensuremath\frown}% + \DeclareUnicodeCharacter{2323}{\ensuremath\smile}% + % + \DeclareUnicodeCharacter{25B3}{\ensuremath\triangle}% + \DeclareUnicodeCharacter{25B7}{\ensuremath\triangleright}% + \DeclareUnicodeCharacter{25BD}{\ensuremath\bigtriangledown}% + \DeclareUnicodeCharacter{25C1}{\ensuremath\triangleleft}% + \DeclareUnicodeCharacter{25C7}{\ensuremath\diamond}% + \DeclareUnicodeCharacter{2660}{\ensuremath\spadesuit}% + \DeclareUnicodeCharacter{2661}{\ensuremath\heartsuit}% + \DeclareUnicodeCharacter{2662}{\ensuremath\diamondsuit}% + \DeclareUnicodeCharacter{2663}{\ensuremath\clubsuit}% + \DeclareUnicodeCharacter{266D}{\ensuremath\flat}% + \DeclareUnicodeCharacter{266E}{\ensuremath\natural}% + \DeclareUnicodeCharacter{266F}{\ensuremath\sharp}% + \DeclareUnicodeCharacter{26AA}{\ensuremath\bigcirc}% + \DeclareUnicodeCharacter{27B9}{\ensuremath\rangle}% + \DeclareUnicodeCharacter{27C2}{\ensuremath\perp}% + \DeclareUnicodeCharacter{27E8}{\ensuremath\langle}% + \DeclareUnicodeCharacter{27F5}{\ensuremath\longleftarrow}% + \DeclareUnicodeCharacter{27F6}{\ensuremath\longrightarrow}% + \DeclareUnicodeCharacter{27F7}{\ensuremath\longleftrightarrow}% + \DeclareUnicodeCharacter{27FC}{\ensuremath\longmapsto}% + \DeclareUnicodeCharacter{29F5}{\ensuremath\setminus}% + \DeclareUnicodeCharacter{2A00}{\ensuremath\bigodot}% + \DeclareUnicodeCharacter{2A01}{\ensuremath\bigoplus}% + \DeclareUnicodeCharacter{2A02}{\ensuremath\bigotimes}% + \DeclareUnicodeCharacter{2A04}{\ensuremath\biguplus}% + \DeclareUnicodeCharacter{2A06}{\ensuremath\bigsqcup}% + \DeclareUnicodeCharacter{2A3F}{\ensuremath\amalg}% + \DeclareUnicodeCharacter{2AAF}{\ensuremath\preceq}% + \DeclareUnicodeCharacter{2AB0}{\ensuremath\succeq}% + % + \global\mathchardef\checkmark="1370% actually the square root sign + \DeclareUnicodeCharacter{2713}{\ensuremath\checkmark}% +}% end of \unicodechardefs + +% UTF-8 byte sequence (pdfTeX) definitions (replacing and @U command) +% It makes the setting that replace UTF-8 byte sequence. +\def\utfeightchardefs{% + \let\DeclareUnicodeCharacter\DeclareUnicodeCharacterUTFviii + \unicodechardefs +} + +% Whether the active definitions of non-ASCII characters expand to +% non-active tokens with the same character code. This is used to +% write characters literally, instead of using active definitions for +% printing the correct glyphs. +\newif\ifpassthroughchars +\passthroughcharsfalse + +% For native Unicode handling (XeTeX and LuaTeX), +% provide a definition macro to replace/pass-through a Unicode character +% +\def\DeclareUnicodeCharacterNative#1#2{% + \catcode"#1=\active + \def\dodeclareunicodecharacternative##1##2##3{% + \begingroup + \uccode`\~="##2\relax + \uppercase{\gdef~}{% + \ifpassthroughchars + ##1% + \else + ##3% + \fi + } + \endgroup + } + \begingroup + \uccode`\.="#1\relax + \uppercase{\def\UTFNativeTmp{.}}% + \expandafter\dodeclareunicodecharacternative\UTFNativeTmp{#1}{#2}% + \endgroup +} + +% Native Unicode handling (XeTeX and LuaTeX) character replacing definition. +% It activates the setting that replaces Unicode characters. +\def\nativeunicodechardefs{% + \let\DeclareUnicodeCharacter\DeclareUnicodeCharacterNative + \unicodechardefs +} + +% For native Unicode handling (XeTeX and LuaTeX), +% make the character token expand +% to the sequences given in \unicodechardefs for printing. +\def\DeclareUnicodeCharacterNativeAtU#1#2{% + \def\UTFAtUTmp{#2} + \expandafter\globallet\csname uni:#1\endcsname \UTFAtUTmp +} + +% @U command definitions for native Unicode handling (XeTeX and LuaTeX). +\def\nativeunicodechardefsatu{% + \let\DeclareUnicodeCharacter\DeclareUnicodeCharacterNativeAtU + \unicodechardefs +} + +% US-ASCII character definitions. +\def\asciichardefs{% nothing need be done + \relax +} + +% Define all Unicode characters we know about. This makes UTF-8 the default +% input encoding and allows @U to work. +\iftxinativeunicodecapable + \nativeunicodechardefsatu +\else + \utfeightchardefs +\fi + +\message{formatting,} + +\newdimen\defaultparindent \defaultparindent = 15pt + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness = 10000 + +% Don't be very finicky about underfull hboxes, either. +\hbadness = 6666 + +% Following George Bush, get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. We call this whenever the paper size is set. +% +\def\setemergencystretch{% + \ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% + \else + \emergencystretch = .15\hsize + \fi +} + +% Parameters in order: 1) textheight; 2) textwidth; +% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; +% 7) physical page height; 8) physical page width. +% +% We also call \setleading{\textleading}, so the caller should define +% \textleading. The caller should also set \parskip. +% +\def\internalpagesizes#1#2#3#4#5#6#7#8{% + \voffset = #3\relax + \topskip = #6\relax + \splittopskip = \topskip + % + \vsize = #1\relax + \advance\vsize by \topskip + \outervsize = \vsize + \advance\outervsize by 2\topandbottommargin + \txipageheight = \vsize + % + \hsize = #2\relax + \outerhsize = \hsize + \advance\outerhsize by 0.5in + \txipagewidth = \hsize + % + \normaloffset = #4\relax + \bindingoffset = #5\relax + % + \ifpdf + \pdfpageheight #7\relax + \pdfpagewidth #8\relax + % if we don't reset these, they will remain at "1 true in" of + % whatever layout pdftex was dumped with. + \pdfhorigin = 1 true in + \pdfvorigin = 1 true in + \else + \ifx\XeTeXrevision\thisisundefined + \special{papersize=#8,#7}% + \else + \pdfpageheight #7\relax + \pdfpagewidth #8\relax + % XeTeX does not have \pdfhorigin and \pdfvorigin. + \fi + \fi + % + \setleading{\textleading} + % + \parindent = \defaultparindent + \setemergencystretch +} + +% @letterpaper (the default). +\def\letterpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % If page is nothing but text, make it come out even. + \internalpagesizes{607.2pt}{6in}% that's 46 lines + {\voffset}{.25in}% + {\bindingoffset}{36pt}% + {11in}{8.5in}% +}} + +% Use @smallbook to reset parameters for 7x9.25 trim size. +\def\smallbook{{\globaldefs = 1 + \parskip = 2pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.5in}{5in}% + {-.2in}{0in}% + {\bindingoffset}{16pt}% + {9.25in}{7in}% + % + \lispnarrowing = 0.3in + \tolerance = 700 + \contentsrightmargin = 0pt + \defbodyindent = .5cm +}} + +% Use @smallerbook to reset parameters for 6x9 trim size. +% (Just testing, parameters still in flux.) +\def\smallerbook{{\globaldefs = 1 + \parskip = 1.5pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.4in}{4.8in}% + {-.2in}{-.4in}% + {0pt}{14pt}% + {9in}{6in}% + % + \lispnarrowing = 0.25in + \tolerance = 700 + \contentsrightmargin = 0pt + \defbodyindent = .4cm +}} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % Double-side printing via postscript on Laserjet 4050 + % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. + % To change the settings for a different printer or situation, adjust + % \normaloffset until the front-side and back-side texts align. Then + % do the same for \bindingoffset. You can set these for testing in + % your texinfo source file like this: + % @tex + % \global\normaloffset = -6mm + % \global\bindingoffset = 10mm + % @end tex + \internalpagesizes{673.2pt}{160mm}% that's 51 lines + {\voffset}{\hoffset}% + {\bindingoffset}{44pt}% + {297mm}{210mm}% + % + \tolerance = 700 + \contentsrightmargin = 0pt + \defbodyindent = 5mm +}} + +% Use @afivepaper to print on European A5 paper. +% From romildo@urano.iceb.ufop.br, 2 July 2000. +% He also recommends making @example and @lisp be small. +\def\afivepaper{{\globaldefs = 1 + \parskip = 2pt plus 1pt minus 0.1pt + \textleading = 12.5pt + % + \internalpagesizes{160mm}{120mm}% + {\voffset}{\hoffset}% + {\bindingoffset}{8pt}% + {210mm}{148mm}% + % + \lispnarrowing = 0.2in + \tolerance = 800 + \contentsrightmargin = 0pt + \defbodyindent = 2mm + \tableindent = 12mm +}} + +% A specific text layout, 24x15cm overall, intended for A4 paper. +\def\afourlatex{{\globaldefs = 1 + \afourpaper + \internalpagesizes{237mm}{150mm}% + {\voffset}{4.6mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + % + % Must explicitly reset to 0 because we call \afourpaper. + \globaldefs = 0 +}} + +% Use @afourwide to print on A4 paper in landscape format. +\def\afourwide{{\globaldefs = 1 + \afourpaper + \internalpagesizes{241mm}{165mm}% + {\voffset}{-2.95mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + \globaldefs = 0 +}} + +\def\bsixpaper{{\globaldefs = 1 + \afourpaper + \internalpagesizes{140mm}{100mm}% + {-6.35mm}{-12.7mm}% + {\bindingoffset}{14pt}% + {176mm}{125mm}% + \let\SETdispenvsize=\smallword + \lispnarrowing = 0.2in + \globaldefs = 0 +}} + + +% @pagesizes TEXTHEIGHT[,TEXTWIDTH] +% Perhaps we should allow setting the margins, \topskip, \parskip, +% and/or leading, also. Or perhaps we should compute them somehow. +% +\parseargdef\pagesizes{\pagesizesyyy #1,,\finish} +\def\pagesizesyyy#1,#2,#3\finish{{% + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi + \globaldefs = 1 + % + \parskip = 3pt plus 2pt minus 1pt + \setleading{\textleading}% + % + \dimen0 = #1\relax + \advance\dimen0 by 2.5in % default 1in margin above heading line + % and 1.5in to include heading, footing and + % bottom margin + % + \dimen2 = \hsize + \advance\dimen2 by 2in % default to 1 inch margin on each side + % + \internalpagesizes{#1}{\hsize}% + {\voffset}{\normaloffset}% + {\bindingoffset}{44pt}% + {\dimen0}{\dimen2}% +}} + +% Set default to letter. +% +\letterpaper + +% Default value of \hfuzz, for suppressing warnings about overfull hboxes. +\hfuzz = 1pt + + +\message{and turning on texinfo input format.} + +\def^^L{\par} % remove \outer, so ^L can appear in an @comment + +% DEL is a comment character, in case @c does not suffice. +\catcode`\^^? = 14 + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other \def\normaldoublequote{"} +\catcode`\$=\other \def\normaldollar{$}%$ font-lock fix +\catcode`\+=\other \def\normalplus{+} +\catcode`\<=\other \def\normalless{<} +\catcode`\>=\other \def\normalgreater{>} +\catcode`\^=\other \def\normalcaret{^} +\catcode`\_=\other \def\normalunderscore{_} +\catcode`\|=\other \def\normalverticalbar{|} +\catcode`\~=\other \def\normaltilde{~} + +% This macro is used to make a character print one way in \tt +% (where it can probably be output as-is), and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} + +% Same as above, but check for italic font. Actually this also catches +% non-italic slanted fonts since it is impossible to distinguish them from +% italic fonts. But since this is only used by $ and it uses \sl anyway +% this is not a problem. +\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} + +% Set catcodes for Texinfo file + +% Active characters for printing the wanted glyph. +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. +% +\catcode`\"=\active +\def\activedoublequote{{\tt\char34}} +\let"=\activedoublequote +\catcode`\~=\active \def\activetilde{{\tt\char126}} \let~ = \activetilde +\chardef\hatchar=`\^ +\catcode`\^=\active \def\activehat{{\tt \hatchar}} \let^ = \activehat + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } +\let\realunder=_ + +\catcode`\|=\active \def|{{\tt\char124}} + +\chardef \less=`\< +\catcode`\<=\active \def\activeless{{\tt \less}}\let< = \activeless +\chardef \gtr=`\> +\catcode`\>=\active \def\activegtr{{\tt \gtr}}\let> = \activegtr +\catcode`\+=\active \def+{{\tt \char 43}} +\catcode`\$=\active \def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix +\catcode`\-=\active \let-=\normaldash + + +% used for headline/footline in the output routine, in case the page +% breaks in the middle of an @tex block. +\def\texinfochars{% + \let< = \activeless + \let> = \activegtr + \let~ = \activetilde + \let^ = \activehat + \setregularquotes + \let\b = \strong + \let\i = \smartitalic + % in principle, all other definitions in \tex have to be undone too. +} + +% Used sometimes to turn off (effectively) the active characters even after +% parsing them. +\def\turnoffactive{% + \normalturnoffactive + \otherbackslash +} + +\catcode`\@=0 + +% \backslashcurfont outputs one backslash character in current font, +% as in \char`\\. +\global\chardef\backslashcurfont=`\\ + +% \realbackslash is an actual character `\' with catcode other. +{\catcode`\\=\other @gdef@realbackslash{\}} + +% In Texinfo, backslash is an active character; it prints the backslash +% in fixed width font. +\catcode`\\=\active % @ for escape char from now on. + +% Print a typewriter backslash. For math mode, we can't simply use +% \backslashcurfont: the story here is that in math mode, the \char +% of \backslashcurfont ends up printing the roman \ from the math symbol +% font (because \char in math mode uses the \mathcode, and plain.tex +% sets \mathcode`\\="026E). Hence we use an explicit \mathchar, +% which is the decimal equivalent of "715c (class 7, e.g., use \fam; +% ignored family value; char position "5C). We can't use " for the +% usual hex value because it has already been made active. + +@def@ttbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}} +@let@backslashchar = @ttbackslash % @backslashchar{} is for user documents. + +% \otherbackslash defines an active \ to be a literal `\' character with +% catcode other. +@gdef@otherbackslash{@let\=@realbackslash} + +% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of +% the literal character `\'. +% +{@catcode`- = @active + @gdef@normalturnoffactive{% + @passthroughcharstrue + @let-=@normaldash + @let"=@normaldoublequote + @let$=@normaldollar %$ font-lock fix + @let+=@normalplus + @let<=@normalless + @let>=@normalgreater + @let^=@normalcaret + @let_=@normalunderscore + @let|=@normalverticalbar + @let~=@normaltilde + @let\=@ttbackslash + @setregularquotes + @unsepspaces + } +} + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have @fixbackslash turn them back on. +@catcode`+=@other @catcode`@_=@other + +% \enablebackslashhack - allow file to begin `\input texinfo' +% +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% If the file did not have a `\input texinfo', then it is turned off after +% the first line; otherwise the first `\' in the file would cause an error. +% This is used on the very last line of this file, texinfo.tex. +% We also use @c to call @fixbackslash, in case ends of lines are hidden. +{ +@catcode`@^=7 +@catcode`@^^M=13@gdef@enablebackslashhack{% + @global@let\ = @eatinput% + @catcode`@^^M=13% + @def@c{@fixbackslash@c}% + % Definition for the newline at the end of this file. + @def ^^M{@let^^M@secondlinenl}% + % Definition for a newline in the main Texinfo file. + @gdef @secondlinenl{@fixbackslash}% + % In case the first line has a whole-line command on it + @let@originalparsearg@parsearg + @def@parsearg{@fixbackslash@originalparsearg} +}} + +{@catcode`@^=7 @catcode`@^^M=13% +@gdef@eatinput input texinfo#1^^M{@fixbackslash}} + +% Emergency active definition of newline, in case an active newline token +% appears by mistake. +{@catcode`@^=7 @catcode13=13% +@gdef@enableemergencynewline{% + @gdef^^M{% + @par% + %<warning: active newline>@par% +}}} + + +@gdef@fixbackslash{% + @ifx\@eatinput @let\ = @ttbackslash @fi + @catcode13=5 % regular end of line + @enableemergencynewline + @let@c=@comment + @let@parsearg@originalparsearg + % Also turn back on active characters that might appear in the input + % file name, in case not using a pre-dumped format. + @catcode`+=@active + @catcode`@_=@active + % + % If texinfo.cnf is present on the system, read it. + % Useful for site-wide @afourpaper, etc. This macro, @fixbackslash, gets + % called at the beginning of every Texinfo file. Not opening texinfo.cnf + % directly in this file, texinfo.tex, makes it possible to make a format + % file for Texinfo. + % + @openin 1 texinfo.cnf + @ifeof 1 @else @input texinfo.cnf @fi + @closein 1 +} + + +% Say @foo, not \foo, in error messages. +@escapechar = `@@ + +% These (along with & and #) are made active for url-breaking, so need +% active definitions as the normal characters. +@def@normaldot{.} +@def@normalquest{?} +@def@normalslash{/} + +% These look ok in all fonts, so just make them not special. +% @hashchar{} gets its own user-level command, because of #line. +@catcode`@& = @other @def@normalamp{&} +@catcode`@# = @other @def@normalhash{#} +@catcode`@% = @other @def@normalpercent{%} + +@let @hashchar = @normalhash + +@c Finally, make ` and ' active, so that txicodequoteundirected and +@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we +@c don't make ` and ' active, @code will not get them as active chars. +@c Do this last of all since we use ` in the previous @catcode assignments. +@catcode`@'=@active +@catcode`@`=@active +@setregularquotes + +@c Local variables: +@c eval: (add-hook 'before-save-hook 'time-stamp) +@c page-delimiter: "^\\\\message\\|emacs-page" +@c time-stamp-start: "def\\\\texinfoversion{" +@c time-stamp-format: "%:y-%02m-%02d.%02H" +@c time-stamp-end: "}" +@c End: + +@c vim:sw=2: + +@enablebackslashhack diff --git a/gprofng/doc/version.texi b/gprofng/doc/version.texi new file mode 100644 index 0000000..d282161 --- /dev/null +++ b/gprofng/doc/version.texi @@ -0,0 +1,4 @@ +@set EDITION 1.0 +@set VERSION 1.0 +@set UPDATED 22 February 2022 +@set UPDATED-MONTH February 2022 diff --git a/gprofng/gp-display-html/Makefile.am b/gprofng/gp-display-html/Makefile.am new file mode 100644 index 0000000..7fc27d1 --- /dev/null +++ b/gprofng/gp-display-html/Makefile.am @@ -0,0 +1,60 @@ +## Process this file with automake to generate Makefile.in +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I . -I .. -I ../.. + +dist_man_MANS = gp-display-html.1 +bin_SCRIPTS = gp-display-html +CLEANFILES = $(bin_SCRIPTS) +MAINTAINERCLEANFILES = $(dist_man_MANS) + +do_subst = sed -e 's/BINUTILS_VERSION/$(VERSION)/' + +gp-display-html: gp-display-html.in Makefile + $(do_subst) < $(srcdir)/gp-display-html.in > $@ + chmod +x $@ + +if BUILD_MAN + +# Use this if the man pages depend on the version number. +# common_mandeps = $(top_srcdir)/../bfd/version.m4 +# +# Also change the dependence line below to this: +# gp-display-html.1: $(common_mandeps) gp-display-html +# +# Currently, the version number shown in the man page is derived from +# the output printed with --version. + +# These variables are used by help2man to generate the man pages. + +INFO_PAGE = "gprofng" +MANUAL = "User Commands" +TEXT_GP_DISPLAY_HTML = "generate an HTML based directory structure to browse the profiles" + +HELP2MAN_OPT = --libtool --no-info --info-page=$(INFO_PAGE) --manual=$(MANUAL) +H2M_FILTER = | sed 's/\.TP/\.TP\n.B/' | sed 's/Commands:/\.SH COMMANDS/' \ + | sed 's/See also:/\.SH SEE ALSO/' | sed 's/Documentation:/.SH DOCUMENTATION/' \ + | sed 's/Limitations:/.SH LIMITATIONS/' + +gp-display-html.1: gp-display-html + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GP_DISPLAY_HTML) ./gp-display-html $(H2M_FILTER) > $@ + +endif + diff --git a/gprofng/gp-display-html/Makefile.in b/gprofng/gp-display-html/Makefile.in new file mode 100644 index 0000000..10f59ee --- /dev/null +++ b/gprofng/gp-display-html/Makefile.in @@ -0,0 +1,630 @@ +# Makefile.in generated by automake 1.15.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2017 Free Software Foundation, Inc. + +# This Makefile.in 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. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = gp-display-html +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/../libtool.m4 \ + $(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \ + $(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/../config/warnings.m4 \ + $(top_srcdir)/../config/enable.m4 \ + $(top_srcdir)/../config/ax_pthread.m4 \ + $(top_srcdir)/config/bison.m4 $(top_srcdir)/../bfd/version.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" +SCRIPTS = $(bin_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +man1dir = $(mandir)/man1 +NROFF = nroff +MANS = $(dist_man_MANS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.in \ + $(top_srcdir)/../mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_SUBDIRS = @BUILD_SUBDIRS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPECT = @EXPECT@ +FGREP = @FGREP@ +GPROFNG_CFLAGS = @GPROFNG_CFLAGS@ +GPROFNG_CPPFLAGS = @GPROFNG_CPPFLAGS@ +GPROFNG_LIBADD = @GPROFNG_LIBADD@ +GPROFNG_LIBDIR = @GPROFNG_LIBDIR@ +GREP = @GREP@ +HELP2MAN = @HELP2MAN@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JAVA = @JAVA@ +JAVAC = @JAVAC@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LD_NO_AS_NEEDED = @LD_NO_AS_NEEDED@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WERROR = @WERROR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +gprofng_cflags = @gprofng_cflags@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +jdk_inc = @jdk_inc@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I . -I .. -I ../.. +dist_man_MANS = gp-display-html.1 +bin_SCRIPTS = gp-display-html +CLEANFILES = $(bin_SCRIPTS) +MAINTAINERCLEANFILES = $(dist_man_MANS) +do_subst = sed -e 's/BINUTILS_VERSION/$(VERSION)/' + +# Use this if the man pages depend on the version number. +# common_mandeps = $(top_srcdir)/../bfd/version.m4 +# +# Also change the dependence line below to this: +# gp-display-html.1: $(common_mandeps) gp-display-html +# +# Currently, the version number shown in the man page is derived from +# the output printed with --version. + +# These variables are used by help2man to generate the man pages. +@BUILD_MAN_TRUE@INFO_PAGE = "gprofng" +@BUILD_MAN_TRUE@MANUAL = "User Commands" +@BUILD_MAN_TRUE@TEXT_GP_DISPLAY_HTML = "generate an HTML based directory structure to browse the profiles" +@BUILD_MAN_TRUE@HELP2MAN_OPT = --libtool --no-info --info-page=$(INFO_PAGE) --manual=$(MANUAL) +@BUILD_MAN_TRUE@H2M_FILTER = | sed 's/\.TP/\.TP\n.B/' | sed 's/Commands:/\.SH COMMANDS/' \ +@BUILD_MAN_TRUE@ | sed 's/See also:/\.SH SEE ALSO/' | sed 's/Documentation:/.SH DOCUMENTATION/' \ +@BUILD_MAN_TRUE@ | sed 's/Limitations:/.SH LIMITATIONS/' + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign gp-display-html/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign gp-display-html/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man1: $(dist_man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(dist_man_MANS)'; \ + test -n "$(man1dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.1[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ + done; } + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man1dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.1[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(SCRIPTS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binSCRIPTS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man1 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binSCRIPTS uninstall-man + +uninstall-man: uninstall-man1 + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-binSCRIPTS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-man1 install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am uninstall-binSCRIPTS uninstall-man uninstall-man1 + +.PRECIOUS: Makefile + + +gp-display-html: gp-display-html.in Makefile + $(do_subst) < $(srcdir)/gp-display-html.in > $@ + chmod +x $@ + +@BUILD_MAN_TRUE@gp-display-html.1: gp-display-html +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GP_DISPLAY_HTML) ./gp-display-html $(H2M_FILTER) > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gprofng/gp-display-html/gp-display-html.in b/gprofng/gp-display-html/gp-display-html.in new file mode 100644 index 0000000..f8fbc24 --- /dev/null +++ b/gprofng/gp-display-html/gp-display-html.in @@ -0,0 +1,256 @@ +#!/usr/bin/perl + +# Copyright (C) 2021 Free Software Foundation, Inc. +# Contributed by Oracle. +# +# This file is part of GNU Binutils. +# +# 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, 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, 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +#------------------------------------------------------------------------------ +# gp-display-html, last updated July 2021 +# +# NOTE: This is a skeleton version. The real code will follow as an update. +#------------------------------------------------------------------------------ + +use strict; +use warnings; + +#------------------------------------------------------------------------------ +# Poor man's version of a boolean. +#------------------------------------------------------------------------------ +my $TRUE = 1; +my $FALSE = 0; + +#------------------------------------------------------------------------------- +# Define the driver command, tool name and version number. +#------------------------------------------------------------------------------- +my $driver_cmd = "gprofng display html"; +my $tool_name = "gp-display-html"; +my $binutils_version = "BINUTILS_VERSION"; +my $version_info = $tool_name . " GNU binutils version " . $binutils_version; + +#------------------------------------------------------------------------------ +# This is cosmetic, but helps with the scoping of variables. +#------------------------------------------------------------------------------ + + main (); + + exit (0); + +#------------------------------------------------------------------------------ +# THE SUBROUTINES +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# This is the driver part of the program. +#------------------------------------------------------------------------------ +sub +main +{ + my $subr_name = "main"; + my $ignore_value; + +#------------------------------------------------------------------------------ +# If no options are given, print the help info and exit. +#------------------------------------------------------------------------------ + $ignore_value = early_scan_specific_options(); + + $ignore_value = be_patient (); + + return (0); + +} #-- End of subroutine main + +sub +be_patient +{ + print "Functionality not implemented yet - please stay tuned for updates\n"; + +} #-- End of subroutine be_patient + +#------------------------------------------------------------------------------ +# Prints the version number and license information. +#------------------------------------------------------------------------------ +sub +print_version_info +{ + print "$version_info\n"; + print "Copyright (C) 2021 Free Software Foundation, Inc.\n"; + print "License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n"; + print "This is free software: you are free to change and redistribute it.\n"; + print "There is NO WARRANTY, to the extent permitted by law.\n"; + + return (0); + +} #-- End of subroutine print_version_info + +#------------------------------------------------------------------------------- +# Print the help overview +#------------------------------------------------------------------------------- +sub +print_help_info +{ + print + "Usage: $driver_cmd [OPTION(S)] EXPERIMENT(S)\n". + "\n". + "Process one or more experiments to generate a directory containing an index.html\n". + "file that can be used to browse the experiment data\n". + "\n". + "Options:\n". + "\n". + " --help print usage information and exit.\n". + " --version print the version number and exit.\n". + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n". + "\n". + "\n". + " -o, --output <dir-name> use <dir-name> to store the results in; the default\n". + " name is ./display.<n>.html with <n> the first number\n". + " not in use; an existing directory is not overwritten.\n". + "\n". + " -O, --overwrite <dir-name> use <dir-name> to store the results in and overwrite\n". + " any existing directory with the same name; make sure\n". + " that umask is set to the correct access permissions.\n". + "\n". + " -fl, --func_limit <limit> impose a limit on the number of functions processed;\n". + " this is an integer number; set to 0 to process all\n". + " functions; the default value is 100.\n". + "\n". + " -ct, --calltree {on|off} enable or disable an html page with a call tree linked\n". + " from the bottom of the first page; default is off.\n". + "\n". + " -tp, --threshold_percentage <percentage> provide a percentage of metric accountability; the\n". + " inclusion of functions for each metric will take\n". + " place in sort order until the percentage has been\n". + " reached.\n". + "\n". + " -dm, --default_metrics {on|off} enable or disable automatic selection of metrics\n". + " and use a default set of metrics; the default is off.\n". + "\n". + " -im, --ignore_metrics <metric-list> ignore the metrics from <metric-list>.\n". + "\n". + " -db, --debug {on|off} enable/disable debug mode; print detailed information to assist with troubleshooting\n". + " or further development of this tool; default is off.\n". + "\n". + " -q, --quiet {on|off} disable/enable the display of warnings; default is off.\n". + "\n". + "Environment:\n". + "\n". + "The options can be set in a configuration file called .gp-display-html.rc. This\n". + "file needs to be either in the current directory, or in the home directory of the user.\n". + "The long name of the option without the leading dashes is supported. For example calltree\n". + "to enable or disable the call tree. Note that some options take a value. In case the same option\n". + "occurs multiple times in this file, only the last setting encountered is preserved.\n". + "\n". + "Documentation:\n". + "\n". + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n". + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n". + "should give you access to this document.\n". + "\n". + "See also:\n". + "\n". + "gprofng(1), gp-archive(1), gp-collect-app(1), gp-display-src(1), gp-display-text(1)\n"; + + return (0); + +} #-- End of subroutine print_help_info + +#------------------------------------------------------------------------------ +# Scan the command line for specific options. +#------------------------------------------------------------------------------ +sub +early_scan_specific_options +{ + my $subr_name = "early_scan_specific_options"; + + my $ignore_value; + my $found_option; + my $option_has_value; + my $option_value; + + my $verbose_setting = $FALSE; + my $debug_setting = $FALSE; + my $quiet_setting = $FALSE; + + $option_has_value = $FALSE; + ($found_option, $option_value) = find_target_option (\@ARGV, $option_has_value, "--version"); + if ($found_option) + { + $ignore_value = print_version_info (); + exit(0); + } + $option_has_value = $FALSE; + ($found_option, $option_value) = find_target_option (\@ARGV, $option_has_value, "--help"); + if ($found_option) + { + $ignore_value = print_help_info (); + exit(0); + } + + return (0); + +} #-- End of subroutine early_scan_specific_options + +#------------------------------------------------------------------------------ +# Scan the command line to see if the specified option is present. +# +# Two types of options are supported: options without value (e.g. --help) or +# those that are set to "on" or "off". +#------------------------------------------------------------------------------ +sub +find_target_option +{ + my ($command_line_ref, $has_value, $target_option) = @_; + + my @command_line = @{ $command_line_ref }; + + my ($command_line_string) = join(" ", @command_line); + + my $option_value = "not set"; + my $found_option = $FALSE; + + if ($command_line_string =~ /\s*($target_option)\s*(on|off)*\s*/) + { + if ($has_value) + { +#------------------------------------------------------------------------------ +# We are looking for this kind if substring: "--verbose on" +#------------------------------------------------------------------------------ + if (defined($1) and defined($2)) + { + if ( ($2 eq "on") or ($2 eq "off") ) + { + $found_option = $TRUE; + $option_value = $2; + } + } + } + else + { +#------------------------------------------------------------------------------ +# We are looking for this kind if substring: "--help" +#------------------------------------------------------------------------------ + if (defined($1)) + { + $found_option = $TRUE; + } + } + } + + return($found_option, $option_value); + +} #-- End of subroutine find_target_option diff --git a/gprofng/libcollector/CHK_LIBC_OBJ b/gprofng/libcollector/CHK_LIBC_OBJ new file mode 100755 index 0000000..dbeb9cb --- /dev/null +++ b/gprofng/libcollector/CHK_LIBC_OBJ @@ -0,0 +1,82 @@ +#!/bin/sh +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# +# CHK_LIBC_OBJ -- a script to scan the .o's in an output directory, +# which is one of ../{intel-S2,sparc-S2,intel-Linux,sparc-Linux} +# +# usage: cd to the output directory, and invoke ../src/CHK_LIBC_OBJ + + +check_obj() { + logF="nm.`basename $1`.log" + if [ `uname` = 'Linux' ]; then + nm $1 | grep -v GLIBC_ > ${logF} + else + nm $1 > ${logF} + fi + + FUNC_LIST="strcpy strlcpy strncpy strcat strlcat strncat strncmp strlen \ + strerror strchr strrchr strpbrk strstr strtok strtok_r \ + printf fprintf sprintf snprintf asprintf wsprintf \ + vprintf vfprintf vsprintf vsnprintf vasprintf \ + memset memcmp memcpy strtol strtoll strtoul strtoull \ + getcpuid calloc malloc free strdup" + res=0 + echo " -- Checking Object file '$1' for functions from libc" + for j in `echo ${FUNC_LIST}` ; do + grep -w ${j} ${logF} | grep UNDEF> grep.log 2>&1 + if [ $? -eq 0 ]; then + grep -w ${j} ${logF} + res=1 + fi + done + return ${res} +} + +STATUS=0 + +for i in *.o ; do + echo "" + check_obj ${i} + res=$? + if [ ${res} -eq 0 ]; then + echo "Object file ${i} does not reference functions in libc" + else + echo "======Object file: ${i} DOES reference functions in libc" + fi + if [ ${STATUS} -eq 0 ]; then + STATUS=${res} + fi +done + +for i in *.so ; do + echo "" + check_obj ${i} + res=$? + if [ ${res} -eq 0 ]; then + echo "Object file ${i} does not reference functions in libc" + else + echo "======Object file: ${i} DOES reference functions in libc" + fi + if [ ${STATUS} -eq 0 ]; then + STATUS=${res} + fi +done + +exit $STATUS diff --git a/gprofng/libcollector/Makefile.am b/gprofng/libcollector/Makefile.am new file mode 100644 index 0000000..bd86e97 --- /dev/null +++ b/gprofng/libcollector/Makefile.am @@ -0,0 +1,79 @@ +## Process this file with automake to generate Makefile.in +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I . -I ../.. + +GPROFNG_VARIANT = @GPROFNG_VARIANT@ + +CSOURCES = \ + gethrtime.c \ + dispatcher.c \ + iolib.c \ + mmaptrace.c \ + memmgr.c \ + tsd.c \ + profile.c \ + envmgmt.c \ + linetrace.c \ + libcol_hwcdrv.c \ + libcol_hwcfuncs.c \ + libcol-i386-dis.c \ + hwprofile.c \ + jprofile.c \ + unwind.c \ + libcol_util.c \ + collector.c \ + $(NULL) + +AM_CFLAGS = $(GPROFNG_CFLAGS) -Wno-nonnull-compare +AM_CPPFLAGS = $(GPROFNG_CPPFLAGS) -I.. -I$(srcdir) \ + -I$(srcdir)/../common -I$(srcdir)/../src \ + -I$(srcdir)/../../include +AM_LDFLAGS = -module -avoid-version \ + -Wl,--version-script,$(srcdir)/mapfile.$(GPROFNG_VARIANT) \ + $(LD_NO_AS_NEEDED) -Wl,-lrt -Wl,-ldl + +myincludedir = @includedir@ +myinclude_HEADERS = $(srcdir)/../../include/collectorAPI.h \ + $(srcdir)/../../include/libcollector.h \ + $(srcdir)/../../include/libfcollector.h + +lib_LTLIBRARIES = libgp-collector.la libgp-collectorAPI.la libgp-heap.la \ + libgp-sync.la libgp-iotrace.la + +libgp_collector_la_SOURCES = $(CSOURCES) +libgp_collector_la_CPPFLAGS = $(AM_CPPFLAGS) $(jdk_inc) \ + -I../../bfd -I$(srcdir)/../.. +# Prevent libtool from reordering -Wl,--no-as-needed after -lrt by +# disguising -lrt as a linker flag. +libgp_collector_la_LDFLAGS = $(AM_LDFLAGS) +libgp_collector_la_LIBADD = + +libgp_heap_la_SOURCES = heaptrace.c +libgp_heap_la_LDFLAGS = $(AM_LDFLAGS) + +libgp_sync_la_SOURCES = synctrace.c +libgp_sync_la_LDFLAGS = $(AM_LDFLAGS) + +libgp_iotrace_la_SOURCES = iotrace.c +libgp_iotrace_la_LDFLAGS = $(AM_LDFLAGS) + +libgp_collectorAPI_la_SOURCES = collectorAPI.c +libgp_collectorAPI_la_LIBADD = -lc -ldl + diff --git a/gprofng/libcollector/Makefile.in b/gprofng/libcollector/Makefile.in new file mode 100644 index 0000000..920c7a7 --- /dev/null +++ b/gprofng/libcollector/Makefile.in @@ -0,0 +1,1131 @@ +# Makefile.in generated by automake 1.15.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2017 Free Software Foundation, Inc. + +# This Makefile.in 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. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/../../config/depstand.m4 \ + $(top_srcdir)/../../config/lead-dot.m4 \ + $(top_srcdir)/../../config/override.m4 \ + $(top_srcdir)/../../libtool.m4 \ + $(top_srcdir)/../../ltoptions.m4 \ + $(top_srcdir)/../../ltsugar.m4 \ + $(top_srcdir)/../../ltversion.m4 \ + $(top_srcdir)/../../lt~obsolete.m4 \ + $(top_srcdir)/../../bfd/version.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(myinclude_HEADERS) $(am__DIST_COMMON) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(SHELL) $(top_srcdir)/../../mkinstalldirs +CONFIG_HEADER = lib-config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(myincludedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libgp_collector_la_DEPENDENCIES = +am__objects_1 = libgp_collector_la-gethrtime.lo \ + libgp_collector_la-dispatcher.lo libgp_collector_la-iolib.lo \ + libgp_collector_la-mmaptrace.lo libgp_collector_la-memmgr.lo \ + libgp_collector_la-tsd.lo libgp_collector_la-profile.lo \ + libgp_collector_la-envmgmt.lo libgp_collector_la-linetrace.lo \ + libgp_collector_la-libcol_hwcdrv.lo \ + libgp_collector_la-libcol_hwcfuncs.lo \ + libgp_collector_la-libcol-i386-dis.lo \ + libgp_collector_la-hwprofile.lo libgp_collector_la-jprofile.lo \ + libgp_collector_la-unwind.lo libgp_collector_la-libcol_util.lo \ + libgp_collector_la-collector.lo +am_libgp_collector_la_OBJECTS = $(am__objects_1) +libgp_collector_la_OBJECTS = $(am_libgp_collector_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgp_collector_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libgp_collector_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +libgp_collectorAPI_la_DEPENDENCIES = +am_libgp_collectorAPI_la_OBJECTS = collectorAPI.lo +libgp_collectorAPI_la_OBJECTS = $(am_libgp_collectorAPI_la_OBJECTS) +libgp_heap_la_LIBADD = +am_libgp_heap_la_OBJECTS = heaptrace.lo +libgp_heap_la_OBJECTS = $(am_libgp_heap_la_OBJECTS) +libgp_heap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libgp_heap_la_LDFLAGS) $(LDFLAGS) -o $@ +libgp_iotrace_la_LIBADD = +am_libgp_iotrace_la_OBJECTS = iotrace.lo +libgp_iotrace_la_OBJECTS = $(am_libgp_iotrace_la_OBJECTS) +libgp_iotrace_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libgp_iotrace_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +libgp_sync_la_LIBADD = +am_libgp_sync_la_OBJECTS = synctrace.lo +libgp_sync_la_OBJECTS = $(am_libgp_sync_la_OBJECTS) +libgp_sync_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libgp_sync_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/../../depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libgp_collector_la_SOURCES) \ + $(libgp_collectorAPI_la_SOURCES) $(libgp_heap_la_SOURCES) \ + $(libgp_iotrace_la_SOURCES) $(libgp_sync_la_SOURCES) +DIST_SOURCES = $(libgp_collector_la_SOURCES) \ + $(libgp_collectorAPI_la_SOURCES) $(libgp_heap_la_SOURCES) \ + $(libgp_iotrace_la_SOURCES) $(libgp_sync_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(myinclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +AM_RECURSIVE_TARGETS = cscope +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/../../ar-lib \ + $(top_srcdir)/../../compile $(top_srcdir)/../../config.guess \ + $(top_srcdir)/../../config.sub $(top_srcdir)/../../depcomp \ + $(top_srcdir)/../../install-sh $(top_srcdir)/../../ltmain.sh \ + $(top_srcdir)/../../missing $(top_srcdir)/../../mkinstalldirs \ + $(top_srcdir)/../common/config.h.in ../../COPYING \ + ../../COPYING.LIB ../../ChangeLog ../../README ../../ar-lib \ + ../../compile ../../config.guess ../../config.rpath \ + ../../config.sub ../../depcomp ../../install-sh \ + ../../ltmain.sh ../../missing ../../mkinstalldirs ../../ylwrap +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GPROFNG_VARIANT = @GPROFNG_VARIANT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I . -I ../.. +CSOURCES = \ + gethrtime.c \ + dispatcher.c \ + iolib.c \ + mmaptrace.c \ + memmgr.c \ + tsd.c \ + profile.c \ + envmgmt.c \ + linetrace.c \ + libcol_hwcdrv.c \ + libcol_hwcfuncs.c \ + libcol-i386-dis.c \ + hwprofile.c \ + jprofile.c \ + unwind.c \ + libcol_util.c \ + collector.c \ + $(NULL) + +AM_CFLAGS = $(GPROFNG_CFLAGS) -Wno-nonnull-compare +AM_CPPFLAGS = $(GPROFNG_CPPFLAGS) -I.. -I$(srcdir) \ + -I$(srcdir)/../common -I$(srcdir)/../src \ + -I$(srcdir)/../../include + +AM_LDFLAGS = -module -avoid-version \ + -Wl,--version-script,$(srcdir)/mapfile.$(GPROFNG_VARIANT) \ + $(LD_NO_AS_NEEDED) -Wl,-lrt -Wl,-ldl + +myincludedir = @includedir@ +myinclude_HEADERS = $(srcdir)/../../include/collectorAPI.h \ + $(srcdir)/../../include/libcollector.h \ + $(srcdir)/../../include/libfcollector.h + +lib_LTLIBRARIES = libgp-collector.la libgp-collectorAPI.la libgp-heap.la \ + libgp-sync.la libgp-iotrace.la + +libgp_collector_la_SOURCES = $(CSOURCES) +libgp_collector_la_CPPFLAGS = $(AM_CPPFLAGS) $(jdk_inc) \ + -I../../bfd -I$(srcdir)/../.. + +# Prevent libtool from reordering -Wl,--no-as-needed after -lrt by +# disguising -lrt as a linker flag. +libgp_collector_la_LDFLAGS = $(AM_LDFLAGS) +libgp_collector_la_LIBADD = +libgp_heap_la_SOURCES = heaptrace.c +libgp_heap_la_LDFLAGS = $(AM_LDFLAGS) +libgp_sync_la_SOURCES = synctrace.c +libgp_sync_la_LDFLAGS = $(AM_LDFLAGS) +libgp_iotrace_la_SOURCES = iotrace.c +libgp_iotrace_la_LDFLAGS = $(AM_LDFLAGS) +libgp_collectorAPI_la_SOURCES = collectorAPI.c +libgp_collectorAPI_la_LIBADD = -lc -ldl +all: lib-config.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +lib-config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(top_srcdir)/../common/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status lib-config.h +$(top_srcdir)/../common/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f lib-config.h stamp-h1 + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgp-collector.la: $(libgp_collector_la_OBJECTS) $(libgp_collector_la_DEPENDENCIES) $(EXTRA_libgp_collector_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgp_collector_la_LINK) -rpath $(libdir) $(libgp_collector_la_OBJECTS) $(libgp_collector_la_LIBADD) $(LIBS) + +libgp-collectorAPI.la: $(libgp_collectorAPI_la_OBJECTS) $(libgp_collectorAPI_la_DEPENDENCIES) $(EXTRA_libgp_collectorAPI_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) -rpath $(libdir) $(libgp_collectorAPI_la_OBJECTS) $(libgp_collectorAPI_la_LIBADD) $(LIBS) + +libgp-heap.la: $(libgp_heap_la_OBJECTS) $(libgp_heap_la_DEPENDENCIES) $(EXTRA_libgp_heap_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgp_heap_la_LINK) -rpath $(libdir) $(libgp_heap_la_OBJECTS) $(libgp_heap_la_LIBADD) $(LIBS) + +libgp-iotrace.la: $(libgp_iotrace_la_OBJECTS) $(libgp_iotrace_la_DEPENDENCIES) $(EXTRA_libgp_iotrace_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgp_iotrace_la_LINK) -rpath $(libdir) $(libgp_iotrace_la_OBJECTS) $(libgp_iotrace_la_LIBADD) $(LIBS) + +libgp-sync.la: $(libgp_sync_la_OBJECTS) $(libgp_sync_la_DEPENDENCIES) $(EXTRA_libgp_sync_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgp_sync_la_LINK) -rpath $(libdir) $(libgp_sync_la_OBJECTS) $(libgp_sync_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/collectorAPI.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heaptrace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iotrace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-collector.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-dispatcher.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-envmgmt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-gethrtime.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-hwprofile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-iolib.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-jprofile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-libcol-i386-dis.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-libcol_hwcdrv.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-libcol_hwcfuncs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-libcol_util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-linetrace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-memmgr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-mmaptrace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-profile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-tsd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgp_collector_la-unwind.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/synctrace.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libgp_collector_la-gethrtime.lo: gethrtime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-gethrtime.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-gethrtime.Tpo -c -o libgp_collector_la-gethrtime.lo `test -f 'gethrtime.c' || echo '$(srcdir)/'`gethrtime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-gethrtime.Tpo $(DEPDIR)/libgp_collector_la-gethrtime.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gethrtime.c' object='libgp_collector_la-gethrtime.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-gethrtime.lo `test -f 'gethrtime.c' || echo '$(srcdir)/'`gethrtime.c + +libgp_collector_la-dispatcher.lo: dispatcher.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-dispatcher.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-dispatcher.Tpo -c -o libgp_collector_la-dispatcher.lo `test -f 'dispatcher.c' || echo '$(srcdir)/'`dispatcher.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-dispatcher.Tpo $(DEPDIR)/libgp_collector_la-dispatcher.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dispatcher.c' object='libgp_collector_la-dispatcher.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-dispatcher.lo `test -f 'dispatcher.c' || echo '$(srcdir)/'`dispatcher.c + +libgp_collector_la-iolib.lo: iolib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-iolib.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-iolib.Tpo -c -o libgp_collector_la-iolib.lo `test -f 'iolib.c' || echo '$(srcdir)/'`iolib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-iolib.Tpo $(DEPDIR)/libgp_collector_la-iolib.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iolib.c' object='libgp_collector_la-iolib.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-iolib.lo `test -f 'iolib.c' || echo '$(srcdir)/'`iolib.c + +libgp_collector_la-mmaptrace.lo: mmaptrace.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-mmaptrace.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-mmaptrace.Tpo -c -o libgp_collector_la-mmaptrace.lo `test -f 'mmaptrace.c' || echo '$(srcdir)/'`mmaptrace.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-mmaptrace.Tpo $(DEPDIR)/libgp_collector_la-mmaptrace.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mmaptrace.c' object='libgp_collector_la-mmaptrace.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-mmaptrace.lo `test -f 'mmaptrace.c' || echo '$(srcdir)/'`mmaptrace.c + +libgp_collector_la-memmgr.lo: memmgr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-memmgr.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-memmgr.Tpo -c -o libgp_collector_la-memmgr.lo `test -f 'memmgr.c' || echo '$(srcdir)/'`memmgr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-memmgr.Tpo $(DEPDIR)/libgp_collector_la-memmgr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='memmgr.c' object='libgp_collector_la-memmgr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-memmgr.lo `test -f 'memmgr.c' || echo '$(srcdir)/'`memmgr.c + +libgp_collector_la-tsd.lo: tsd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-tsd.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-tsd.Tpo -c -o libgp_collector_la-tsd.lo `test -f 'tsd.c' || echo '$(srcdir)/'`tsd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-tsd.Tpo $(DEPDIR)/libgp_collector_la-tsd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tsd.c' object='libgp_collector_la-tsd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-tsd.lo `test -f 'tsd.c' || echo '$(srcdir)/'`tsd.c + +libgp_collector_la-profile.lo: profile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-profile.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-profile.Tpo -c -o libgp_collector_la-profile.lo `test -f 'profile.c' || echo '$(srcdir)/'`profile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-profile.Tpo $(DEPDIR)/libgp_collector_la-profile.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profile.c' object='libgp_collector_la-profile.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-profile.lo `test -f 'profile.c' || echo '$(srcdir)/'`profile.c + +libgp_collector_la-envmgmt.lo: envmgmt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-envmgmt.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-envmgmt.Tpo -c -o libgp_collector_la-envmgmt.lo `test -f 'envmgmt.c' || echo '$(srcdir)/'`envmgmt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-envmgmt.Tpo $(DEPDIR)/libgp_collector_la-envmgmt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='envmgmt.c' object='libgp_collector_la-envmgmt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-envmgmt.lo `test -f 'envmgmt.c' || echo '$(srcdir)/'`envmgmt.c + +libgp_collector_la-linetrace.lo: linetrace.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-linetrace.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-linetrace.Tpo -c -o libgp_collector_la-linetrace.lo `test -f 'linetrace.c' || echo '$(srcdir)/'`linetrace.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-linetrace.Tpo $(DEPDIR)/libgp_collector_la-linetrace.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='linetrace.c' object='libgp_collector_la-linetrace.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-linetrace.lo `test -f 'linetrace.c' || echo '$(srcdir)/'`linetrace.c + +libgp_collector_la-libcol_hwcdrv.lo: libcol_hwcdrv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-libcol_hwcdrv.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-libcol_hwcdrv.Tpo -c -o libgp_collector_la-libcol_hwcdrv.lo `test -f 'libcol_hwcdrv.c' || echo '$(srcdir)/'`libcol_hwcdrv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-libcol_hwcdrv.Tpo $(DEPDIR)/libgp_collector_la-libcol_hwcdrv.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcol_hwcdrv.c' object='libgp_collector_la-libcol_hwcdrv.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-libcol_hwcdrv.lo `test -f 'libcol_hwcdrv.c' || echo '$(srcdir)/'`libcol_hwcdrv.c + +libgp_collector_la-libcol_hwcfuncs.lo: libcol_hwcfuncs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-libcol_hwcfuncs.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-libcol_hwcfuncs.Tpo -c -o libgp_collector_la-libcol_hwcfuncs.lo `test -f 'libcol_hwcfuncs.c' || echo '$(srcdir)/'`libcol_hwcfuncs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-libcol_hwcfuncs.Tpo $(DEPDIR)/libgp_collector_la-libcol_hwcfuncs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcol_hwcfuncs.c' object='libgp_collector_la-libcol_hwcfuncs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-libcol_hwcfuncs.lo `test -f 'libcol_hwcfuncs.c' || echo '$(srcdir)/'`libcol_hwcfuncs.c + +libgp_collector_la-libcol-i386-dis.lo: libcol-i386-dis.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-libcol-i386-dis.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-libcol-i386-dis.Tpo -c -o libgp_collector_la-libcol-i386-dis.lo `test -f 'libcol-i386-dis.c' || echo '$(srcdir)/'`libcol-i386-dis.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-libcol-i386-dis.Tpo $(DEPDIR)/libgp_collector_la-libcol-i386-dis.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcol-i386-dis.c' object='libgp_collector_la-libcol-i386-dis.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-libcol-i386-dis.lo `test -f 'libcol-i386-dis.c' || echo '$(srcdir)/'`libcol-i386-dis.c + +libgp_collector_la-hwprofile.lo: hwprofile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-hwprofile.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-hwprofile.Tpo -c -o libgp_collector_la-hwprofile.lo `test -f 'hwprofile.c' || echo '$(srcdir)/'`hwprofile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-hwprofile.Tpo $(DEPDIR)/libgp_collector_la-hwprofile.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hwprofile.c' object='libgp_collector_la-hwprofile.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-hwprofile.lo `test -f 'hwprofile.c' || echo '$(srcdir)/'`hwprofile.c + +libgp_collector_la-jprofile.lo: jprofile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-jprofile.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-jprofile.Tpo -c -o libgp_collector_la-jprofile.lo `test -f 'jprofile.c' || echo '$(srcdir)/'`jprofile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-jprofile.Tpo $(DEPDIR)/libgp_collector_la-jprofile.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='jprofile.c' object='libgp_collector_la-jprofile.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-jprofile.lo `test -f 'jprofile.c' || echo '$(srcdir)/'`jprofile.c + +libgp_collector_la-unwind.lo: unwind.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-unwind.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-unwind.Tpo -c -o libgp_collector_la-unwind.lo `test -f 'unwind.c' || echo '$(srcdir)/'`unwind.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-unwind.Tpo $(DEPDIR)/libgp_collector_la-unwind.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unwind.c' object='libgp_collector_la-unwind.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-unwind.lo `test -f 'unwind.c' || echo '$(srcdir)/'`unwind.c + +libgp_collector_la-libcol_util.lo: libcol_util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-libcol_util.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-libcol_util.Tpo -c -o libgp_collector_la-libcol_util.lo `test -f 'libcol_util.c' || echo '$(srcdir)/'`libcol_util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-libcol_util.Tpo $(DEPDIR)/libgp_collector_la-libcol_util.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcol_util.c' object='libgp_collector_la-libcol_util.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-libcol_util.lo `test -f 'libcol_util.c' || echo '$(srcdir)/'`libcol_util.c + +libgp_collector_la-collector.lo: collector.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgp_collector_la-collector.lo -MD -MP -MF $(DEPDIR)/libgp_collector_la-collector.Tpo -c -o libgp_collector_la-collector.lo `test -f 'collector.c' || echo '$(srcdir)/'`collector.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgp_collector_la-collector.Tpo $(DEPDIR)/libgp_collector_la-collector.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='collector.c' object='libgp_collector_la-collector.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgp_collector_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgp_collector_la-collector.lo `test -f 'collector.c' || echo '$(srcdir)/'`collector.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool config.lt +install-myincludeHEADERS: $(myinclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(myinclude_HEADERS)'; test -n "$(myincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(myincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(myincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(myincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(myincludedir)" || exit $$?; \ + done + +uninstall-myincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(myinclude_HEADERS)'; test -n "$(myincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(myincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) lib-config.h +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(myincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-myincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES uninstall-myincludeHEADERS + +.MAKE: all install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-am clean \ + clean-cscope clean-generic clean-libLTLIBRARIES clean-libtool \ + cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ + dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ + distcheck distclean distclean-compile distclean-generic \ + distclean-hdr distclean-libtool distclean-tags distcleancheck \ + distdir distuninstallcheck dvi dvi-am html html-am info \ + info-am install install-am install-data install-data-am \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-myincludeHEADERS \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-libLTLIBRARIES uninstall-myincludeHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gprofng/libcollector/aclocal.m4 b/gprofng/libcollector/aclocal.m4 new file mode 100644 index 0000000..b269c73 --- /dev/null +++ b/gprofng/libcollector/aclocal.m4 @@ -0,0 +1,1237 @@ +# generated automatically by aclocal 1.15.1 -*- Autoconf -*- + +# Copyright (C) 1996-2017 Free Software Foundation, Inc. + +# 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. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# Copyright (C) 2002-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.15' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.15.1], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.15.1])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# Copyright (C) 2011-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_PROG_AR([ACT-IF-FAIL]) +# ------------------------- +# Try to determine the archiver interface, and trigger the ar-lib wrapper +# if it is needed. If the detection of archiver interface fails, run +# ACT-IF-FAIL (default is to abort configure with a proper error message). +AC_DEFUN([AM_PROG_AR], +[AC_BEFORE([$0], [LT_INIT])dnl +AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl +AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([ar-lib])dnl +AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false]) +: ${AR=ar} + +AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface], + [AC_LANG_PUSH([C]) + am_cv_ar_interface=ar + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])], + [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([am_ar_try]) + if test "$ac_status" -eq 0; then + am_cv_ar_interface=ar + else + am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([am_ar_try]) + if test "$ac_status" -eq 0; then + am_cv_ar_interface=lib + else + am_cv_ar_interface=unknown + fi + fi + rm -f conftest.lib libconftest.a + ]) + AC_LANG_POP([C])]) + +case $am_cv_ar_interface in +ar) + ;; +lib) + # Microsoft lib, so override with the ar-lib wrapper script. + # FIXME: It is wrong to rewrite AR. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__AR in this case, + # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something + # similar. + AR="$am_aux_dir/ar-lib $AR" + ;; +unknown) + m4_default([$1], + [AC_MSG_ERROR([could not determine $AR interface])]) + ;; +esac +AC_SUBST([AR])dnl +]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# +# 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. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# +# 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. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# +# 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. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542> + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: <http://www.gnu.org/software/coreutils/>. + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_MAINTAINER_MODE([DEFAULT-MODE]) +# ---------------------------------- +# Control maintainer-specific portions of Makefiles. +# Default is to disable them, unless 'enable' is passed literally. +# For symmetry, 'disable' may be passed as well. Anyway, the user +# can override the default with the --enable/--disable switch. +AC_DEFUN([AM_MAINTAINER_MODE], +[m4_case(m4_default([$1], [disable]), + [enable], [m4_define([am_maintainer_other], [disable])], + [disable], [m4_define([am_maintainer_other], [enable])], + [m4_define([am_maintainer_other], [enable]) + m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) +AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode's default is 'disable' unless 'enable' is passed + AC_ARG_ENABLE([maintainer-mode], + [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], + am_maintainer_other[ make rules and dependencies not useful + (and sometimes confusing) to the casual installer])], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST([MAINT])dnl +] +) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# +# 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. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# +# 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. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2017 Free Software Foundation, Inc. +# +# 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. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2017 Free Software Foundation, Inc. +# +# 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. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar <conftest.tar]) + AM_RUN_LOG([cat conftest.dir/file]) + grep GrepMe conftest.dir/file >/dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([../../config/depstand.m4]) +m4_include([../../config/lead-dot.m4]) +m4_include([../../config/override.m4]) +m4_include([../../libtool.m4]) +m4_include([../../ltoptions.m4]) +m4_include([../../ltsugar.m4]) +m4_include([../../ltversion.m4]) +m4_include([../../lt~obsolete.m4]) diff --git a/gprofng/libcollector/collector.c b/gprofng/libcollector/collector.c new file mode 100644 index 0000000..93c9d33 --- /dev/null +++ b/gprofng/libcollector/collector.c @@ -0,0 +1,2494 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <alloca.h> +#include <errno.h> +#include <signal.h> +#include <ucontext.h> +#include <stdlib.h> /* exit() */ +#include <sys/param.h> +#include <sys/utsname.h> /* struct utsname */ +#include <sys/resource.h> +#include <sys/syscall.h> /* system call fork() */ + +#include "gp-defs.h" +#include "collector.h" +#include "descendants.h" +#include "gp-experiment.h" +#include "memmgr.h" +#include "cc_libcollector.h" +#include "tsd.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +typedef unsigned long ulong_t; + +extern char **environ; +extern void __collector_close_experiment (); +extern int __collector_set_size_limit (char *par); + +/* ------- internal function prototypes ---------- */ +CollectorModule __collector_register_module (ModuleInterface *modint); +static void write_sample (char *name); +static const char *__collector_get_params (); +static const char *__collector_get_expdir (); +static FrameInfo __collector_getUserCtx (CollectorModule modl, HiResTime ts, int mode, void *arg); +static FrameInfo __collector_getUID1 (CM_Array *arg); +static int __collector_writeMetaData (CollectorModule modl, char *format, ...); +static int __collector_writeDataRecord (CollectorModule modl, struct Common_packet *pckt); +static int __collector_writeDataPacket (CollectorModule modl, struct CM_Packet *pckt); +static void *allocCSize (struct Heap*, unsigned, int); +static void freeCSize (struct Heap*, void*, unsigned); +static void *allocVSize (struct Heap*, unsigned); +static void *reallocVSize (struct Heap*, void*, unsigned); + +static int collector_create_expr_dir (const char *new_exp_name); +static int collector_create_expr_dir_lineage (const char *parent_exp_name); +static int collector_exp_dir_append_x (int linenum, const char *parent_exp_name); +static int collector_tail_init (const char *parent_exp_name); +static int log_open (); +static void log_header_write (sp_origin_t origin); +static void log_pause (); +static void log_resume (); +static void fs_warn (); +static void log_close (); +static void get_progspec (char *cmdline, int tmp_sz, char *progname, int sz); +static void sample_handler (int, siginfo_t*, void*); +static int sample_set_interval (char *); +static int set_duration (char *); +static int sample_set_user_sig (char *); +static void pause_handler (int, siginfo_t*, void*); +static int pause_set_user_sig (char *); +static int set_user_sig_action (char*); +static void ovw_open (); +static hrtime_t ovw_write (); + +/* ------- global data controlling the collector's behavior -------- */ + +static CollectorInterface collector_interface ={ + __collector_register_module, /* registerModule */ + __collector_get_params, /* getParams */ + __collector_get_expdir, /* getExpDir */ + __collector_log_write, /* writeLog */ + __collector_getUserCtx, /* getFrameInfo */ + __collector_getUID1, /* getUID */ + __collector_getUID, /* getUID2 */ + __collector_getStackTrace, /* getStackTrace */ + __collector_writeMetaData, /* writeMetaData */ + __collector_writeDataRecord, /* writeDataRecord */ + __collector_writeDataPacket, /* writeDataPacket */ + write_sample, /* write_sample */ + get_progspec, /* get_progspec */ + __collector_open_experiment, /* open_experiment */ + NULL, /* getHiResTime */ + __collector_newHeap, /* newHeap */ + __collector_deleteHeap, /* deleteHeap */ + allocCSize, /* allocCSize */ + freeCSize, /* freeCSize */ + allocVSize, /* allocVSize */ + reallocVSize, /* reallocVSize */ + __collector_tsd_create_key, /* createKey */ + __collector_tsd_get_by_key, /* getKey */ + __collector_dlog /* writeDebugInfo */ +}; + +#define MAX_MODULES 32 +static ModuleInterface *modules[MAX_MODULES]; +static int modules_st[MAX_MODULES]; +static void *modules_hndl[MAX_MODULES]; +static volatile int nmodules = 0; + +/* flag set non-zero, if data collected implies a filesystem warning is appropriate */ +static int fs_matters = 0; +static const char *collector_params = NULL; +static const char *project_home = NULL; +Heap *__collector_heap = NULL; +int __collector_no_threads; +int __collector_libthread_T1 = -1; + +static volatile int collector_paused = 0; + +int __collector_tracelevel = -1; +static int collector_debug_opt = 0; + +hrtime_t __collector_next_sample = 0; +int __collector_sample_period = 0; /* if non-zero, periodic sampling is enabled */ + +hrtime_t __collector_delay_start = 0; /* if non-zero, delay before starting data */ +hrtime_t __collector_terminate_time = 0; /* if non-zero, fixed duration run */ + +static collector_mutex_t __collector_glob_lock = COLLECTOR_MUTEX_INITIALIZER; +static collector_mutex_t __collector_open_guard = COLLECTOR_MUTEX_INITIALIZER; +static collector_mutex_t __collector_close_guard = COLLECTOR_MUTEX_INITIALIZER; +static collector_mutex_t __collector_sample_guard = COLLECTOR_MUTEX_INITIALIZER; +static collector_mutex_t __collector_suspend_guard = COLLECTOR_MUTEX_INITIALIZER; +static collector_mutex_t __collector_resume_guard = COLLECTOR_MUTEX_INITIALIZER; +char __collector_exp_dir_name[MAXPATHLEN + 1] = ""; /* experiment directory */ +int __collector_size_limit = 0; + +static char *archive_mode = NULL; + +volatile sp_state_t __collector_expstate = EXP_INIT; +static int exp_origin = SP_ORIGIN_LIBCOL_INIT; +static int exp_open = 0; +int __collector_exp_active = 0; +static int paused_when_suspended = 0; +static int exp_initted = 0; +static char exp_progspec[_POSIX_ARG_MAX + 1]; /* program cmdline. includes args */ +static char exp_progname[_POSIX_ARG_MAX + 1]; /* program name == argv[0] */ + +hrtime_t __collector_start_time = 0; +static time_t start_sec_time = 0; + +/* Sample related data */ +static int sample_installed = 0; /* 1 if the sample signal handler installed */ +static int sample_mode = 0; /* dynamically turns sample record writing on/off */ +static int sample_number = 0; /* index of the current sample record */ +static struct sigaction old_sample_handler; +int __collector_sample_sig = -1; /* user-specified sample signal */ +int __collector_sample_sig_warn = 0; /* non-zero if warning already given */ + +/* Pause/resume related data */ +static struct sigaction old_pause_handler; +int __collector_pause_sig = -1; /* user-specified pause signal */ +int __collector_pause_sig_warn = 0; /* non-zero if warning already given */ + +static struct sigaction old_close_handler; +static struct sigaction old_exit_handler; + +/* Experiment files */ +static char ovw_name[MAXPATHLEN]; /* Overview data file name */ + +/* macro to convert a timestruc to hrtime_t */ +#define ts2hrt(x) ((hrtime_t)(x).tv_sec*NANOSEC + (hrtime_t)(x).tv_nsec) + +static void +init_tracelevel () +{ +#if DEBUG + char *s = CALL_UTIL (getenv)("SP_COLLECTOR_TRACELEVEL"); + if (s != NULL) + __collector_tracelevel = CALL_UTIL (atoi)(s); + TprintfT (DBG_LT0, "collector: SP_COLLECTOR_TRACELEVEL=%d\n", __collector_tracelevel); + s = CALL_UTIL (getenv)("SP_COLLECTOR_DEBUG"); + if (s != NULL) + collector_debug_opt = CALL_UTIL (atoi)(s) & ~(SP_DUMP_TIME | SP_DUMP_FLAG); +#endif +} + +static CollectorInterface * +get_collector_interface () +{ + if (collector_interface.getHiResTime == NULL) + collector_interface.getHiResTime = __collector_gethrtime; + return &collector_interface; +} + +/* + * __collector_module_init is an alternate method to initialize + * dynamic collector modules (er_heap, er_sync, er_iotrace, er_mpi, tha). + * Every module that needs to register itself with libcollector + * before the experiment is open implements its own global + * __collector_module_init and makes sure the next one is called. + */ +static void +collector_module_init (CollectorInterface *col_intf) +{ + int nmodules = 0; + + ModuleInitFunc next_init = (ModuleInitFunc) dlsym (RTLD_DEFAULT, "__collector_module_init"); + if (next_init != NULL) + { + nmodules++; + next_init (col_intf); + } + TprintfT (DBG_LT1, "collector_module_init: %d modules\n", nmodules); +} + +/* Routines concerned with general experiment start and stop */ + +/* initialization -- init section routine -- called when libcollector loaded */ +static void collector_init () __attribute__ ((constructor)); + +static void +collector_init () +{ + if (__collector_util_init () != 0) + /* we can't do anything without various utility functions */ + abort (); + init_tracelevel (); + + /* + * Unconditionally install the SIGPROF handler + * to process signals originated in dtracelets. + */ + __collector_sigprof_install (); + + /* Initialize all preloaded modules */ + collector_module_init (get_collector_interface ()); + + /* determine experiment name */ + char *exp = CALL_UTIL (getenv)("SP_COLLECTOR_EXPNAME"); + if ((exp == NULL) || (CALL_UTIL (strlen)(exp) == 0)) + { + TprintfT (DBG_LT0, "collector_init: SP_COLLECTOR_EXPNAME undefined - no experiment to start\n"); + /* not set -- no experiment to run */ + return; + } + else + TprintfT (DBG_LT1, "collector_init: found SP_COLLECTOR_EXPNAME = %s\n", exp); + + /* determine the data descriptor for the experiment */ + char *params = CALL_UTIL (getenv)("SP_COLLECTOR_PARAMS"); + if (params == NULL) + { + TprintfT (0, "collector_init: SP_COLLECTOR_PARAMS undefined - no experiment to start\n"); + return; + } + + /* now do the real open of the experiment */ + if (__collector_open_experiment (exp, params, SP_ORIGIN_LIBCOL_INIT)) + { + TprintfT (0, "collector_init: __collector_open_experiment failed\n"); + /* experiment open failed, close it */ + __collector_close_experiment (); + return; + } + return; +} + +CollectorModule +__collector_register_module (ModuleInterface *modint) +{ + TprintfT (DBG_LT1, "collector: module %s calls for registration.\n", + modint->description == NULL ? "(null)" : modint->description); + if (modint == NULL) + return COLLECTOR_MODULE_ERR; + if (nmodules >= MAX_MODULES) + return COLLECTOR_MODULE_ERR; + if (modint->initInterface && + modint->initInterface (get_collector_interface ())) + return COLLECTOR_MODULE_ERR; + int idx = nmodules++; + modules[idx] = modint; + modules_st[idx] = 0; + + if (exp_open && modint->openExperiment) + { + modules_st[idx] = modint->openExperiment (__collector_exp_dir_name); + if (modules_st[idx] == COL_ERROR_NONE && modules[idx]->description != NULL) + { + modules_hndl[idx] = __collector_create_handle (modules[idx]->description); + if (modules_hndl[idx] == NULL) + modules_st[idx] = -1; + } + } + if (__collector_exp_active && collector_paused == 0 && + modint->startDataCollection && modules_st[idx] == 0) + modint->startDataCollection (); + TprintfT (DBG_LT1, "collector: module %s (%d) registered.\n", + modint->description == NULL ? "(null)" : modint->description, idx); + return (CollectorModule) idx; +} + +static const char * +__collector_get_params () +{ + return collector_params; +} + +static const char * +__collector_get_expdir () +{ + return __collector_exp_dir_name; +} + +static FrameInfo +__collector_getUserCtx (CollectorModule modl, HiResTime ts, int mode, void *arg) +{ + return __collector_get_frame_info (ts, mode, arg); +} + +static FrameInfo +__collector_getUID1 (CM_Array *arg) +{ + return __collector_getUID (arg, (FrameInfo) 0); +} + +static int +__collector_writeMetaData (CollectorModule modl, char *format, ...) +{ + if (modl < 0 || modl >= nmodules || modules[modl]->description == NULL) + { + TprintfT (DBG_LT0, "__collector_writeMetaData(): bad module: %d\n", modl); + return 1; + } + char fname[MAXPATHLEN + 1]; + CALL_UTIL (strlcpy)(fname, __collector_exp_dir_name, sizeof (fname)); + CALL_UTIL (strlcat)(fname, "/metadata.", sizeof (fname)); + CALL_UTIL (strlcat)(fname, modules[modl]->description, sizeof (fname)); + CALL_UTIL (strlcat)(fname, ".xml", sizeof (fname)); + int fd = CALL_UTIL (open)(fname, O_CREAT | O_WRONLY | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) + { + TprintfT (DBG_LT0, "__collector_writeMetaData(): can't open file: %s\n", fname); + return 1; + } + char buf[1024]; + char *bufptr = buf; + va_list va; + va_start (va, format); + int sz = __collector_xml_vsnprintf (bufptr, sizeof (buf), format, va); + va_end (va); + + if (sz >= sizeof (buf)) + { + /* Allocate a new buffer */ + sz += 1; /* add the terminating null byte */ + bufptr = (char*) alloca (sz); + + va_start (va, format); + sz = __collector_xml_vsnprintf (bufptr, sz, format, va); + va_end (va); + } + CALL_UTIL (write)(fd, bufptr, sz); + CALL_UTIL (close)(fd); + return COL_ERROR_NONE; +} + +/* check that the header fields are filled-in, and then call __collector_writeDataPacket */ +static int +__collector_writeDataRecord (CollectorModule modl, struct Common_packet *pckt) +{ + return __collector_write_record (modules_hndl[modl], pckt); +} + +static int +__collector_writeDataPacket (CollectorModule modl, struct CM_Packet *pckt) +{ + return __collector_write_packet (modules_hndl[modl], pckt); +} + +static void * +allocCSize (struct Heap *heap, unsigned sz, int log) +{ + return __collector_allocCSize (heap ? heap : __collector_heap, sz, log); +} + +static void +freeCSize (struct Heap *heap, void *ptr, unsigned sz) +{ + __collector_freeCSize (heap ? heap : __collector_heap, ptr, sz); +} + +static void * +allocVSize (struct Heap *heap, unsigned sz) +{ + return __collector_allocVSize (heap ? heap : __collector_heap, sz); +} + +static void * +reallocVSize (struct Heap *heap, void *ptr, unsigned sz) +{ + return __collector_reallocVSize (heap ? heap : __collector_heap, ptr, sz); +} + +static time_t +get_gm_time (struct tm *tp) +{ + /* + Note that glibc contains a function of the same purpose named `timegm'. + But obviously, it is not universally available. + + Some implementations of mktime return -1 for the nonexistent localtime hour + at the beginning of DST. In this event, use 'mktime(tm - 1hr) + 3600'. + nonexistent + tm_isdst is set to 0 to force mktime to introduce a consistent offset + (the non DST offset) since tm and tm+o might be on opposite sides of a DST change. + + Schematically: + mktime(tm) --> t+o + gmtime_r(t+o) --> tm+o + mktime(tm+o) --> t+2o + t = t+o - (t+2o - t+o) + */ + struct tm stm; + time_t tl = CALL_UTIL (mktime)(tp); + if (tl == -1) + { + stm = *tp; + stm.tm_hour--; + tl = CALL_UTIL (mktime)(&stm); + if (tl == -1) + return -1; + tl += 3600; + } + + (void) (CALL_UTIL (gmtime_r)(&tl, &stm)); + stm.tm_isdst = 0; + time_t tb = CALL_UTIL (mktime)(&stm); + if (tb == -1) + { + stm.tm_hour--; + tb = CALL_UTIL (mktime)(&stm); + if (tb == -1) + return -1; + tb += 3600; + } + return (tl - (tb - tl)); +} + +static void +log_write_event_run () +{ + /* get the gm and local time */ + struct tm start_stm; + CALL_UTIL (gmtime_r)(&start_sec_time, &start_stm); + time_t start_gm_time = get_gm_time (&start_stm); + time_t lcl_time = CALL_UTIL (mktime)(&start_stm); + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" time=\"%lld\" tm_zone=\"%lld\"/>\n", + SP_JCMD_RUN, + (unsigned) (__collector_start_time / NANOSEC), + (unsigned) (__collector_start_time % NANOSEC), + (long long) start_gm_time, + (long long) (lcl_time - start_gm_time)); +} + +static void * +m_dlopen (const char *filename, int flag) +{ + void *p = dlopen (filename, flag); + TprintfT (DBG_LT1, "collector.c: dlopen(%s, %d) returns %p\n", filename, flag, p); + return p; +} +/* real routine to open an experiment + * called by collector_init from libcollector init section + * called by __collector_start_experiment when a child is forked */ +int +__collector_open_experiment (const char *exp, const char *params, sp_origin_t origin) +{ + char *s; + char *buf = NULL; + char *duration_string = NULL; + int err; + int is_founder = 1; + int record_this_experiment = 1; + int seen_F_flag = 0; + static char buffer[32]; + if (exp_open) + { + /* experiment already opened */ + TprintfT (0, "collector: ERROR: Attempt to open opened experiment\n"); + return COL_ERROR_EXPOPEN; + } + __collector_start_time = collector_interface.getHiResTime (); + TprintfT (DBG_LT1, "\n\t\t__collector_open_experiment(SP_COLLECTOR_EXPNAME=%s, params=%s, origin=%d); setting start_time\n", + exp, params, origin); + if (environ) + __collector_env_printall ("__collector_open_experiment", environ); + else + TprintfT (DBG_LT1, "collector_open_experiment found environ == NULL)\n"); + + /* + * Recheck sigprof handler + * XXXX Bug 18177509 - additional sigprof signal kills target program + */ + __collector_sigprof_install (); + exp_origin = origin; + collector_params = params; + + /* Determine which of the three possible threading models: + * singlethreaded + * multi-LWP (no threads) + * multithreaded + * is the one the target is actually using. + * + * we really only need to distinguish between first two + * and the third. The thr_main() trick does exactly that. + * is the one the target is actually using. + * + * __collector_no_threads applies to all signal handlers, + * and must be set before signal handlers are installed. + */ + __collector_no_threads = 0; + __collector_exp_dir_name[0] = 0; + sample_mode = 0; + sample_number = 0; + + /* create global heap */ + if (__collector_heap == NULL) + { + __collector_heap = __collector_newHeap (); + if (__collector_heap == NULL) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment COLERROR_NOZMEM 1\n"); + return COL_ERROR_NOZMEM; + } + } + //check whether is origin is collect + char * envar = CALL_UTIL (getenv)("SP_COLLECTOR_ORIGIN_COLLECT"); + TprintfT (DBG_LT1, "__collector_open_experiment SP_COLLECTOR_ORIGIN_COLLECT = '%s'\n", + (envar == NULL) ? "NULL" : envar); + if (envar) + exp_origin = SP_ORIGIN_COLLECT; + + //check if this is the founder process + is_founder = getpid (); + if (origin != SP_ORIGIN_DBX_ATTACH) + { + envar = CALL_UTIL (getenv)("SP_COLLECTOR_FOUNDER"); + if (envar) + is_founder = CALL_UTIL (atoi)(envar); + if (is_founder != 0) + { + if (is_founder != getpid ()) + { + TprintfT (0, "__collector_open_experiment SP_COLLECTOR_FOUNDER=%d != pid(%d)\n", + is_founder, getpid ()); + //CALL_UTIL(fprintf)(stderr, "__collector_open_experiment SP_COLLECTOR_FOUNDER=%d != pid(%d); not recording experiment\n", + //is_founder, getpid() ); + //return COL_ERROR_UNEXP_FOUNDER; + is_founder = 0; // Special case (CR 22917352) + } + /* clear FOUNDER for descendant experiments */ + TprintfT (0, "__collector_open_experiment setting SP_COLLECTOR_FOUNDER=0\n"); + CALL_UTIL (strlcpy)(buffer, "SP_COLLECTOR_FOUNDER=0", sizeof (buffer)); + CALL_UTIL (putenv)(buffer); + } + } + + /* Set up fork/exec interposition (requires __collector_heap). */ + /* Determine if "collect -F" specification enables this subexperiment */ + get_progspec (exp_progspec, sizeof (exp_progspec), exp_progname, sizeof (exp_progname)); + + /* convert the returned exp_progname to a basename */ + const char * base_name = __collector_strrchr (exp_progname, '/'); + if (base_name == NULL) + base_name = exp_progname; + else + base_name = base_name + 1; + err = __collector_ext_line_init (&record_this_experiment, exp_progspec, base_name); + if (err != COL_ERROR_NONE) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment COLERROR: %d\n", err); + return err; + } + + /* Due to the fix of bug 15691122, we need to initialize unwind to make + * the function __collector_ext_return_address() work for dlopen interposition. + * */ + if (!record_this_experiment && !is_founder) + { + TprintfT (DBG_LT0, "__collector_open_experiment: NOT creating experiment. (is_founder=%d, record=%d)\n", + is_founder, record_this_experiment); + return collector_tail_init (exp); + } + TprintfT (DBG_LT0, "__collector_open_experiment: is_founder=%d, record=%d\n", + is_founder, record_this_experiment); + if (is_founder || origin == SP_ORIGIN_FORK) + { + CALL_UTIL (strlcpy)(__collector_exp_dir_name, exp, sizeof (__collector_exp_dir_name)); + if (origin == SP_ORIGIN_FORK) + { /*create exp dir for fork-child*/ + if (collector_create_expr_dir (__collector_exp_dir_name)) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_BADDIR 1: `%s'\n", exp); + return COL_ERROR_BADDIR; + } + } + } + else + {/* founder/fork-child will already have created experiment dir, but exec/combo descendants must do so now */ + if (collector_create_expr_dir_lineage (exp)) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_BADDIR 2: `%s'\n", exp); + return COL_ERROR_BADDIR; + } + static char exp_name_env[MAXPATHLEN + 1]; + TprintfT (DBG_LT1, "collector_open_experiment: setting SP_COLLECTOR_EXPNAME to %s\n", __collector_exp_dir_name); + CALL_UTIL (snprintf)(exp_name_env, sizeof (exp_name_env), "SP_COLLECTOR_EXPNAME=%s", __collector_exp_dir_name); + CALL_UTIL (putenv)(exp_name_env); + } + /* Check that the name is that of a directory (new structure) */ + DIR *expDir = CALL_UTIL (opendir)(__collector_exp_dir_name); + if (expDir == NULL) + { + /* can't open it */ + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_BADDIR 3: `%s'\n", exp); + return COL_ERROR_BADDIR; + } + CALL_UTIL (closedir)(expDir); + + if (CALL_UTIL (access)(__collector_exp_dir_name, W_OK)) + { + TprintfT (0, "collector: ERROR: access error: errno=%d\n", errno); + if ((errno == EACCES) || (errno == EROFS)) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_DIRPERM: `%s'\n", exp); + TprintfT (DBG_LT0, "collector: ERROR: experiment directory `%s' is not writeable\n", + __collector_exp_dir_name); + return COL_ERROR_DIRPERM; + } + else + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_BADDIR 4: `%s'\n", exp); + return COL_ERROR_BADDIR; + } + } + + /* reset the paused flag */ + collector_paused = (origin == SP_ORIGIN_FORK ? paused_when_suspended : 0); + + /* mark the experiment as opened */ + __collector_expstate = EXP_OPEN; + TprintfT (DBG_LT1, "collector: __collector_expstate->EXP_OPEN\n"); + + /* open the log file */ + err = log_open (); + if (err != COL_ERROR_NONE) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_LOG_OPEN\n"); + return COL_ERROR_LOG_OPEN; + } + if (origin != SP_ORIGIN_GENEXP && origin != SP_ORIGIN_KERNEL) + log_header_write (origin); + + /* Make a copy of params so that we can modify the string */ + int paramsz = CALL_UTIL (strlen)(params) + 1; + buf = (char*) alloca (paramsz); + if (buf == NULL) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_ARGS2BIG: %s\n", params); + TprintfT (DBG_LT0, "collector: ERROR: experiment parameter `%s' is too long\n", params); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\"/></event>\n", + SP_JCMD_CERROR, COL_ERROR_ARGS2BIG); + return COL_ERROR_ARGS2BIG; + } + CALL_UTIL (strlcpy)(buf, params, paramsz); + + /* create directory for archives (if founder) */ + char archives[MAXPATHLEN]; + CALL_UTIL (snprintf)(archives, MAXPATHLEN, "%s/%s", __collector_exp_dir_name, + SP_ARCHIVES_DIR); + if (is_founder) + { + mode_t dmode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + if ((CALL_UTIL (mkdir)(archives, dmode) != 0) && (errno != EEXIST)) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_MKDIR: %s: errno = %d\n", archives, errno); + TprintfT (0, "collector: ERROR: mkdir(%s) failed: errno = %d\n", archives, errno); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">mkdir(%s): errno=%d</event>\n", + SP_JCMD_COMMENT, COL_COMMENT_NONE, archives, errno); + /* this is not a fatal error currently */ + } + else + TprintfT (DBG_LT1, "collector: archive mkdir(%s) succeeded\n", archives); + } + + /* initialize the segments map and mmap interposition */ + if (origin != SP_ORIGIN_GENEXP && origin != SP_ORIGIN_KERNEL) + { + if ((err = __collector_ext_mmap_install (1)) != COL_ERROR_NONE) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\"/></event>\n", SP_JCMD_CERROR, err); + return err; + } + } + + /* open the overview file for sample data */ + if (origin != SP_ORIGIN_GENEXP) + ovw_open (); + + /* initialize TSD module (note: relies on __collector_heap) */ + if (__collector_tsd_init () != 0) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_TSD_INIT\n"); + __collector_log_write ("<event kind=\"%s\" id=\"%d\">TSD could not be initialized</event>\n", SP_JCMD_CERROR, COL_ERROR_TSD_INIT); + return COL_ERROR_TSD_INIT; + } + + /* experiment is initialized; allow pause/resume/close */ + exp_initted = 1; + + // 24935305 should not use SIGPROF if collect -p -t and -S are all off + /* (check here if -t or -S is on; -p is checked later) */ + if (((params[0] == 't' || params[0] == 'S') && params[1] == ':') + || CALL_UTIL (strstr)(params, ";t:") + || CALL_UTIL (strstr)(params, ";S:")) + { + /* set a default time to 100 ms.; use negative value to force setting */ + TprintfT (DBG_LT1, "collector: open_experiment setting timer to 100000\n"); + __collector_ext_itimer_set (-100000); + } + + /* call open for all dynamic modules */ + int i; + for (i = 0; i < nmodules; i++) + { + if (modules[i]->openExperiment != NULL) + { + modules_st[i] = modules[i]->openExperiment (__collector_exp_dir_name); + if (modules_st[i] == COL_ERROR_NONE && modules[i]->description != NULL) + { + modules_hndl[i] = __collector_create_handle (modules[i]->description); + if (modules_hndl[i] == NULL) + modules_st[i] = -1; + } + } + /* check to see if anyone closed the experiment */ + if (!exp_initted) + { + CALL_UTIL (fprintf)(stderr, "__collector_open_experiment: COL_ERROR_EXP_OPEN\n"); + __collector_log_write ("<event kind=\"%s\" id=\"%d\">Experiment closed prematurely</event>\n", SP_JCMD_CERROR, COL_ERROR_EXPOPEN); + return COL_ERROR_EXPOPEN; + } + } + + /* initialize for subsequent stack unwinds */ + __collector_ext_unwind_init (1); + TprintfT (DBG_LT0, "__collector_open_experiment(); module init done, params=%s\n", + buf); + + /* now parse the data descriptor */ + /* The parameter string is a series of specifiers, + * each of which is of the form: + * <key>:<param>; + * key is a single letter, the : and ; are mandatory, + * and param is a string which may be zero-length, and + * which contains any character except a null-byte or ; + * param is interpreted by the handler for the particular key + */ + + s = buf; + + while (*s) + { + char *par; + char key = *s++; + /* ensure that it's followed by a colon */ + if (*s++ != ':') + { + TprintfT (0, "collector: ERROR: parameter %c is not followed by a colon\n", key); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, COL_ERROR_ARGS, params); + return COL_ERROR_ARGS; + } + /* find the semicolon terminator */ + par = s; + while (*s && (*s != ';')) + s++; + if (*s != ';') + { + /* not followed by semicolon */ + TprintfT (0, "collector: ERROR: parameter %c:%s is not terminated by a semicolon\n", key, par); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, COL_ERROR_ARGS, params); + return COL_ERROR_ARGS; + } + /* terminate par, and position for next descriptor */ + *s++ = 0; + + /* now process that element of the data descriptor */ + switch (key) + { + case 'g': /* g<sig>; */ + if ((err = sample_set_user_sig (par)) != COL_ERROR_NONE) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, err, par); + return err; + } + break; + case 'd': /* d<sig>; -or- d<sig>p; */ + if ((err = pause_set_user_sig (par)) != COL_ERROR_NONE) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, err, par); + return err; + } + break; + case 'H': + m_dlopen ("libgp-heap.so", RTLD_LAZY); /* hack to force .so's constructor to be called (?) */ + break; + case 's': + m_dlopen ("libgp-sync.so", RTLD_LAZY); /* hack to force .so's constructor to be called (?) */ + break; + case 'i': + m_dlopen ("libgp-iotrace.so", RTLD_LAZY); /* hack to force .so's constructor to be called (?) */ + break; + case 'F': /* F; */ + seen_F_flag = 1; + TprintfT (DBG_LT0, "__collector_open_experiment: calling __collector_ext_line_install (%s, %s)\n", + par, __collector_exp_dir_name); + if ((err = __collector_ext_line_install (par, __collector_exp_dir_name)) != COL_ERROR_NONE) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, err, par); + return err; + } + break; + case 'a': /* a; */ + archive_mode = __collector_strdup (par); + break; + case 't': /* t:<expt-duration>; */ + duration_string = par; + break; + case 'S': /* S:<sample-interval>; */ + if ((err = sample_set_interval (par)) != COL_ERROR_NONE) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, err, par); + return err; + } + break; + case 'L': /* L:<experiment-size-limit>; */ + if ((err = __collector_set_size_limit (par)) != COL_ERROR_NONE) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, err, par); + return err; + } + break; + case 'P': /* P:PROJECT_HOME; */ + project_home = __collector_strdup (par); + break; + case 'h': + case 'p': + fs_matters = 1; + break; + case 'Y': + err = set_user_sig_action (par); + if (err != COL_ERROR_NONE) + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, err, par); + break; + default: + /* Ignore unknown parameters; allow them to be handled by modules */ + break; + } + } + /* end of data descriptor parsing */ + + if (!seen_F_flag) + { + char * par = "0"; // This will not happen when collect has no -F option + if ((err = __collector_ext_line_install (par, __collector_exp_dir_name)) != COL_ERROR_NONE) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, err, par); + return err; + } + } + + /* now that we know what data is being collected, we can set the filesystem warning */ + fs_warn (); + + // We have to create all tsd keys before __collector_tsd_allocate(). + // With the pthreads-based implementation, this might no longer be necessary. + // In any case, we still have to create the key before a thread can use it. + __collector_ext_gettid_tsd_create_key (); + __collector_ext_dispatcher_tsd_create_key (); + + /* allocate tsd for the current thread */ + if (__collector_tsd_allocate () != 0) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">TSD allocate failed</event>\n", SP_JCMD_CERROR, COL_ERROR_EXPOPEN); + return COL_ERROR_EXPOPEN; + } + /* init tsd for unwind, called right after __collector_tsd_allocate()*/ + __collector_ext_unwind_key_init (1, NULL); + + /* start java attach if suitable */ + if (exp_origin == SP_ORIGIN_DBX_ATTACH) + __collector_jprofile_start_attach (); + start_sec_time = CALL_UTIL (time)(NULL); + __collector_start_time = collector_interface.getHiResTime (); + TprintfT (DBG_LT0, "\t__collector_open_experiment; resetting start_time\n"); + if (duration_string != NULL && (err = set_duration (duration_string)) != COL_ERROR_NONE) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CERROR, err, duration_string); + return err; + } + + /* install the common SIGPROF dispatcher (requires TSD) */ + if ((err = __collector_ext_dispatcher_install ()) != COL_ERROR_NONE) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\"/></event>\n", SP_JCMD_CERROR, err); + return err; + } + + /* mark the experiment open complete */ + exp_open = 1; + if (exp_origin == SP_ORIGIN_DBX_ATTACH) + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" time=\"%lld\" tm_zone=\"%lld\"/>\n", + SP_JCMD_RUN, + (unsigned) (__collector_start_time / NANOSEC), (unsigned) (__collector_start_time % NANOSEC), + (long long) start_sec_time, (long long) 0); + else + log_write_event_run (); + + /* schedule the first sample */ + __collector_next_sample = __collector_start_time + ((hrtime_t) NANOSEC) * __collector_sample_period; + __collector_ext_usage_sample (MASTER_SMPL, "collector_open_experiment"); + + /* start data collection in dynamic modules */ + if (collector_paused == 0) + { + for (i = 0; i < nmodules; i++) + if (modules[i]->startDataCollection != NULL && modules_st[i] == 0) + modules[i]->startDataCollection (); + } + else + { + hrtime_t ts = GETRELTIME (); + (void) __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\"/>\n", + SP_JCMD_PAUSE, (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC)); + } + + /* mark the experiment active */ + __collector_exp_active = 1; + return COL_ERROR_NONE; +} + +/* prepare directory for new experiment of fork-child */ + +/* return 0 if successful */ +static int +collector_create_expr_dir (const char *new_exp_name) +{ + int ret = -1; + mode_t dmode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + TprintfT (DBG_LT1, "collector: __collector_create_expr_dir(%s)\n", new_exp_name); + if (CALL_UTIL (mkdir)(new_exp_name, dmode) < 0) + TprintfT (0, "__collector_create_expr_dir(%s) ERROR: errno=%d\n", new_exp_name, errno); + else + ret = 0; + return (ret); +} + +/* append _xN to __collector_exp_dir_name*/ +/* return 0 if successful */ +static int +collector_exp_dir_append_x (int linenum, const char *parent_exp_name) +{ + char buffer[MAXPATHLEN + 1]; + char * p = __collector_strrchr (parent_exp_name, '/'); + if (p == NULL || (*(p + 1) != '_')) + { + size_t sz = CALL_UTIL (strlen)(parent_exp_name); + const char * q = parent_exp_name + sz - 3; + if (sz < 3 || __collector_strncmp (q, ".er", CALL_UTIL (strlen)(q)) != 0 + || CALL_UTIL (access)(parent_exp_name, F_OK) != 0) + { + TprintfT (0, "collector_exp_dir_append_x() ERROR: invalid parent_exp_name %s\n", parent_exp_name); + return -1; + } + CALL_UTIL (strlcpy)(buffer, parent_exp_name, sizeof (buffer)); + CALL_UTIL (snprintf)(__collector_exp_dir_name, sizeof (__collector_exp_dir_name), + "%s/_x%d.er", buffer, linenum); + } + else + { + p = __collector_strrchr (parent_exp_name, '.'); + if (p == NULL || *(p + 1) != 'e' || *(p + 2) != 'r') + { + TprintfT (0, "collector_exp_dir_append_x() ERROR: invalid parent_exp_name %s\n", parent_exp_name); + return -1; + } + CALL_UTIL (strlcpy)(buffer, parent_exp_name, + ((p - parent_exp_name + 1)<sizeof (buffer)) ? (p - parent_exp_name + 1) : sizeof (buffer)); + CALL_UTIL (snprintf)(__collector_exp_dir_name, sizeof (__collector_exp_dir_name), + "%s_x%d.er", buffer, linenum); + } + return 0; +} + +/* prepare directory for new experiment of exec/combo child*/ + +/* return 0 if successful */ +static int +collector_create_expr_dir_lineage (const char *parent_exp_name) +{ + int ret = -1; + mode_t dmode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + int linenum = 1; + while (linenum < INT_MAX) + { + if (collector_exp_dir_append_x (linenum, parent_exp_name) != 0) + return -1; + if (CALL_UTIL (access)(__collector_exp_dir_name, F_OK) != 0) + { + if (CALL_UTIL (mkdir)(__collector_exp_dir_name, dmode) == 0) + return 0; + } + linenum++; + TprintfT (DBG_LT0, "collector: collector_create_expr_dir_lineage(%s -> %s)\n", parent_exp_name, __collector_exp_dir_name); + } + return (ret); +} + +/* Finish the initializing work if we don't collect data while libcollector.so is preloaded. */ +/* return COL_ERROR_NONE if successful */ +static int +collector_tail_init (const char *parent_exp_name) +{ + int err = COL_ERROR_NONE; + if (exp_origin != SP_ORIGIN_FORK) + { + /* For exec/combo descendants. Don't create dir for this subexp, but update lineage by appending "_x0". */ + /* Different children can have the same _x0 if their name don't match -F exp. + * Assume their fork children inherit the program name, there will be no _x0_fN.er to create. + * So we don't need to worry about the lineage messed up by _x0. + */ + int linenum = 0; + if (collector_exp_dir_append_x (linenum, parent_exp_name) != 0) + return COL_ERROR_BADDIR; + static char exp_name_env[MAXPATHLEN + 1]; + CALL_UTIL (snprintf)(exp_name_env, sizeof (exp_name_env), "SP_COLLECTOR_EXPNAME=%s", __collector_exp_dir_name); + TprintfT (DBG_LT1, "collector_tail_init: setting SP_COLLECTOR_EXPNAME to %s\n", __collector_exp_dir_name); + CALL_UTIL (putenv)(exp_name_env); + } + /* initialize the segments map and mmap interposition */ + if (exp_origin != SP_ORIGIN_GENEXP && exp_origin != SP_ORIGIN_KERNEL) + if ((err = __collector_ext_mmap_install (0)) != COL_ERROR_NONE) + return err; + + /* initialize TSD module (note: relies on __collector_heap) */ + if (__collector_tsd_init () != 0) + return COL_ERROR_EXPOPEN; + + /* initialize for subsequent stack unwinds */ + __collector_ext_unwind_init (0); + + char * buf = NULL; + /* Make a copy of params so that we can modify the string */ + int paramsz = CALL_UTIL (strlen)(collector_params) + 1; + buf = (char*) alloca (paramsz); + CALL_UTIL (strlcpy)(buf, collector_params, paramsz); + + char *par_F = "0"; + char *s; + for (s = buf; *s;) + { + char key = *s++; + /* ensure that it's followed by a colon */ + if (*s++ != ':') + { + TprintfT (DBG_LT0, "collector_tail_init: ERROR: parameter %c is not followed by a colon\n", key); + return COL_ERROR_ARGS; + } + + /* find the semicolon terminator */ + char *par = s; + while (*s && (*s != ';')) + s++; + if (*s != ';') + { + /* not followed by semicolon */ + TprintfT (0, "collector_tail_init: ERROR: parameter %c:%s is not terminated by a semicolon\n", key, par); + return COL_ERROR_ARGS; + } + /* terminate par, and position for next descriptor */ + *s++ = 0; + /* now process that element of the data descriptor */ + if (key == 'F') + { + par_F = par; + break; + } + } + if ((err = __collector_ext_line_install (par_F, __collector_exp_dir_name)) != COL_ERROR_NONE) + return err; + + /* allocate tsd for the current thread */ + if (__collector_tsd_allocate () != 0) + return COL_ERROR_EXPOPEN; + return COL_ERROR_NONE; +} + +/* routines concerning closing the experiment */ +/* close down -- fini section routine */ +static void collector_fini () __attribute__ ((destructor)); +static void +collector_fini () +{ + TprintfT (DBG_LT0, "collector_fini: closing experiment\n"); + __collector_close_experiment (); + +} + +void collector_terminate_expt () __attribute__ ((weak, alias ("__collector_terminate_expt"))); + +/* __collector_terminate_expt called by user, or from dbx */ +void +__collector_terminate_expt () +{ + TprintfT (DBG_LT0, "__collector_terminate_expt: %s; calling close\n", __collector_exp_dir_name); + __collector_close_experiment (); + TprintfT (DBG_LT0, "__collector_terminate_expt done\n\n"); +} + +/* + * We manage the SIGCHLD handler with sigaction and don't worry about signal or sigset(). + * This is in line with the comments in dispatcher.c + * immediately preceding the wrapper function for (Linux) signal(). + */ +static struct sigaction original_sigchld_sigaction; +static pid_t mychild_pid = -1; + +/* __collector_SIGCHLD_signal_handler called when er_archive exits */ +static void +__collector_SIGCHLD_signal_handler (int sig, siginfo_t *si, void *context) +{ + pid_t calling_pid = si->si_pid; + /* Potential race. + * We get mychild_pid from the vfork() return value. + * So there is an outside chance that the child completes and sends SIGCHLD + * before the handler knows the value of mychild_pid. + */ + if (calling_pid == mychild_pid) + // er_archive has exited; so restore the user handler + __collector_sigaction (SIGCHLD, &original_sigchld_sigaction, NULL); + else + { + // if we can't identify the pid, the signal must be for the user's handler + if (original_sigchld_sigaction.sa_handler != SIG_DFL + && original_sigchld_sigaction.sa_handler != SIG_IGN) + original_sigchld_sigaction.sa_sigaction (sig, si, context); + } + TprintfT (DBG_LT1, "__collector_SIGCHLD_signal_handler done\n\n"); +} + +int +collector_sigchld_sigaction (const struct sigaction *nact, + struct sigaction *oact) +{ + // get the current SIGCHLD handler + struct sigaction cur_handler; + __collector_sigaction (SIGCHLD, NULL, &cur_handler); + + // if we have NOT installed our own handler, return an error + // (force the caller to deal with this case) + if (cur_handler.sa_sigaction != __collector_SIGCHLD_signal_handler) + return -1; + + // if we HAVE installed our own handler, act on the user's handler + if (oact) + __collector_memcpy (oact, &original_sigchld_sigaction, sizeof (struct sigaction)); + if (nact) + __collector_memcpy (&original_sigchld_sigaction, nact, sizeof (struct sigaction)); + return 0; +} + +/* + * __collector_close_experiment may be called either from + * __collector_terminate_expt() or the .fini section + */ +void +__collector_close_experiment () +{ + hrtime_t ts; + char *argv[10]; + int status; + TprintfT (DBG_LT1, "collector: __collector_close_experiment(): %s\n", __collector_exp_dir_name); + if (!exp_initted) + return; + /* The experiment may have been previously closed */ + if (!exp_open) + return; + + if (__collector_mutex_trylock (&__collector_close_guard)) + /* someone else is in the middle of closing the experiment */ + return; + + /* record the termination of the experiment */ + ts = GETRELTIME (); + collector_params = NULL; + + /* tell all dynamic modules to stop data collection */ + int i; + for (i = 0; i < nmodules; i++) + if (modules[i]->stopDataCollection != NULL) + modules[i]->stopDataCollection (); + + /* notify all dynamic modules the experiment is being closed */ + for (i = 0; i < nmodules; i++) + { + if (modules[i]->closeExperiment != NULL) + modules[i]->closeExperiment (); + __collector_delete_handle (modules_hndl[i]); + modules_hndl[i] = NULL; + } + + /* acquire the global lock -- only one close at a time */ + __collector_mutex_lock (&__collector_glob_lock); + /* deinstall mmap tracing (with final update) */ + __collector_ext_mmap_deinstall (1); + + /* deinstall common SIGPROF dispatcher */ + __collector_ext_dispatcher_deinstall (); + + /* disable line interposition */ + __collector_ext_line_close (); + + /* Other threads may be reading tsd now. */ + //__collector_tsd_fini(); + + /* delete global heap */ + /* omazur: do not delete the global heap + * to avoid crashes in TSD. Need a better solution. + __collector_deleteHeap( __collector_heap ); + __collector_heap = NULL; + */ + __collector_mutex_unlock (&__collector_glob_lock); + + /* take a final sample */ + __collector_ext_usage_sample (MASTER_SMPL, "collector_close_experiment"); + sample_mode = 0; + + /* close the frameinfo file */ + __collector_ext_unwind_close (); + if (exp_origin != SP_ORIGIN_DBX_ATTACH) + log_write_event_run (); + + /* mark the experiment as closed */ + __collector_expstate = EXP_CLOSED; + TprintfT (DBG_LT1, "collector: __collector_expstate->EXP_CLOSED: project_home=%s\n", + STR (project_home)); + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\"/>\n", + SP_JCMD_EXIT, + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC)); + + /* derive er_archive's absolute path from that of libcollector */ + argv[0] = NULL; + if (project_home && archive_mode && __collector_strcmp (archive_mode, "off")) + { + /* construct a command to launch it */ + char *er_archive_name = "/bin/gp-archive"; + size_t cmdlen = CALL_UTIL (strlen)(project_home) + CALL_UTIL (strlen)(er_archive_name) + 1; + char *command = (char*) alloca (cmdlen); + CALL_UTIL (snprintf)(command, cmdlen, "%s%s", project_home, er_archive_name); + if (CALL_UTIL (access)(command, F_OK) == 0) + { + // build the argument list + int nargs = 0; + argv[nargs++] = command; + argv[nargs++] = "-n"; + argv[nargs++] = "-a"; + argv[nargs++] = archive_mode; + size_t len = CALL_UTIL (strlen)(__collector_exp_dir_name) + 1; + size_t len1 = CALL_UTIL (strlen)(SP_ARCHIVE_LOG_FILE) + 1; + char *str = (char*) alloca (len + len1); + CALL_UTIL (snprintf)(str, len + 15, "%s/%s", __collector_exp_dir_name, SP_ARCHIVE_LOG_FILE); + argv[nargs++] = "--outfile"; + argv[nargs++] = str; + str = (char*) alloca (len); + CALL_UTIL (snprintf)(str, len, "%s", __collector_exp_dir_name); + argv[nargs++] = str; + argv[nargs] = NULL; + } + } + + /* log the archive command to be run */ + if (argv[0] == NULL) + { + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_COMMENT, COL_COMMENT_NONE, "No archive command run"); + TprintfT (DBG_LT1, "collector: No archive command run\n"); + } + else + { + char cmdbuf[4096]; + int bufoffset = 0; + int i; + for (i = 0; argv[i] != NULL; i++) + { + bufoffset += CALL_UTIL (snprintf)(&cmdbuf[bufoffset], (sizeof (cmdbuf) - bufoffset), + " %s", argv[i]); + } + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">Archive command `%s'</event>\n", + SP_JCMD_COMMENT, COL_COMMENT_NONE, cmdbuf); + TprintfT (DBG_LT1, "collector: running `%s'\n", cmdbuf); + } + log_close (); + TprintfT (DBG_LT1, "__collector_close_experiment(%s) done\n", __collector_exp_dir_name); + exp_open = 0; /* mark the experiment as closed */ + __collector_exp_active = 0; /* mark the experiment as inactive */ + + /* reset all experiment parameters */ + sample_mode = 0; + collector_paused = 0; + __collector_pause_sig = -1; + __collector_pause_sig_warn = 0; + __collector_sample_sig = -1; + __collector_sample_sig_warn = 0; + __collector_sample_period = 0; + __collector_exp_dir_name[0] = 0; + + /* uninstall the pause and sample signal handlers */ + /* XXXX -- not yet, because of potential race conditions in libthread */ + if (argv[0] == NULL) + { + /* er_archive command will not be run */ + __collector_mutex_unlock (&__collector_close_guard); + return; + } + + struct sigaction sa; + CALL_UTIL (memset)(&sa, 0, sizeof (struct sigaction)); + sa.sa_sigaction = __collector_SIGCHLD_signal_handler; + sa.sa_flags = SA_SIGINFO; + __collector_sigaction (SIGCHLD, &sa, &original_sigchld_sigaction); + + /* linetrace interposition takes care of unsetting Environment variables */ + /* create a child process to invoke er_archive */ + pid_t pid = CALL_UTIL (vfork)(); + if (pid == 0) + { + /* pid is zero == child process -- invoke er_archive */ + /* Unset LD_PRELOAD environment variables */ + CALL_UTIL (unsetenv)("LD_PRELOAD_32"); + CALL_UTIL (unsetenv)("LD_PRELOAD_64"); + CALL_UTIL (unsetenv)("LD_PRELOAD"); + /* Invoke er_archive */ + CALL_UTIL (execv)(argv[0], argv); + CALL_UTIL (exit)(1); /* exec failed -- child exits with an error */ + } + else if (pid != -1) + { + mychild_pid = pid; // notify our signal handler who the child is + pid_t w; + /* copied from system.c */ + do + { + w = CALL_UTIL (waitpid)(pid, &status, 0); + } + while (w == -1 && errno == EINTR); + TprintfT (DBG_LT1, "collector: creating archive done\n"); + // __collector_SIGCHLD_signal_handler should now be de-installed, but it does so itself + } + else + /* child-process creation failed */ + TprintfT (DBG_LT0, "collector: creating archive process failed\n"); + + __collector_mutex_unlock (&__collector_close_guard); + TprintfT (DBG_LT1, "collector: __collector_close_experiment done\n"); + return; +} + +/* + * void __collector_clean_state() + * Perform all necessary cleanup steps in child process after fork(). + */ +void +__collector_clean_state () +{ + TprintfT (DBG_LT1, "collector: collector_clean_state()\n"); + int i; + /* + * We are in child process after fork(). + * First of all we have to reset all mutex locks in collector's subsystems. + * After that we can reinitialize modules. + */ + __collector_mmgr_init_mutex_locks (__collector_heap); + __collector_mutex_init (&__collector_glob_lock); + __collector_mutex_init (&__collector_open_guard); + __collector_mutex_init (&__collector_close_guard); + __collector_mutex_init (&__collector_sample_guard); + __collector_mutex_init (&__collector_suspend_guard); + __collector_mutex_init (&__collector_resume_guard); + + if (__collector_mutex_trylock (&__collector_close_guard)) + /* someone else is in the middle of closing the experiment */ + return; + + /* Stop data collection in all dynamic modules */ + for (i = 0; i < nmodules; i++) + if (modules[i]->stopDataCollection != NULL) + modules[i]->stopDataCollection (); + + // Now we can reset modules + for (i = 0; i < nmodules; i++) + { + if (modules[i]->detachExperiment != NULL && modules_st[i] == 0) + modules[i]->detachExperiment (); + __collector_delete_handle (modules_hndl[i]); + modules_hndl[i] = NULL; + } + + /* acquire the global lock -- only one suspend at a time */ + __collector_mutex_lock (&__collector_glob_lock); + { + + /* stop any profile data writing */ + paused_when_suspended = collector_paused; + collector_paused = 1; + + /* deinstall common SIGPROF dispatcher */ + __collector_ext_dispatcher_suspend (); + + /* mark the experiment as suspended */ + __collector_exp_active = 0; + + /* XXXX mark the experiment as closed! */ + exp_open = 0; /* This is a hack to allow fork child to call__collector_open_experiment() */ + + /* mark the experiment log closed! */ + log_close (); + } + __collector_mutex_unlock (&__collector_glob_lock); + + // Now we can reset subsystems. + __collector_ext_dispatcher_fork_child_cleanup (); + __collector_mmap_fork_child_cleanup (); + __collector_tsd_fork_child_cleanup (); + paused_when_suspended = 0; + collector_paused = 0; + __collector_expstate = EXP_INIT; + TprintfT (DBG_LT1, "__collector_clean_slate: __collector_expstate->EXP_INIT\n"); + exp_origin = SP_ORIGIN_LIBCOL_INIT; + exp_initted = 0; + __collector_start_time = collector_interface.getHiResTime (); + TprintfT (DBG_LT1, " -->__collector_clean_slate; resetting start_time\n"); + start_sec_time = 0; + + /* Sample related data */ + sample_installed = 0; // 1 if the sample signal handler installed + sample_mode = 0; // dynamically turns sample record writing on/off + sample_number = 0; // index of the current sample record + __collector_sample_sig = -1; // user-specified sample signal + __collector_sample_sig_warn = 0; // non-zero if warning already given + + /* Pause/resume related data */ + __collector_pause_sig = -1; // user-specified pause signal + __collector_pause_sig_warn = 0; // non-zero if warning already given + __collector_mutex_unlock (&__collector_close_guard); + return; +} + +/* modelled on __collector_close_experiment */ +void +__collector_suspend_experiment (char *why) +{ + if (!exp_initted) + return; + /* The experiment may have been previously closed */ + if (!exp_open) + return; + /* The experiment may have been previously suspended */ + if (!__collector_exp_active) + return; + if (__collector_mutex_trylock (&__collector_suspend_guard)) + /* someone else is in the middle of suspending the experiment */ + return; + + /* Stop data collection in all dynamic modules */ + int i; + for (i = 0; i < nmodules; i++) + if (modules[i]->stopDataCollection != NULL) + modules[i]->stopDataCollection (); + + /* take a pre-suspension sample */ + __collector_ext_usage_sample (MASTER_SMPL, why); + + /* acquire the global lock -- only one suspend at a time */ + __collector_mutex_lock (&__collector_glob_lock); + /* stop any profile data writing */ + paused_when_suspended = collector_paused; + collector_paused = 1; + + /* deinstall common SIGPROF dispatcher */ + __collector_ext_dispatcher_suspend (); + + /* mark the experiment as suspended */ + __collector_exp_active = 0; + + /* XXXX mark the experiment as closed! */ + exp_open = 0; // This is a hack to allow fork child to call __collector_open_experiment() + log_pause (); // mark the experiment log closed! + TprintfT (DBG_LT0, "collector: collector_suspend_experiment(%s, %d)\n\n", why, collector_paused); + __collector_mutex_unlock (&__collector_glob_lock); + __collector_mutex_unlock (&__collector_suspend_guard); + return; +} + +void +__collector_resume_experiment () +{ + if (!exp_initted) + return; + + /* The experiment may have been previously resumed */ + if (__collector_exp_active) + return; + if (__collector_mutex_trylock (&__collector_resume_guard)) + /* someone else is in the middle of resuming the experiment */ + return; + + /* acquire the global lock -- only one resume at a time */ + __collector_mutex_lock (&__collector_glob_lock); + /* mark the experiment as re-activated */ + __collector_exp_active = 1; + /* XXXX mark the experiment as open! */ + exp_open = 1; // This is a hack to allow fork child to call__collector_open_experiment() + log_resume (); // mark the experiment log re-opened! + TprintfT (DBG_LT0, "collector: collector_resume_experiment(%d)\n", paused_when_suspended); + /* resume any profile data writing */ + collector_paused = paused_when_suspended; + /* restart common SIGPROF dispatcher */ + __collector_ext_dispatcher_restart (); + __collector_mutex_unlock (&__collector_glob_lock); + + /* take a post-suspension sample */ + __collector_ext_usage_sample (MASTER_SMPL, "collector_resume_experiment"); + + /* Resume data collection in all dynamic modules */ + if (collector_paused == 0) + { + int i; + for (i = 0; i < nmodules; i++) + if (modules[i]->startDataCollection != NULL && modules_st[i] == 0) + modules[i]->startDataCollection (); + } + + if (__collector_sample_period != 0) + { + hrtime_t now = collector_interface.getHiResTime (); + while (__collector_next_sample < now) + __collector_next_sample += ((hrtime_t) NANOSEC) * __collector_sample_period; + } + + /* check for experiment past termination time */ + if (__collector_terminate_time != 0) + { + hrtime_t now = collector_interface.getHiResTime (); + if (__collector_terminate_time < now) + { + TprintfT (DBG_LT0, "__collector_resume_experiment: now (%lld) > terminate_time (%lld); closing experiment\n", + (now - __collector_start_time), (__collector_terminate_time - __collector_start_time)); + __collector_close_experiment (); + } + } + __collector_mutex_unlock (&__collector_resume_guard); + return; +} + +/* Code to support Samples and Pause/Resume */ +void collector_sample () __attribute__ ((weak, alias ("__collector_sample"))); +void +__collector_sample (char *name) +{ + __collector_ext_usage_sample (PROGRAM_SMPL, name); +} + +static void +write_sample (char *name) +{ + if (sample_mode == 0) + return; + /* make the sample timestamp relative to the start */ + hrtime_t ts, now = collector_interface.getHiResTime (); + + /* update time for next periodic sample */ + /* since this is common to all LWPs, and only one (the first!) will + update it to the next period, doing the update early will avoid + the overhead/frustration of the other LWPs + */ + if (__collector_sample_period != 0) + { + /* this update should only be done for periodic samples */ + while (__collector_next_sample < now) + __collector_next_sample += ((hrtime_t) NANOSEC) * __collector_sample_period; + } + + /* take the sample and record it; use (return - __collector_start_time) for timestamp */ + now = ovw_write (); + ts = now - __collector_start_time; + + /* write sample records to log file */ + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" id=\"%d\" label=\"%s\"/>\n", + SP_JCMD_SAMPLE, + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + sample_number, + name); + /* increment the sample number */ + sample_number++; +} + +/* + * __collector_ext_usage_sample + * + * Handle taking a process usage sample and recording it. + * Common to all different types of sample: + * libcollector master samples at initiation and close, + * programmatic samples via libcollector API calls, + * periodic samples originating in the dispatcher, + * manual samples originating in the signal sample handler, + * manual samples originating from the debugger + * Differentiating type and name information is currently not recorded. + */ +void +__collector_ext_usage_sample (Smpl_type type, char *name) +{ + /* name is optional */ + if (name == NULL) + name = ""; + TprintfT (DBG_LT3, "collector: __collector_ext_usage_sample(%d,%s)\n", type, name); + if (!exp_initted) + return; + + /* if paused, don't record periodic samples */ + if ((type == PERIOD_SMPL) && (collector_paused == 1)) + return; + + /* There is a possibility of entering this function + * from sample_handler, dbx direct call to __collector_sample, + * and user called collector_sample. Since we are making a + * new sample anyway just return. + */ + if (__collector_mutex_trylock (&__collector_sample_guard)) + return; + if (type != PERIOD_SMPL || __collector_sample_period != 0) + write_sample (name); + __collector_mutex_unlock (&__collector_sample_guard); +} + +/* set the sample period from the parameter */ +static int +sample_set_interval (char *param) +{ + if (!exp_initted) + return COL_ERROR_SMPLINIT; + __collector_sample_period = CALL_UTIL (strtol)(param, NULL, 0); /* seconds */ + TprintfT (DBG_LT1, "collector: collector_sample period set to %d seconds.\n", + __collector_sample_period); + if (__collector_sample_period > 0) + (void) __collector_log_write ("<setting %s=\"%d\"/>\n", + SP_JCMD_SAMPLE_PERIOD, __collector_sample_period); + return COL_ERROR_NONE; +} + +/* set the experiment duration from the parameter */ + +/* parameter is of the form nnn:mmm, where nnn is the start delay in seconds, + * and mmm is the terminate time in seconds; if nnn is zero, + * data collection starts when the run starts. If mmm is zero, + * data collection terminates when the run terminates. Otherwise, + * nnn must be less than mmm + */ +static int +set_duration (char *param) +{ + if (!exp_initted) + return COL_ERROR_DURATION_INIT; + int delay_start = CALL_UTIL (strtol)(param, ¶m, 0); /* seconds */ + int terminate_duration = 0; + if (*param == 0) + { + /* we only have one parameter, the terminate time */ + terminate_duration = delay_start; + delay_start = 0; + } + else if (*param == ':') + { + param++; + terminate_duration = CALL_UTIL (strtol)(param, ¶m, 0); /* seconds */ + } + else + return COL_ERROR_DURATION_INIT; + TprintfT (DBG_LT1, "collector: collector_delay_start duration set to %d seconds.\n", + delay_start); + TprintfT (DBG_LT1, "collector: collector_terminate duration set to %d seconds.\n", + terminate_duration); + if (terminate_duration > 0) + __collector_log_write ("<setting %s=\"%d\"/>\n<setting %s=\"%d\"/>\n", + SP_JCMD_DELAYSTART, delay_start, + SP_JCMD_TERMINATE, terminate_duration); + __collector_delay_start = (hrtime_t) 0; + if (delay_start != 0) + { + __collector_delay_start = __collector_start_time + ((hrtime_t) NANOSEC) * delay_start; + collector_paused = 1; + } + __collector_terminate_time = terminate_duration == 0 ? (hrtime_t) 0 : + __collector_start_time + ((hrtime_t) NANOSEC) * terminate_duration; + return COL_ERROR_NONE; +} + +static int +sample_set_user_sig (char *par) +{ + int sig = CALL_UTIL (strtol)(par, &par, 0); + TprintfT (DBG_LT1, "collector: sample_set_user_sig(sig=%d,installed=%d)\n", + sig, sample_installed); + /* Installing the sampling signal handler more + * than once is not good. + */ + if (!sample_installed) + { + struct sigaction act; + sigemptyset (&act.sa_mask); + /* XXXX should any signals be blocked? */ + act.sa_sigaction = sample_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (sig, &act, &old_sample_handler) == -1) + { + TprintfT (DBG_LT0, "collector: ERROR: collector_sample_handler install failed (sig=%d).\n", + __collector_sample_sig); + return COL_ERROR_ARGS; + } + if (old_sample_handler.sa_handler == SIG_DFL || + old_sample_handler.sa_sigaction == sample_handler) + old_sample_handler.sa_handler = SIG_IGN; + TprintfT (DBG_LT1, "collector: collector_sample_handler installed (sig=%d,hndlr=0x%p).\n", + sig, sample_handler); + __collector_sample_sig = sig; + sample_installed = 1; + } + (void) __collector_log_write ("<setting %s=\"%u\"/>\n", SP_JCMD_SAMPLE_SIG, __collector_sample_sig); + return COL_ERROR_NONE; +} + +/* signal handler for sample signal */ +static void +sample_handler (int sig, siginfo_t *sip, void *uap) +{ + if (sip && sip->si_code == SI_USER) + { + TprintfT (DBG_LT1, "collector: collector_sample_handler sampling!\n"); + __collector_ext_usage_sample (MANUAL_SMPL, "signal"); + } + else if (old_sample_handler.sa_handler != SIG_IGN) + { + TprintfT (DBG_LT1, "collector: collector_sample_handler forwarding signal.\n"); + (old_sample_handler.sa_sigaction)(sig, sip, uap); + } +} + +void collector_pause () __attribute__ ((weak, alias ("__collector_pause"))); + +void +__collector_pause () +{ + __collector_pause_m ("API"); +} + +void +__collector_pause_m (char *reason) +{ + hrtime_t now; + char xreason[MAXPATHLEN]; + TprintfT (DBG_LT0, "collector: __collector_pause_m(%s)\n", reason); + + /* Stop data collection in all dynamic modules */ + for (int i = 0; i < nmodules; i++) + if (modules[i]->stopDataCollection != NULL) + modules[i]->stopDataCollection (); + + /* Take a pause sample */ + CALL_UTIL (snprintf)(xreason, sizeof (xreason), "collector_pause(%s)", reason); + __collector_ext_usage_sample (MASTER_SMPL, xreason); + + /* Record the event in the log file */ + now = GETRELTIME (); + (void) __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" name=\"%s\"/>\n", SP_JCMD_PAUSE, + (unsigned) (now / NANOSEC), (unsigned) (now % NANOSEC), reason); + __collector_expstate = EXP_PAUSED; + TprintfT (DBG_LT1, "collector: __collector_expstate->EXP_PAUSED\n"); + collector_paused = 1; +} + +void collector_resume () __attribute__ ((weak, alias ("__collector_resume"))); + +void +__collector_resume () +{ + TprintfT (DBG_LT0, "collector: __collector_resume()\n"); + __collector_expstate = EXP_OPEN; + TprintfT (DBG_LT1, "collector: __collector_expstate->EXP_OPEN\n"); + + /* Record the event in the log file */ + hrtime_t now = GETRELTIME (); + (void) __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\"/>\n", SP_JCMD_RESUME, + (unsigned) (now / NANOSEC), (unsigned) (now % NANOSEC)); + /* Take a resume sample */ + __collector_ext_usage_sample (MASTER_SMPL, "collector_resume"); + + /* Resume data collection in all dynamic modules */ + for (int i = 0; i < nmodules; i++) + if (modules[i]->startDataCollection != NULL && modules_st[i] == 0) + modules[i]->startDataCollection (); + collector_paused = 0; +} + +static int +pause_set_user_sig (char *par) +{ + struct sigaction act; + int sig = CALL_UTIL (strtol)(par, &par, 0); + if (*par) + { + /* not end of the token */ + if (*par != 'p') + { + /* it should be a p */ + TprintfT (DBG_LT0, "collector: ERROR: collector_user_handler bad terminator (par=%p[0]=%d).\n", + par, (int) *par); + return COL_ERROR_ARGS; + + } + else + { + /*, it's a p, make sure next is end of token */ + par++; + if (*par) + { + TprintfT (DBG_LT0, "collector: ERROR: collector_user_handler bad terminator (par=%p[0]=%d).\n", + par, (int) *par); + return COL_ERROR_ARGS; + } + else + /* start off paused */ + collector_paused = 1; + } + } + sigemptyset (&act.sa_mask); + /* XXXX should any signals be blocked? */ + act.sa_sigaction = pause_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (sig, &act, &old_pause_handler) == -1) + { + TprintfT (DBG_LT0, "collector: ERROR: collector_pause_handler install failed (sig=%d).\n", sig); + return COL_ERROR_ARGS; + } + if (old_pause_handler.sa_handler == SIG_DFL || + old_pause_handler.sa_sigaction == pause_handler) + old_pause_handler.sa_handler = SIG_IGN; + TprintfT (DBG_LT1, "collector: collector_pause_handler installed (sig=%d,hndlr=0x%p).\n", + sig, pause_handler); + __collector_pause_sig = sig; + (void) __collector_log_write ("<setting %s=\"%u\"/>\n", SP_JCMD_PAUSE_SIG, + __collector_pause_sig); + return COL_ERROR_NONE; +} + +/* signal handler for pause/resume signal */ +static void +pause_handler (int sig, siginfo_t *sip, void *uap) +{ + if (sip && sip->si_code == SI_USER) + { + if (collector_paused == 1) + { + __collector_resume (); + TprintfT (DBG_LT0, "collector: collector_pause_handler resumed!\n"); + } + else + { + __collector_pause_m ("signal"); + TprintfT (DBG_LT0, "collector: collector_pause_handler paused!\n"); + } + } + else if (old_pause_handler.sa_handler != SIG_IGN) + { + TprintfT (DBG_LT0, "collector: collector_pause_handler forwarding signal.\n"); + (old_pause_handler.sa_sigaction)(sig, sip, uap); + } +} + +static void +get_progspec (char *retstr, int tmp_sz, char *name, int name_sz) +{ + int procfd, count, i; + *retstr = 0; + tmp_sz--; + *name = 0; + name_sz--; + procfd = CALL_UTIL (open)("/proc/self/cmdline", O_RDONLY); + int getting_name = 0; + if (procfd != -1) + { + count = CALL_UTIL (read)(procfd, retstr, tmp_sz); + retstr[count] = '\0'; + for (i = 0; i < count; i++) + { + if (getting_name == 0) + name[i] = retstr[i]; + if (retstr[i] == '\0') + { + getting_name = 1; + if ((i + 1) < count) + retstr[i] = ' '; + } + } + CALL_UTIL (close)(procfd); + } +} + +static void +fs_warn () +{ + /* if data implies we don't care, just return */ + if (fs_matters == 0) + return; +} + +static void +close_handler (int sig, siginfo_t *sip, void *uap) +{ + if (sip && sip->si_code == SI_USER) + { + TprintfT (DBG_LT0, "collector: close_handler: processing signal.\n"); + __collector_close_experiment (); + } + else if (old_close_handler.sa_handler != SIG_IGN) + { + TprintfT (DBG_LT0, "collector: close_handler forwarding signal.\n"); + (old_close_handler.sa_sigaction)(sig, sip, uap); + } +} + +static void +exit_handler (int sig, siginfo_t *sip, void *uap) +{ + if (sip && sip->si_code == SI_USER) + { + TprintfT (DBG_LT0, "collector: exit_handler: processing signal.\n"); + CALL_UTIL (exit)(1); + } + else if (old_exit_handler.sa_handler != SIG_IGN) + { + TprintfT (DBG_LT0, "collector: exit_handler forwarding signal.\n"); + (old_exit_handler.sa_sigaction)(sig, sip, uap); + } +} + +static int +set_user_sig_action (char *par) +{ + int sig = CALL_UTIL (strtol)(par, &par, 0); + if (*par != '=') + { + TprintfT (DBG_LT0, "collector: ERROR: set_user_sig_action bad separator: %s.\n", par); + return COL_ERROR_ARGS; + } + par++; + struct sigaction act; + sigemptyset (&act.sa_mask); + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (__collector_strcmp (par, "exit") == 0) + { + act.sa_sigaction = exit_handler; + if (sigaction (sig, &act, &old_exit_handler) == -1) + { + TprintfT (DBG_LT0, "collector: ERROR: set_user_sig_action failed: %d=%s.\n", sig, par); + return COL_ERROR_ARGS; + } + } + else if (__collector_strcmp (par, "close") == 0) + { + act.sa_sigaction = close_handler; + if (sigaction (sig, &act, &old_close_handler) == -1) + { + TprintfT (DBG_LT0, "collector: ERROR: set_user_sig_action failed: %d=%s.\n", sig, par); + return COL_ERROR_ARGS; + } + } + else + { + TprintfT (DBG_LT0, "collector: ERROR: set_user_sig_action unknown action: %d=%s.\n", sig, par); + return COL_ERROR_ARGS; + } + __collector_log_write ("<setting signal=\"%u\" action=\"%s\"/>\n", sig, par); + return COL_ERROR_NONE; +} + +/*============================================================*/ +/* + * Routines for handling the log file + */ +static struct DataHandle *log_hndl = NULL; +static int log_initted = 0; +static int log_enabled = 0; + +static int +log_open () +{ + log_hndl = __collector_create_handle (SP_LOG_FILE); + if (log_hndl == NULL) + return COL_ERROR_LOG_OPEN; + log_initted = 1; + log_enabled = 1; + TprintfT (DBG_LT1, "log_open()\n"); + return COL_ERROR_NONE; +} + +static void +log_header_write (sp_origin_t origin) +{ + __collector_log_write ("<experiment %s=\"%d.%d\">\n", + SP_JCMD_VERSION, SUNPERF_VERNUM, SUNPERF_VERNUM_MINOR); + __collector_log_write ("<collector>%s</collector>\n", VERSION); + __collector_log_write ("</experiment>\n"); + + struct utsname sysinfo; + if (uname (&sysinfo) < 0) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\"/></event>\n", SP_JCMD_CERROR, COL_ERROR_SYSINFO, errno); + __collector_log_write ("<system>\n"); + } + else + { + long page_size = CALL_UTIL (sysconf)(_SC_PAGESIZE); + long npages = CALL_UTIL (sysconf)(_SC_PHYS_PAGES); + __collector_log_write ("<system hostname=\"%s\" arch=\"%s\" os=\"%s %s\" pagesz=\"%ld\" npages=\"%ld\">\n", + sysinfo.nodename, sysinfo.machine, sysinfo.sysname, sysinfo.release, page_size, npages); + } + + //YXXX Updating this section? Check similar cut/paste code in: + // collctrl.cc::Coll_Ctrl() + // collector.c::log_header_write() + // cpu_frequency.h::get_cpu_frequency() + + FILE *procf = CALL_UTIL (fopen)("/proc/cpuinfo", "r"); + if (procf != NULL) + { + char temp[1024]; + int cpu = -1; + while (CALL_UTIL (fgets)(temp, sizeof (temp), procf) != NULL) + { +#if ARCH(Intel) + if (__collector_strStartWith (temp, "processor") == 0) + { + char *val = CALL_UTIL (strchr)(temp, ':'); + cpu = val ? CALL_UTIL (atoi)(val + 1) : -1; + } + // else if ( __collector_strStartWith(temp, "model") == 0 + // && CALL_UTIL(strstr)(temp, "name") == 0) { + // char *val = CALL_UTIL(strchr)( temp, ':' ); + // int model = val ? CALL_UTIL(atoi)( val + 1 ) : -1; + // } + // else if ( __collector_strStartWith(temp, "cpu family") == 0 ) { + // char *val = CALL_UTIL(strchr)( temp, ':' ); + // int family = val ? CALL_UTIL(atoi)( val + 1 ) : -1; + // } + else if (__collector_strStartWith (temp, "cpu MHz") == 0) + { + char *val = CALL_UTIL (strchr)(temp, ':'); + int mhz = val ? CALL_UTIL (atoi)(val + 1) : 0; /* reading it as int is fine */ + (void) __collector_log_write (" <cpu id=\"%d\" clk=\"%d\"/>\n", cpu, mhz); + } +#elif ARCH(SPARC) + if (__collector_strStartWith (temp, "Cpu") == 0 && + temp[3] != '\0' && + __collector_strStartWith ((CALL_UTIL (strchr)(temp + 1, 'C')) ? CALL_UTIL (strchr)(temp + 1, 'C') : (temp + 4), "ClkTck") == 0) + { // sparc-Linux + char *val = CALL_UTIL (strchr)(temp, ':'); + int mhz = 0; + if (val) + { + unsigned long long freq; + (*__collector_sscanfp) (val + 2, "%llx", &freq); + mhz = (unsigned int) (((double) freq) / 1000000.0 + 0.5); + } + char *numend = CALL_UTIL (strchr)(temp + 1, 'C') ? CALL_UTIL (strchr)(temp + 1, 'C') : (temp + 4); + *numend = '\0'; + cpu = CALL_UTIL (atoi)(temp + 3); + __collector_log_write (" <cpu id=\"%d\" clk=\"%d\"/>\n", cpu, mhz); + } +#elif defined(__aarch64__) + if (__collector_strStartWith (temp, "processor") == 0) + { + char *val = CALL_UTIL (strchr)(temp, ':'); + cpu = val ? CALL_UTIL (atoi)(val + 1) : -1; + if (cpu != -1) + { + unsigned int mhz; + asm volatile("mrs %0, cntfrq_el0" : "=r" (mhz)); + __collector_log_write (" <cpu id=\"%d\" clk=\"%d\"/>\n", cpu, + mhz / 1000000); + } + } +#endif + } + CALL_UTIL (fclose)(procf); + } + __collector_log_write ("</system>\n"); + __collector_log_write ("<process pid=\"%d\"></process>\n", getpid ()); + __collector_log_write ("<process ppid=\"%d\"></process>\n", getppid ()); + __collector_log_write ("<process pgrp=\"%d\"></process>\n", getpgrp ()); + __collector_log_write ("<process sid=\"%d\"></process>\n", getsid (0)); + + /* XXX -- cwd commented out + It would be nice to get the current directory for the experiment, + but neither method below will work--the /proc method returns a + 0-length string, and using getcwd will break collect on /bin/sh + (as cuserid does) because of /bin/sh's private malloc + omazur: readlink seems to work on Linux + */ + /* write the current directory */ + char cwd[MAXPATHLEN + 1]; + int i = readlink ("/proc/self/cwd", cwd, sizeof (cwd)); + if (i >= 0) + { + cwd[i < sizeof (cwd) ? i : sizeof (cwd) - 1] = 0; + (void) __collector_log_write ("<process cwd=\"%s\"></process>\n", cwd); + } + (void) __collector_log_write ("<process wsize=\"%d\"></process>\n", (int) (8 * sizeof (void *))); + + ucontext_t ucp; + ucp.uc_stack.ss_sp = NULL; + ucp.uc_stack.ss_size = 0; + if (getcontext (&ucp) == 0) + { + (void) __collector_log_write ("<process stackbase=\"0x%lx\"></process>\n", + (unsigned long) ucp.uc_stack.ss_sp + ucp.uc_stack.ss_size); + } + + (void) __collector_log_write ("<process>%s</process>\n", + origin == SP_ORIGIN_FORK ? "(fork)" : exp_progspec); + __collector_libthread_T1 = 0; +} + +static void +log_pause (void) +{ + if (log_initted) + log_enabled = 0; +} + +static void +log_resume (void) +{ + if (log_initted) + log_enabled = 1; +} + +/* __collector_log_write -- write a line to the log file + * return value: + * 0 if OK + * 1 if error (in creating or extending the log file) + */ +int +__collector_log_write (char *format, ...) +{ + char buf[4096]; + va_list va; + int rc = 0; + static size_t loglen = 0; + + va_start (va, format); + char *bufptr = buf; + int sz = __collector_xml_vsnprintf (bufptr, sizeof (buf), format, va); + int allocated_sz = 0; + va_end (va); + if (sz >= sizeof (buf)) + { + /* Allocate a new buffer. + * We need this buffer only temporarily and locally. + * But don't use the thread stack + * since it already has buf + * and is unlikely to have additonal room for something even larger than buf. + */ + sz += 1; /* add the terminating null byte */ + bufptr = (char*) __collector_allocCSize (__collector_heap, sz, 0); + if (bufptr) + { + allocated_sz = sz; + va_start (va, format); + sz = __collector_xml_vsnprintf (bufptr, sz, format, va); + va_end (va); + } + } + int newlen = CALL_UTIL (strlen)(bufptr); + if (sz != newlen) + // no need to free bufptr if we're going to abort anyhow + abort (); + bufptr[newlen + 1] = 0; + loglen = loglen + newlen; + TprintfT (DBG_LT2, "__collector_log_write len=%ld, loglen=%ld %s", + (long) newlen, (long) loglen, bufptr); + if (log_enabled <= 0) + { +#if 0 + /* XXX suppress log_write messages with no log file open + * this is reached from SimApp dealing with the clock frequency, which it should + * not be doing. For now, don't write a message. + */ + CALL_UTIL (fprintf)(stderr, "__collector_log_write COL_ERROR_LOG_OPEN: %s", buf); +#endif + } + else + rc = __collector_write_string (log_hndl, bufptr, sz); + if (allocated_sz) + __collector_freeCSize (__collector_heap, (void *) bufptr, allocated_sz); + return rc; +} + +static void +log_close () +{ + log_enabled = 0; + log_initted = 0; + __collector_delete_handle (log_hndl); + log_hndl = NULL; +} + +/*============================================================*/ +/* + * Routines for handling the overview file + */ +static void +ovw_open () +{ + CALL_UTIL (strlcpy)(ovw_name, __collector_exp_dir_name, sizeof (ovw_name)); + CALL_UTIL (strlcat)(ovw_name, "/", sizeof (ovw_name)); + CALL_UTIL (strlcat)(ovw_name, SP_OVERVIEW_FILE, sizeof (ovw_name)); + int fd = CALL_UTIL (open)(ovw_name, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_OVWOPEN, errno, ovw_name); + return; + } + CALL_UTIL (close)(fd); + sample_mode = 1; +} + +static __inline__ void +timeval_to_timespec(struct timeval *tval, struct timespec *value) +{ + value->tv_nsec = tval->tv_usec * 1000; + value->tv_sec = tval->tv_sec; +} + +/* + * Resource usage. /proc/<pid>/usage /proc/<pid>/lwp/<lwpid>/lwpusage + */ +typedef struct prusage +{ + id_t pr_lwpid; /* lwp id. 0: process or defunct */ + int pr_count; /* number of contributing lwps */ + timestruc_t pr_tstamp; /* current time stamp */ + timestruc_t pr_create; /* process/lwp creation time stamp */ + timestruc_t pr_term; /* process/lwp termination time stamp */ + timestruc_t pr_rtime; /* total lwp real (elapsed) time */ + timestruc_t pr_utime; /* user level cpu time */ + timestruc_t pr_stime; /* system call cpu time */ + timestruc_t pr_ttime; /* other system trap cpu time */ + timestruc_t pr_tftime; /* text page fault sleep time */ + timestruc_t pr_dftime; /* data page fault sleep time */ + timestruc_t pr_kftime; /* kernel page fault sleep time */ + timestruc_t pr_ltime; /* user lock wait sleep time */ + timestruc_t pr_slptime; /* all other sleep time */ + timestruc_t pr_wtime; /* wait-cpu (latency) time */ + timestruc_t pr_stoptime; /* stopped time */ + timestruc_t filltime[6]; /* filler for future expansion */ + ulong_t pr_minf; /* minor page faults */ + ulong_t pr_majf; /* major page faults */ + ulong_t pr_nswap; /* swaps */ + ulong_t pr_inblk; /* input blocks */ + ulong_t pr_oublk; /* output blocks */ + ulong_t pr_msnd; /* messages sent */ + ulong_t pr_mrcv; /* messages received */ + ulong_t pr_sigs; /* signals received */ + ulong_t pr_vctx; /* voluntary context switches */ + ulong_t pr_ictx; /* involuntary context switches */ + ulong_t pr_sysc; /* system calls */ + ulong_t pr_ioch; /* chars read and written */ + ulong_t filler[10]; /* filler for future expansion */ +} prusage_t; + +static hrtime_t starttime = 0; + +static hrtime_t +ovw_write () +{ + if (sample_mode == 0) + return 0; + int fd; + int res; + struct prusage usage; + struct rusage rusage; + hrtime_t hrt, delta; + + /* Fill in the prusage structure with info from getrusage() */ + hrt = collector_interface.getHiResTime (); + if (starttime == 0) + starttime = hrt; + res = getrusage (RUSAGE_SELF, &rusage); + if (res != 0) + { + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_OVWREAD, errno, ovw_name); + return ( hrt); + } + + CALL_UTIL (memset)(&usage, 0, sizeof (struct prusage)); + usage.pr_lwpid = getpid (); + usage.pr_count = 1; + usage.pr_tstamp.tv_sec = hrt / NANOSEC; + usage.pr_tstamp.tv_nsec = hrt % NANOSEC; + usage.pr_create.tv_sec = starttime / NANOSEC; + usage.pr_create.tv_nsec = starttime % NANOSEC; + delta = hrt - starttime; + usage.pr_rtime.tv_sec = delta / NANOSEC; + usage.pr_rtime.tv_nsec = delta % NANOSEC; + timeval_to_timespec (&rusage.ru_utime, &usage.pr_utime); + timeval_to_timespec (&rusage.ru_stime, &usage.pr_stime); + + /* make sure that user- and system cpu time are not negative */ + if (ts2hrt (usage.pr_utime) < 0) + { + usage.pr_utime.tv_sec = 0; + usage.pr_utime.tv_nsec = 0; + } + if (ts2hrt (usage.pr_stime) < 0) + { + usage.pr_stime.tv_sec = 0; + usage.pr_stime.tv_nsec = 0; + } + + /* fill in other fields */ + usage.pr_minf = (ulong_t) rusage.ru_minflt; + usage.pr_majf = (ulong_t) rusage.ru_majflt; + usage.pr_nswap = (ulong_t) rusage.ru_nswap; + usage.pr_inblk = (ulong_t) rusage.ru_inblock; + usage.pr_oublk = (ulong_t) rusage.ru_oublock; + usage.pr_msnd = (ulong_t) rusage.ru_msgsnd; + usage.pr_mrcv = (ulong_t) rusage.ru_msgrcv; + usage.pr_sigs = (ulong_t) rusage.ru_nsignals; + usage.pr_vctx = (ulong_t) rusage.ru_nvcsw; + usage.pr_ictx = (ulong_t) rusage.ru_nivcsw; + + fd = CALL_UTIL (open)(ovw_name, O_WRONLY | O_APPEND); + if (fd < 0) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_OVWOPEN, errno, ovw_name); + return ( ts2hrt (usage.pr_tstamp)); + } + + CALL_UTIL (lseek)(fd, 0, SEEK_END); + res = CALL_UTIL (write)(fd, &usage, sizeof (usage)); + CALL_UTIL (close)(fd); + if (res != sizeof (usage)) + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_OVWWRITE, errno, ovw_name); + return (hrt); +} + +void +__collector_dlog (int tflag, int level, char *format, ...) +{ + if ((tflag & SP_DUMP_FLAG) == 0) + { + if (level > __collector_tracelevel) + return; + } + else if ((tflag & collector_debug_opt) == 0) + return; + + /* In most cases this allocation should suffice */ + int bufsz = CALL_UTIL (strlen)(format) + 128; + char *buf = (char*) alloca (bufsz); + char *p = buf; + int left = bufsz; + if ((tflag & SP_DUMP_NOHEADER) == 0) + { + p += CALL_UTIL (snprintf)(p, left, "P%d,L%02u,t%02lu", + (int) getpid (), + (unsigned int) __collector_lwp_self (), + __collector_no_threads ? 0 : __collector_thr_self ()); + left = bufsz - (p - buf); + if (tflag) + { + hrtime_t ts = GETRELTIME (); + p += CALL_UTIL (snprintf)(p, left, " %u.%09u ", (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC)); + } + else + p += CALL_UTIL (snprintf)(p, left, ": "); + left = bufsz - (p - buf); + } + + va_list va; + va_start (va, format); + int nbufsz = CALL_UTIL (vsnprintf)(p, left, format, va); + va_end (va); + + if (nbufsz >= left) + { + /* Allocate a new buffer */ + nbufsz += 1; /* add the terminating null byte */ + char *nbuf = (char*) alloca (nbufsz + (p - buf)); + __collector_memcpy (nbuf, buf, p - buf); + p = nbuf + (p - buf); + + va_start (va, format); + nbufsz = CALL_UTIL (vsnprintf)(p, nbufsz, format, va); + va_end (va); + buf = nbuf; + } + CALL_UTIL (write)(2, buf, CALL_UTIL (strlen)(buf)); +} + +/*============================================================*/ +#if ! ARCH(SPARC) /* !sparc-Linux */ +/* + * Routines for handling _exit and _Exit + */ +/*------------------------------------------------------------- _exit */ + +#define CALL_REAL(x) (*(int(*)())__real_##x) +#define NULL_PTR(x) ( __real_##x == NULL ) + +static void *__real__exit = NULL; /* libc only: _exit */ +static void *__real__Exit = NULL; /* libc only: _Exit */ +void _exit () __attribute__ ((weak, alias ("__collector_exit"))); +void _Exit () __attribute__ ((weak, alias ("__collector_Exit"))); + +void +__collector_exit (int status) +{ + if (NULL_PTR (_exit)) + { + __real__exit = dlsym (RTLD_NEXT, "_exit"); + if (__real__exit == NULL) + __real__exit = dlsym (RTLD_DEFAULT, "_exit"); + } + TprintfT (DBG_LT1, "__collector_exit() interposing @0x%p __real__exit\n", __real__exit); + __collector_terminate_expt (); + TprintfT (DBG_LT1, "__collector_exit(): experiment terminated\n"); + CALL_REAL (_exit)(status); // this will exit the process +} + +void +__collector_Exit (int status) +{ + if (NULL_PTR (_Exit)) + { + __real__Exit = dlsym (RTLD_NEXT, "_Exit"); + if (__real__Exit == NULL) + __real__Exit = dlsym (RTLD_DEFAULT, "_exit"); + } + TprintfT (DBG_LT1, "__collector_Exit() interposing @0x%p __real__Exit\n", __real__Exit); + __collector_terminate_expt (); + TprintfT (DBG_LT1, "__collector_Exit(): experiment terminated\n"); + CALL_REAL (_Exit)(status); // this will exit the process +} +#endif /* !sparc-Linux */ diff --git a/gprofng/libcollector/collector.h b/gprofng/libcollector/collector.h new file mode 100644 index 0000000..c54568d --- /dev/null +++ b/gprofng/libcollector/collector.h @@ -0,0 +1,236 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _COLLECTOR_H +#define _COLLECTOR_H + +#include <ucontext.h> +#include <signal.h> + +#include "gp-defs.h" +#include "data_pckts.h" +#include "libcol_util.h" +#include "collector_module.h" + +#define GETRELTIME() (__collector_gethrtime() - __collector_start_time) + +extern hrtime_t __collector_start_time; + +/* ========================================================== */ +/* ------- internal function prototypes ----------------- */ +/* These will not be exported from libcollector.so */ +struct DataHandle; +struct Heap; +extern struct DataHandle *__collector_create_handle (char*); +extern void __collector_delete_handle (struct DataHandle*); +extern int __collector_write_record (struct DataHandle*, Common_packet*); +extern int __collector_write_packet (struct DataHandle*, CM_Packet*); +extern int __collector_write_string (struct DataHandle*, char*, int); +extern FrameInfo __collector_get_frame_info (hrtime_t, int, void *); +extern FrameInfo __collector_getUID (CM_Array *arg, FrameInfo uid); +extern int __collector_getStackTrace (void *buf, int size, void *bptr, void *eptr, void *arg); +extern void *__collector_ext_return_address (unsigned level); +extern void __collector_mmap_fork_child_cleanup (); + +extern int __collector_ext_mmap_install (int); +extern int __collector_ext_mmap_deinstall (int); +extern int __collector_ext_update_map_segments (void); +extern int __collector_check_segment (unsigned long addr, + unsigned long *base, + unsigned long *end, int maxnretries); +extern int __collector_check_readable_segment (unsigned long addr, + unsigned long *base, + unsigned long *end, int maxnretries); +extern int __collector_ext_line_init (int * pfollow_this_experiment, + const char * progspec, + const char *progname); +extern int __collector_ext_line_install (char *, const char *); +extern void __collector_ext_line_close (); +extern void __collector_ext_unwind_init (int); +extern void __collector_ext_unwind_close (); +extern int __collector_ext_jstack_unwind (char*, int, ucontext_t *); +extern void __collector_ext_dispatcher_fork_child_cleanup (); +extern void __collector_ext_unwind_key_init (int isPthread, void * stack); +extern void __collector_ext_dispatcher_tsd_create_key (); +extern void __collector_ext_dispatcher_thread_timer_suspend (); +extern int __collector_ext_dispatcher_thread_timer_resume (); +extern int __collector_ext_dispatcher_install (); +extern void __collector_ext_dispatcher_suspend (); +extern void __collector_ext_dispatcher_restart (); +extern void __collector_ext_dispatcher_deinstall (); +extern void __collector_ext_usage_sample (Smpl_type type, char *name); +extern void __collector_ext_profile_handler (siginfo_t *, ucontext_t *); +extern int __collector_ext_clone_pthread (int (*fn)(void *), void *child_stack, int flags, void *arg, + va_list va /* pid_t *ptid, struct user_desc *tlspid_t *" ctid" */); + +/* D-light related functions */ +extern int __collector_sigprof_install (); +extern int __collector_ext_hwc_active (); +extern void __collector_ext_hwc_check (siginfo_t *, ucontext_t *); +extern int __collector_ext_hwc_lwp_init (); +extern void __collector_ext_hwc_lwp_fini (); +extern int __collector_ext_hwc_lwp_suspend (); +extern int __collector_ext_hwc_lwp_resume (); +extern int (*__collector_VM_ReadByteInstruction)(unsigned char *); +extern int (*__collector_omp_stack_trace)(char*, int, hrtime_t, void*); +extern hrtime_t (*__collector_gethrtime)(); +extern int (*__collector_mpi_stack_trace)(char*, int, hrtime_t); +extern int __collector_open_experiment (const char *exp, const char *par, sp_origin_t origin); +extern void __collector_suspend_experiment (char *why); +extern void __collector_resume_experiment (); +extern void __collector_clean_state (); +extern void __collector_close_experiment (); +extern void __collector_terminate_expt (); +extern void __collector_terminate_hook (); +extern void __collector_sample (char *name); +extern void __collector_pause (); +extern void __collector_pause_m (); +extern void __collector_resume (); +extern int collector_sigemt_sigaction (const struct sigaction*, + struct sigaction*); +extern int collector_sigchld_sigaction (const struct sigaction*, + struct sigaction*); + +extern int +__collector_log_write (char *format, ...) __attribute__ ((format (printf, 1, 2))); + +/* ------- internal global data ----------------- */ +/* These will not be exported from libcollector.so */ +extern struct Heap *__collector_heap; + +/* experiment state flag */ +typedef enum +{ + EXP_INIT, EXP_OPEN, EXP_PAUSED, EXP_CLOSED +} sp_state_t; +extern volatile sp_state_t __collector_expstate; + +/* global flag, defines whether target is threaded or not + * if set, put _lwp_self() for thread id instead of thr_self() + * in output packets; should be set before any data packets + * are written, i.e., before signal handlers are installed. + */ +extern int __collector_no_threads; +extern int __collector_libthread_T1; /* T1 or not T1 */ +extern int __collector_sample_sig; /* set to signal used to trigger a sample */ +extern int __collector_sample_sig_warn; /* if 1, warning given on target use */ +extern int __collector_pause_sig; /* set to signal used to toggle pause-resume */ +extern int __collector_pause_sig_warn; /* if 1, warning given on target use */ +extern hrtime_t __collector_delay_start; +extern int __collector_exp_active; + +/* global hrtime_t for next periodic sample */ +extern hrtime_t __collector_next_sample; +extern int __collector_sample_period; + +/* global hrtime_t for experiment termination (-t) */ +extern hrtime_t __collector_terminate_time; +extern int __collector_terminate_duration; +extern char __collector_exp_dir_name[]; +extern int __collector_java_mode; +extern int __collector_java_asyncgetcalltrace_loaded; +extern int __collector_jprofile_start_attach (); + +/* --------- information controlling debug tracing ------------- */ + +/* global flag, defines level of trace information */ +extern void __collector_dlog (int, int, char *, ...) __attribute__ ((format (printf, 3, 4))); + +#define STR(x) ((x) ? (x) : "NULL") + +// To set collector_debug_opt use: +// SP_COLLECTOR_DEBUG=4 ; export SP_COLLECTOR_DEBUG ; collect ... +enum +{ + SP_DUMP_TIME = 1, + SP_DUMP_FLAG = 2, + SP_DUMP_JAVA = 4, + SP_DUMP_NOHEADER = 8, + SP_DUMP_UNWIND = 16, + SP_DUMP_STACK = 32, +}; + +#ifndef DEBUG +#define DprintfT(flag, ...) +#define tprintf(...) +#define Tprintf(...) +#define TprintfT(...) + +#else +#define DprintfT(flag, ...) __collector_dlog(SP_DUMP_FLAG | (flag), 0, __VA_ARGS__ ) +#define tprintf(...) __collector_dlog( SP_DUMP_NOHEADER, __VA_ARGS__ ) +#define Tprintf(...) __collector_dlog( 0, __VA_ARGS__ ) +#define TprintfT(...) __collector_dlog( SP_DUMP_TIME, __VA_ARGS__ ) + +#endif /* DEBUG */ + +// To find the glibc version: +// objdump -T /lib*/*so /lib*/*/*.so | grep popen +// IMPORTANT: The GLIBC_* versions below must match those in mapfile.<variant> + #if ARCH(Aarch64) + #define SYS_LIBC_NAME "libc.so.6" + #define SYS_PTHREAD_CREATE_VERSION "GLIBC_2.17" + #define SYS_DLOPEN_VERSION "GLIBC_2.17" + #define SYS_POPEN_VERSION "GLIBC_2.17" + #define SYS_FOPEN_X_VERSION "GLIBC_2.17" + #define SYS_FGETPOS_X_VERSION "GLIBC_2.17" + +#elif ARCH(Intel) + #define SYS_LIBC_NAME "libc.so.6" + #define SYS_POSIX_SPAWN_VERSION "GLIBC_2.15" + #if WSIZE(32) + #define SYS_PTHREAD_CREATE_VERSION "GLIBC_2.1" + #define SYS_DLOPEN_VERSION "GLIBC_2.1" + #define SYS_POPEN_VERSION "GLIBC_2.1" + #define SYS_TIMER_X_VERSION "GLIBC_2.2" + #define SYS_FOPEN_X_VERSION "GLIBC_2.1" + #define SYS_FGETPOS_X_VERSION "GLIBC_2.2" + #define SYS_FGETPOS64_X_VERSION "GLIBC_2.2" + #define SYS_OPEN64_X_VERSION "GLIBC_2.2" + #define SYS_PREAD_X_VERSION "GLIBC_2.2" + #define SYS_PWRITE_X_VERSION "GLIBC_2.2" + #define SYS_PWRITE64_X_VERSION "GLIBC_2.2" + #else /* WSIZE(64) */ + #define SYS_PTHREAD_CREATE_VERSION "GLIBC_2.2.5" + #define SYS_DLOPEN_VERSION "GLIBC_2.2.5" + #define SYS_POPEN_VERSION "GLIBC_2.2.5" + #define SYS_TIMER_X_VERSION "GLIBC_2.3.3" + #define SYS_FOPEN_X_VERSION "GLIBC_2.2.5" + #define SYS_FGETPOS_X_VERSION "GLIBC_2.2.5" + #endif + + #elif ARCH(SPARC) + #define SYS_LIBC_NAME "libc.so.6" + #define SYS_DLOPEN_VERSION "GLIBC_2.1" + #if WSIZE(32) + #define SYS_PTHREAD_CREATE_VERSION "GLIBC_2.1" + #define SYS_POPEN_VERSION "GLIBC_2.1" + #define SYS_FOPEN_X_VERSION "GLIBC_2.1" + #define SYS_FGETPOS_X_VERSION "GLIBC_2.2" + #else /* WSIZE(64) */ + #define SYS_PTHREAD_CREATE_VERSION "GLIBC_2.2" + #define SYS_POPEN_VERSION "GLIBC_2.2" + #define SYS_TIMER_X_VERSION "GLIBC_2.3.3" + #define SYS_FOPEN_X_VERSION "GLIBC_2.2" + #define SYS_FGETPOS_X_VERSION "GLIBC_2.2" + #endif + #endif + +#endif diff --git a/gprofng/libcollector/collectorAPI.c b/gprofng/libcollector/collectorAPI.c new file mode 100644 index 0000000..8c9b920 --- /dev/null +++ b/gprofng/libcollector/collectorAPI.c @@ -0,0 +1,140 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* C and Fortran stubs for collector API */ + +#include "config.h" +#include <dlfcn.h> +#include "gp-defs.h" +#include "collectorAPI.h" +#include "gp-experiment.h" + +static void *__real_collector_sample = NULL; +static void *__real_collector_pause = NULL; +static void *__real_collector_resume = NULL; +static void *__real_collector_terminate_expt = NULL; +static void *__real_collector_func_load = NULL; +static void *__real_collector_func_unload = NULL; + +#define INIT_API if (init_API == 0) collectorAPI_initAPI() +#define NULL_PTR(x) (__real_##x == NULL) +#define CALL_REAL(x) (*(void(*)())__real_##x) +#define CALL_IF_REAL(x) INIT_API; if (!NULL_PTR(x)) CALL_REAL(x) + +static int init_API = 0; + +void +collectorAPI_initAPI (void) +{ + void *libcollector = dlopen (SP_LIBCOLLECTOR_NAME, RTLD_NOLOAD); + if (libcollector == NULL) + libcollector = RTLD_DEFAULT; + __real_collector_sample = dlsym (libcollector, "__collector_sample"); + __real_collector_pause = dlsym (libcollector, "__collector_pause"); + __real_collector_resume = dlsym (libcollector, "__collector_resume"); + __real_collector_terminate_expt = dlsym (libcollector, "__collector_terminate_expt"); + __real_collector_func_load = dlsym (libcollector, "__collector_func_load"); + __real_collector_func_unload = dlsym (libcollector, "__collector_func_unload"); + init_API = 1; +} + +/* initialization -- init section routine */ +static void collectorAPI_init () __attribute__ ((constructor)); + +static void +collectorAPI_init (void) +{ + collectorAPI_initAPI (); +} + +/* C API */ +void +collector_pause (void) +{ + CALL_IF_REAL (collector_pause)(); +} + +void +collector_resume (void) +{ + CALL_IF_REAL (collector_resume)(); +} + +void +collector_sample (const char *name) +{ + CALL_IF_REAL (collector_sample)(name); +} + +void +collector_terminate_expt (void) +{ + CALL_IF_REAL (collector_terminate_expt)(); +} + +void +collector_func_load (const char *name, const char *alias, const char *sourcename, + void *vaddr, int size, int lntsize, Lineno *lntable) +{ + CALL_IF_REAL (collector_func_load)(name, alias, sourcename, + vaddr, size, lntsize, lntable); +} + +void +collector_func_unload (void *vaddr) +{ + CALL_IF_REAL (collector_func_unload)(vaddr); +} + +/* Fortran API */ +void +collector_pause_ (void) +{ + CALL_IF_REAL (collector_pause)(); +} + +void +collector_resume_ (void) +{ + CALL_IF_REAL (collector_resume)(); +} + +void +collector_terminate_expt_ (void) +{ + CALL_IF_REAL (collector_terminate_expt)(); +} + +void +collector_sample_ (char *name, long name_length) +{ + INIT_API; + if (!NULL_PTR (collector_sample)) + { + char name_string[256]; + long length = sizeof (name_string) - 1; + if (name_length < length) + length = name_length; + for (long i = 0; i < length; i++) + name_string[i] = name[i]; + name_string[length] = '\0'; + CALL_REAL (collector_sample)(name_string); + } +} diff --git a/gprofng/libcollector/configure b/gprofng/libcollector/configure new file mode 100755 index 0000000..ed23350 --- /dev/null +++ b/gprofng/libcollector/configure @@ -0,0 +1,18081 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for gprofng 2.38.50. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + 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 + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1 + + 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\" ) || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 </dev/null +exec 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='gprofng' +PACKAGE_TARNAME='gprofng' +PACKAGE_VERSION='2.38.50' +PACKAGE_STRING='gprofng 2.38.50' +PACKAGE_BUGREPORT='' +PACKAGE_URL='' + +ac_unique_file="libcol_util.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +GPROFNG_VARIANT +CXXCPP +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +OBJDUMP +LN_S +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +SED +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +LIBTOOL +ac_ct_AR +AR +RANLIB +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +ac_ct_CXX +CXXFLAGS +CXX +EGREP +GREP +CPP +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +MAINT +MAINTAINER_MODE_FALSE +MAINTAINER_MODE_TRUE +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_maintainer_mode +enable_dependency_tracking +enable_shared +enable_static +with_pic +enable_fast_install +with_gnu_ld +enable_libtool_lock +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +CXX +CXXFLAGS +CCC +CXXCPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures gprofng 2.38.50 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/gprofng] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of gprofng 2.38.50:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-shared[=PKGS] build shared libraries [default=yes] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-pic try to use only PIC/non-PIC objects [default=use + both] + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +gprofng configure 2.38.50 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case <limits.h> declares $2. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by gprofng $as_me 2.38.50, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + +ac_aux_dir= +for ac_dir in ../.. "$srcdir"/../..; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in ../.. \"$srcdir\"/../.." "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +am__api_version='1.15' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='gprofng' + VERSION='2.38.50' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542> + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: <http://www.gnu.org/software/coreutils/>. + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } + # Check whether --enable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then : + enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 +$as_echo "$USE_MAINTAINER_MODE" >&6; } + if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + + + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdio.h> +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = xyes; then : + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if ${ac_cv_safe_to_define___extensions__+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar lib "link -lib" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar lib "link -lib" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 +$as_echo_n "checking the archiver ($AR) interface... " >&6; } +if ${am_cv_ar_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + am_cv_ar_interface=ar + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int some_variable = 0; +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=ar + else + am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=lib + else + am_cv_ar_interface=unknown + fi + fi + rm -f conftest.lib libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 +$as_echo "$am_cv_ar_interface" >&6; } + +case $am_cv_ar_interface in +ar) + ;; +lib) + # Microsoft lib, so override with the ar-lib wrapper script. + # FIXME: It is wrong to rewrite AR. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__AR in this case, + # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something + # similar. + AR="$am_aux_dir/ar-lib $AR" + ;; +unknown) + as_fn_error $? "could not determine $AR interface" "$LINENO" 5 + ;; +esac + + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.2.7a' +macro_revision='1.3134' + + + + + + + + + + + + + +ltmain="$ac_aux_dir/ltmain.sh" + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +# 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' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# 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*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 </dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break + ;; + *) + test "$with_gnu_ld" != yes && break + ;; + esac + fi + done + IFS="$lt_save_ifs" +else + lt_cv_path_LD="$LD" # Let the user override the test with a path. +fi +fi + +LD="$lt_cv_path_LD" +if test -n "$LD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 </dev/null` in +*GNU* | *'with BFD'*) + lt_cv_prog_gnu_ld=yes + ;; +*) + lt_cv_prog_gnu_ld=no + ;; +esac +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the nm to test. + lt_nm_to_check="$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 + 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=. + case "$lt_tmp_nm" in + */*|*\\*) tmp_nm="$lt_tmp_nm";; + *) tmp_nm="$ac_dir/$lt_tmp_nm";; + esac + 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 + case `"$tmp_nm" -B "$tmp_nm" 2>&1 | grep -v '^ *$' | sed '1q'` in + *$tmp_nm*) lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p "$tmp_nm" 2>&1 | grep -v '^ *$' | sed '1q'` in + *$tmp_nm*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test "$lt_cv_path_NM" != "no"; 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 + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # 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 + ;; + + 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"; 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"`func_fallback_echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 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 + +fi + +if test -n $lt_cv_sys_max_cmd_len ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 +$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 +$as_echo "$xsi_shell" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 +$as_echo_n "checking whether the shell understands \"+=\"... " >&6; } +lt_shell_append=no +( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 +$as_echo "$lt_shell_append" >&6; } + + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# 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 + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + darwin*) + if test "$GCC" = yes; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 +# which 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. + # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. + if ( test "$lt_cv_nm_interface" = "BSD nm" && 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 + lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' + 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 + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +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 Linux ELF. +linux* | k*bsd*-gnu | kopensolaris*-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*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; 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 + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } +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 + + + + + + + + + + + + +plugin_option= +plugin_names="liblto_plugin.so liblto_plugin-0.dll cyglto_plugin-0.dll" +for plugin in $plugin_names; do + plugin_so=`${CC} ${CFLAGS} --print-prog-name $plugin` + if test x$plugin_so = x$plugin; then + plugin_so=`${CC} ${CFLAGS} --print-file-name $plugin` + fi + if test x$plugin_so != x$plugin; then + plugin_option="--plugin $plugin_so" + break + fi +done + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +else + AR="$ac_cv_prog_AR" +fi + +test -z "$AR" && AR=ar +if test -n "$plugin_option"; then + if $AR --help 2>&1 | grep -q "\--plugin"; then + touch conftest.c + $AR $plugin_option rc conftest.a conftest.c + if test "$?" != 0; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Failed: $AR $plugin_option rc" >&5 +$as_echo "$as_me: WARNING: Failed: $AR $plugin_option rc" >&2;} + else + AR="$AR $plugin_option" + fi + rm -f conftest.* + fi +fi +test -z "$AR_FLAGS" && AR_FLAGS=cru + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: +if test -n "$plugin_option" && test "$RANLIB" != ":"; then + if $RANLIB --help 2>&1 | grep -q "\--plugin"; then + RANLIB="$RANLIB $plugin_option" + fi +fi + + + + + + +# 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 + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# 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 + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# 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 "$host_cpu" = ia64; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BCDRT]' + ;; +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 + +# 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 -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$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 -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/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 + # and D for any global 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};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print 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 + + # 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 { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 + (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && 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 +#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. */ +const struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$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_save_LIBS="$LIBS" + lt_save_CFLAGS="$CFLAGS" + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS="$lt_save_LIBS" + CFLAGS="$lt_save_CFLAGS" + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + + + + + + + + + + + + + + + + + + + + + + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test "x$enable_libtool_lock" != xno && 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 which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; 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 which ABI we are using. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test "$lt_cv_prog_gnu_ld" = yes; 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* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; 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" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +sparc*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) LD="${LD-ld} -m elf64_sparc" ;; + *) + 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" + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -f conftest && test ! -s conftest.err && test $_lt_result = 0 && $GREP forced_load conftest 2>&1 >/dev/null; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + 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 "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; 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 "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + + + + +# Set options + + + + enable_dlopen=no + + + enable_win32_dll=no + + + # Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; 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 +else + enable_shared=yes +fi + + + + + + + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; 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 +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; pic_mode="$withval" +else + pic_mode=default +fi + + +test -z "$pic_mode" && pic_mode=default + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; 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 +else + enable_fast_install=yes +fi + + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +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 "X${COLLECT_NAMES+set}" != Xset; 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 + +for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + +# 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/${ac_tool_prefix}file; then + lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" + 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 +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/file; then + lt_cv_path_MAGIC_CMD="$ac_dir/file" + 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 +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC="$CC" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$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);}' + + + + + + + +# 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 + +# 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 +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* + +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* + + +## 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_prog_compiler_no_builtin_flag= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # 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\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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 + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } + + if test "$GCC" = yes; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + lt_prog_compiler_pic='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-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_prog_compiler_pic='-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 + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + 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_prog_compiler_pic='-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_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + lt_prog_compiler_pic='-Xcompiler -fPIC' + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + 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). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-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_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ F* | *Sun*Fortran*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic" >&5 +$as_echo "$lt_prog_compiler_pic" >&6; } + + + + + + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" + # 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\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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 + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works" = xyes; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + 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>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test x"$lt_cv_prog_compiler_static_works" = xyes; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=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\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $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* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=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\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $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* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_flag_spec_ld= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # 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'. + exclude_expsyms='_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. + 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 "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + esac + + ld_shlibs=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 "$with_gnu_ld" = yes; 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 "$lt_use_gnu_ld_interface" = yes; 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 + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec='${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 + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v 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 "$host_cpu" != ia64; then + ld_shlibs=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 + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$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)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach <jrb3@best.com> says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='${wl}--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$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 (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; 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 + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${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. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='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 "$host_os" = linux-dietlibc; 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 "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='${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 + whole_archive_flag_spec='${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 + whole_archive_flag_spec= + tmp_sharedflag='--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 + whole_archive_flag_spec='${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' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='${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' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='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 + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec= + hardcode_libdir_flag_spec_ld='-rpath $libdir' + archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='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 $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $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 + ld_shlibs=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 + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** 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 + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test "$ld_shlibs" = no; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$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. + hardcode_minus_L=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test "$host_cpu" = ia64; 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 AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | 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 + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + 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. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='${wl}-f,' + + if test "$GCC" = yes; 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 + hardcode_direct=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 + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; 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 "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec='${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. + always_export_symbols=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`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 "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$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. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`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 "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec='${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. + no_undefined_flag=' ${wl}-bernotok' + allow_undefined_flag=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$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)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-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. + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=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. + archive_cmds='$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. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + fix_srcfile_path='`cygpath -w "$srcfile"`' + enable_shared_with_static_runtimes=yes + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds="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}" + module_expsym_cmds="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}" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=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*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_flag_spec_ld='+b $libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # 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) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -b" + 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>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test x"$lt_cv_prog_compiler__b" = xyes; then + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds='$CC -shared $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. + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo(void) {} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + archive_expsym_cmds='$CC -shared $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 +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + else + archive_cmds='$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' + archive_expsym_cmds='$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 + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$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 + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$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 + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_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' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$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' + archive_expsym_cmds='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 + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared ${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='' + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='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}' + archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='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 + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=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 "$GCC" = yes; then + whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='${wl}-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT 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. + no_undefined_flag='${wl}-z,text' + allow_undefined_flag='${wl}-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='${wl}-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test "$ld_shlibs" = no && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds 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. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test "$GCC" = yes; 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` + 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" + else + 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 + +case $host_os in +aix3*) + version_type=linux + 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 + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; 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 + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib<name>.so + # instead of lib<name>.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}' + else + # 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' + fi + 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%'\''`; test $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 + 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,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) + 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}' + + 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 + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # 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`' + + 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 + 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} $libname${shared_ext}' + 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 + 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=yes + 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 "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + 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 + 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 "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + 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 + ;; + +# This must be Linux ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux + 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 + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + 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 + + # Append ld.so.conf contents 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="/lib /usr/lib $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 + 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*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + 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 + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +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 + 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 "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux + 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 + 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=freebsd-elf + 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 "$with_gnu_ld" = yes; 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 + 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 + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test "X$hardcode_automatic" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct" != no && + # 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 "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test "$hardcode_action" = relink || + test "$inherit_rpath" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test "x$enable_dlopen" != xyes; 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && 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" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line 12016 "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include <stdio.h> + +#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 -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +void fnord () __attribute__((visibility("default"))); +#endif + +void fnord () { int i=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 { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line 12122 "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include <stdio.h> + +#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 -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +void fnord () __attribute__((visibility("default"))); +#endif + +void fnord () { int i=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 { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + 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 + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +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" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +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" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report which library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test "$can_build_shared" = "no" && 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 "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +else + _lt_caught_CXX_error=yes +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_flag_spec_ld_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +reload_flag_CXX=$reload_flag +reload_cmds_CXX=$reload_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$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 "$_lt_caught_CXX_error" != yes; 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. + + + + + + +# 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 + + + # save warnings/boilerplate of simple test code + 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* + + 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* + + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + 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++"} + compiler=$CC + compiler_CXX=$CC + for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + 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 </dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break + ;; + *) + test "$with_gnu_ld" != yes && break + ;; + esac + fi + done + IFS="$lt_save_ifs" +else + lt_cv_path_LD="$LD" # Let the user override the test with a path. +fi +fi + +LD="$lt_cv_path_LD" +if test -n "$LD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 </dev/null` in +*GNU* | *'with BFD'*) + lt_cv_prog_gnu_ld=yes + ;; +*) + lt_cv_prog_gnu_ld=no + ;; +esac +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_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 "$with_gnu_ld" = yes; then + archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$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' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${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 + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + 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. + archive_cmds_CXX='$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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; 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 + # need to do runtime linking. + 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 + ;; + 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. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='${wl}-f,' + + if test "$GXX" = yes; 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 + hardcode_direct_CXX=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 + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; 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 "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec_CXX='${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. + always_export_symbols_CXX=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_CXX='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`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 "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$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. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`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 "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_CXX='${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. + no_undefined_flag_CXX=' ${wl}-bernotok' + allow_undefined_flag_CXX=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + fi + archive_cmds_need_lc_CXX=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach <jrb3@best.com> says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-all-symbols' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$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 (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; 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 + ld_shlibs_CXX=no + fi + ;; + darwin* | rhapsody*) + + + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + else + whole_archive_flag_spec_CXX='' + fi + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds_CXX="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}" + module_expsym_cmds_CXX="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}" + if test "$lt_cv_apple_cc_single_mod" != "yes"; then + archive_cmds_CXX="\$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}" + archive_expsym_cmds_CXX="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 + ld_shlibs_CXX=no + fi + + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; + + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + + gnu*) + ;; + + haiku*) + archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs_CXX=yes + ;; + + hpux9*) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='${wl}-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=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 + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $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 "$GXX" = yes; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=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 + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$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 "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${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 + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${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. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='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++ + archive_cmds_CXX='$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. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + archive_cmds_CXX='$CC -shared -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 + archive_cmds_CXX='$CC -shared -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 + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-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. + archive_cmds_CXX='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' + archive_expsym_cmds_CXX='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"' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$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."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$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 + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5].* | *pgcpp\ [1-5].*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' + old_archive_cmds_CXX='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 | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='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 | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='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 | $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 + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='$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 + + hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${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++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$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 + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # 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 + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds_CXX='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 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='${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' + compiler_needs_object_CXX=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. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=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*) + ld_shlibs_CXX=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + ld_shlibs_CXX=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='${wl}-E' + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + ld_shlibs_CXX=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. + archive_cmds_CXX='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' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$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' + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$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' + archive_expsym_cmds_CXX='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' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac + + hardcode_libdir_separator_CXX=: + + # 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 "$GXX" = yes && test "$with_gnu_ld" = no; then + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + archive_cmds_CXX='$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' + ;; + *) + archive_cmds_CXX='$CC -shared -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 + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # 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 + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='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' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=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?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=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. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$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. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + no_undefined_flag_CXX=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared -nostdlib ${wl}-M $wl$lib.exp -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. + archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='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 -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 + + hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='${wl}-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT 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. + no_undefined_flag_CXX='${wl}-z,text' + allow_undefined_flag_CXX='${wl}-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ + '"$old_archive_cmds_CXX" + reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ + '"$reload_cmds_CXX" + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$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 + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test "$ld_shlibs_CXX" = no && can_build_shared=no + + GCC_CXX="$GXX" + LD_CXX="$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... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF + +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; 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 $p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + else + prev= + fi + + if test "$pre_test_object_deps_done" = no; then + case $p 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 "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX="${prev}${p}" + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${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 "$postdeps_CXX"; then + postdeps_CXX="${prev}${p}" + else + postdeps_CXX="${postdeps_CXX} ${prev}${p}" + fi + fi + ;; + + *.$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 "$pre_test_object_deps_done" = no; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX="$p" + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX="$p" + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext + +# PORTME: override above test on systems where it is broken +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. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac + + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } + + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + lt_prog_compiler_pic_CXX='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-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_prog_compiler_pic_CXX='-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 + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static_CXX= + ;; + 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_prog_compiler_pic_CXX=-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_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix[4-9]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--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 + ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+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_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-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_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd*) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_prog_compiler_pic_CXX" >&6; } + + + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" + # 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\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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 + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + 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>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then + : +else + lt_prog_compiler_static_CXX= +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=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\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $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* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=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\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + 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_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $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* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + 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 AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;/^.*[ ]__nm__/s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test "$ld_shlibs_CXX" = no && can_build_shared=no + +with_gnu_ld_CXX=$with_gnu_ld + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds_CXX 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. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc_CXX=no + else + lt_cv_archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } + archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +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 + +case $host_os in +aix3*) + version_type=linux + 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 + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; 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 + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib<name>.so + # instead of lib<name>.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}' + else + # 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' + fi + 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%'\''`; test $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 + 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,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) + 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}' + + ;; + 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 + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # 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`' + + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + 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} $libname${shared_ext}' + 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 + 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=yes + 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 "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + 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 + 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 "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + 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 + ;; + +# This must be Linux ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux + 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 + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + 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 + + # Append ld.so.conf contents 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="/lib /usr/lib $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 + 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*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + 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 + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +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 + 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 "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux + 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 + 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=freebsd-elf + 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 "$with_gnu_ld" = yes; 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 + 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 + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test "X$hardcode_automatic_CXX" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct_CXX" != no && + # 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 "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && + test "$hardcode_minus_L_CXX" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } + +if test "$hardcode_action_CXX" = relink || + test "$inherit_rpath_CXX" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + + fi # test -n "$compiler" + + CC=$lt_save_CC + 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 "$_lt_caught_CXX_error" != yes + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + +# Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; 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 +else + enable_shared=yes +fi + + + + + + +# Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; 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 +else + enable_static=no +fi + + + + + + + +if test "$enable_shared" != "yes"; then + as_fn_error $? "Cannot set --enable-shared for gprofng/libcollector." "$LINENO" 5 +fi + +GPROFNG_VARIANT=unknown +case "${target}" in + x86_64-*-linux*) + GPROFNG_VARIANT=amd64-Linux + ;; + i?86-*-linux*) + GPROFNG_VARIANT=intel-Linux + ;; + aarch64-*-linux*) + GPROFNG_VARIANT=aarch64-Linux + ;; +esac + + +ac_config_files="$ac_config_files Makefile" + +ac_config_headers="$ac_config_headers lib-config.h:../common/config.h.in" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + 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 + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by gprofng $as_me 2.38.50, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +gprofng config.status 2.38.50 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + + +# 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' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_ld='`$ECHO "$hardcode_libdir_flag_spec_ld" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +fix_srcfile_path='`$ECHO "$fix_srcfile_path" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' +predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' +predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' +postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' +reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' +reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_ld_CXX='`$ECHO "$hardcode_libdir_flag_spec_ld_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' +fix_srcfile_path_CXX='`$ECHO "$fix_srcfile_path_CXX" | $SED "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' + +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 SHELL \ +ECHO \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +AR \ +AR_FLAGS \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_wl \ +lt_prog_compiler_pic \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_flag_spec_ld \ +hardcode_libdir_separator \ +fix_srcfile_path \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +reload_flag_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_flag_spec_ld_CXX \ +hardcode_libdir_separator_CXX \ +fix_srcfile_path_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +sys_lib_dlsearch_path_spec \ +reload_cmds_CXX \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' +xsi_shell='$xsi_shell' +lt_shell_append='$lt_shell_append' + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile' + + + + + + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "lib-config.h") CONFIG_HEADERS="$CONFIG_HEADERS lib-config.h:../common/config.h.in" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' <confdefs.h | sed ' +s/'"$ac_delim"'/"\\\ +"/g' >>$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "libtool":C) + + # See if we are running on zsh, and set the options which 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 + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $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. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# 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 +# 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="CXX " + +# ### BEGIN LIBTOOL CONFIG + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method == "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# 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 +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# If ld is used when linking, flag to hardcode \$libdir into a binary +# during linking. This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# 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. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path=$lt_fix_srcfile_path + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_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 "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain="$ac_aux_dir/ltmain.sh" + + + # 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 '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + case $xsi_shell in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac +} + +# func_basename file +func_basename () +{ + func_basename_result="${1##*/}" +} + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}" +} + +# func_stripname 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). +func_stripname () +{ + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"} +} + +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=${1%%=*} + func_opt_split_arg=${1#*=} +} + +# func_lo2o object +func_lo2o () +{ + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac +} + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=${1%.*}.lo +} + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=$(( $* )) +} + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=${#1} +} + +_LT_EOF + ;; + *) # Bourne compatible functions. + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "${1}" | $SED "$basename"` +} + + +# func_stripname 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). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} + +# sed scripts: +my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q' +my_sed_long_arg='1s/^-[^=]*=//' + +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=`$ECHO "${1}" | $SED "$my_sed_long_opt"` + func_opt_split_arg=`$ECHO "${1}" | $SED "$my_sed_long_arg"` +} + +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` +} + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` +} + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "$@"` +} + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` +} + +_LT_EOF +esac + +case $lt_shell_append in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$1+=\$2" +} +_LT_EOF + ;; + *) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$1=\$$1\$2" +} + +_LT_EOF + ;; + esac + + + sed -n '/^# Generated shell functions inserted here/,$p' "$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" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# If ld is used when linking, flag to hardcode \$libdir into a binary +# during linking. This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# 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. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path=$lt_fix_srcfile_path_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + diff --git a/gprofng/libcollector/configure.ac b/gprofng/libcollector/configure.ac new file mode 100644 index 0000000..8acd66f --- /dev/null +++ b/gprofng/libcollector/configure.ac @@ -0,0 +1,60 @@ +dnl Process this file with autoconf to produce a configure script. +dnl +dnl Copyright (C) 2021 Free Software Foundation, Inc. +dnl +dnl This file is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 3 of the License, or +dnl (at your option) any later version. +dnl +dnl This program 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 +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; see the file COPYING3. If not see +dnl <http://www.gnu.org/licenses/>. + +m4_include([../../bfd/version.m4]) +AC_INIT([gprofng], BFD_VERSION) +AC_CONFIG_MACRO_DIRS([../../config ../..]) +AC_CONFIG_AUX_DIR(../..) +AM_INIT_AUTOMAKE +AM_MAINTAINER_MODE + +AC_CONFIG_SRCDIR(libcol_util.c) + +AC_USE_SYSTEM_EXTENSIONS +AC_PROG_CC +AC_PROG_CXX +AC_PROG_INSTALL +AC_PROG_RANLIB +AM_PROG_AR + +LT_INIT +AC_ENABLE_SHARED +AC_DISABLE_STATIC + +if test "$enable_shared" != "yes"; then + AC_MSG_ERROR([Cannot set --enable-shared for gprofng/libcollector.]) +fi + +GPROFNG_VARIANT=unknown +case "${target}" in + x86_64-*-linux*) + GPROFNG_VARIANT=amd64-Linux + ;; + i?86-*-linux*) + GPROFNG_VARIANT=intel-Linux + ;; + aarch64-*-linux*) + GPROFNG_VARIANT=aarch64-Linux + ;; +esac +AC_SUBST(GPROFNG_VARIANT) + +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_HEADERS([lib-config.h:../common/config.h.in]) +AC_OUTPUT + diff --git a/gprofng/libcollector/descendants.h b/gprofng/libcollector/descendants.h new file mode 100644 index 0000000..8fa18c8 --- /dev/null +++ b/gprofng/libcollector/descendants.h @@ -0,0 +1,81 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* Lineage events for process fork, exec, etc. */ + +#ifndef DESCENDANTS_H +#define DESCENDANTS_H + +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <alloca.h> +#include <assert.h> + +#include "gp-defs.h" +#include "gp-experiment.h" +#include "collector.h" +#include "memmgr.h" +#include "cc_libcollector.h" +#include "tsd.h" + +/* configuration, not changed after init. */ +typedef enum +{ + LM_DORMANT = -2, /* env vars preserved, not recording */ + LM_CLOSED = -1, /* env vars cleared, not recording */ + LM_TRACK_LINEAGE = 1, /* env vars preserved, recording */ +} line_mode_t; + +extern line_mode_t line_mode; +extern int user_follow_mode; +extern int java_mode; +extern int dbg_current_mode; /* for debug only */ +extern unsigned line_key; +extern char **sp_env_backup; + +#define INIT_REENTRANCE(x) ((x) = __collector_tsd_get_by_key (line_key)) +#define CHCK_REENTRANCE(x) (((INIT_REENTRANCE(x)) == NULL) || (*(x) != 0)) +#define PUSH_REENTRANCE(x) ((*(x))++) +#define POP_REENTRANCE(x) ((*(x))--) + +/* environment variables that must be forwarded to descendents */ +#define SP_COLLECTOR_PARAMS "SP_COLLECTOR_PARAMS" +#define SP_COLLECTOR_EXPNAME "SP_COLLECTOR_EXPNAME" +#define SP_COLLECTOR_FOLLOW_SPEC "SP_COLLECTOR_FOLLOW_SPEC" +#define SP_COLLECTOR_FOUNDER "SP_COLLECTOR_FOUNDER" +#define SP_PRELOAD_STRINGS "SP_COLLECTOR_PRELOAD" +#define LD_PRELOAD_STRINGS "LD_PRELOAD" +#define SP_LIBPATH_STRINGS "SP_COLLECTOR_LIBRARY_PATH" +#define LD_LIBPATH_STRINGS "LD_LIBRARY_PATH" +#define JAVA_TOOL_OPTIONS "JAVA_TOOL_OPTIONS" +#define COLLECTOR_JVMTI_OPTION "-agentlib:gp-collector" + +extern int __collector_linetrace_shutdown_hwcs_6830763_XXXX; +extern void __collector_env_unset (char *envp[]); +extern void __collector_env_save_preloads (); +extern char ** __collector_env_backup (); +extern void __collector_env_backup_free (); +extern void __collector_env_update (char *envp[]); +extern void __collector_env_print (char *label); +extern void __collector_env_printall (char *label, char *envp[]); +extern char ** __collector_env_allocate (char *const old_env[], int allocate_env); + +#endif diff --git a/gprofng/libcollector/dispatcher.c b/gprofng/libcollector/dispatcher.c new file mode 100644 index 0000000..f9a7de1 --- /dev/null +++ b/gprofng/libcollector/dispatcher.c @@ -0,0 +1,1263 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Central SIGPROF dispatcher to various module event handlers + * (REALPROF profile, HWC check, overview sample, manual sample) + */ + +#include "config.h" +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <ucontext.h> +#include <sys/param.h> +#include <sys/signal.h> +#include <sys/syscall.h> +#include <time.h> +#include <signal.h> + +#include "gp-defs.h" +#include "gp-experiment.h" +#include "collector.h" +#include "collector_module.h" +#include "tsd.h" +#include "hwcdrv.h" + + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LTT 0 // for interposition on GLIBC functions +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +static void collector_sigprof_dispatcher (int, siginfo_t*, void*); +static int init_interposition_intf (); +#include "memmgr.h" +static int collector_timer_create (timer_t * ptimerid); +static int collector_timer_settime (int period, timer_t timerid); +static int collector_timer_gettime (timer_t timerid); +static volatile int collector_sigprof_entries = 0; /* counter for SIGPROF signals in DISPATCH_TST mode */ +static timer_t collector_master_thread_timerid = NULL; +static collector_mutex_t collector_clone_libc_lock = COLLECTOR_MUTEX_INITIALIZER; +static unsigned dispatcher_key = COLLECTOR_TSD_INVALID_KEY; + +static void *__real_clone = NULL; +static void *__real_timer_create = NULL; +static void *__real_timer_settime = NULL; +static void *__real_timer_delete = NULL; +static void *__real_timer_gettime = NULL; +#if ARCH(Intel) && WSIZE(32) +static void *__real_pthread_create_2_1 = NULL; +static void *__real_pthread_create_2_0 = NULL; +#elif ARCH(Intel) && WSIZE(64) +static void *__real_timer_create_2_3_3 = NULL; +static void *__real_timer_create_2_2_5 = NULL; +#elif ARCH(SPARC) && WSIZE(64) +static void *__real_timer_create_2_3_3 = NULL; +static void *__real_timer_create_2_2 = NULL; +#endif + +/* Original SIGPROF handler which will be replaced with the dispatcher. Used + * to properly interact with libaio, which uses SIGPROF as its SIGAIOCANCEL. */ +static struct sigaction original_sigprof_handler; + +enum +{ + DISPATCH_NYI = -1, /* dispatcher not yet installed */ + DISPATCH_OFF = 0, /* dispatcher installed, but disabled */ + DISPATCH_ON = 1, /* dispatcher installed, and enabled */ + DISPATCH_TST = 2 /* dispatcher installed, and enabled in testing mode */ +}; + +static int dispatch_mode = DISPATCH_NYI; /* controls SIGPROF dispatching */ +static int itimer_period_requested = 0; /* dispatcher itimer period */ +static int itimer_period_actual = 0; /* actual dispatcher itimer period */ + +#define CALL_REAL(x) (*(int(*)())__real_##x) +#define NULL_PTR(x) ( __real_##x == NULL ) + +static void *__real_sigaction = NULL; +static void *__real_setitimer = NULL; +static void *__real_libc_setitimer = NULL; +static void *__real_sigprocmask = NULL; +static void *__real_thr_sigsetmask = NULL; +static void *__real_pthread_sigmask = NULL; +static void *__real_pthread_create = NULL; + +/* + * void collector_sigprof_dispatcher() + * + * Common SIGPROF event handler which dispatches events to appropriate + * module handlers, if they are active for this collection and due. + * Dispatch sequence, logic and handlers currently hardcoded in dispatcher. + */ +static void +collector_sigprof_dispatcher (int sig, siginfo_t *info, void *context) +{ + if (info == NULL || (info->si_code <= 0 && info->si_code != SI_TIMER)) + { + TprintfT (DBG_LT2, "collector_sigprof_dispatcher signal for %p\n", + original_sigprof_handler.sa_handler); + /* pass signal to previous handler */ + /* watch for recursion, SIG_IGN, and SIG_DFL */ + if (original_sigprof_handler.sa_handler == SIG_DFL) + __collector_SIGDFL_handler (SIGPROF); + else if (original_sigprof_handler.sa_handler != SIG_IGN && + original_sigprof_handler.sa_sigaction != &collector_sigprof_dispatcher) + { + (original_sigprof_handler.sa_sigaction)(sig, info, context); + TprintfT (DBG_LT2, "collector_sigprof_dispatcher handled\n"); + } + } + else if (dispatch_mode == DISPATCH_ON) + { +#if ARCH(SPARC) + ucontext_t uctxmem; + ucontext_t *uctx = &uctxmem; + uctx->uc_link = NULL; + /* 23340823 signal handler third argument should point to a ucontext_t */ + /* Convert sigcontext to ucontext_t on sparc-Linux */ + struct sigcontext *sctx = (struct sigcontext*) context; +#if WSIZE(32) + uctx->uc_mcontext.gregs[REG_PC] = sctx->si_regs.pc; + __collector_memcpy (&uctx->uc_mcontext.gregs[3], + sctx->si_regs.u_regs, + sizeof (sctx->si_regs.u_regs)); +#else + uctx->uc_mcontext.mc_gregs[MC_PC] = sctx->sigc_regs.tpc; + __collector_memcpy (&uctx->uc_mcontext.mc_gregs[3], + sctx->sigc_regs.u_regs, + sizeof (sctx->sigc_regs.u_regs)); +#endif /* WSIZE() */ + +#else /* not sparc-Linux */ + ucontext_t *uctx = (ucontext_t*) context; +#endif /* ARCH() */ + TprintfT (DBG_LT3, "collector_sigprof_dispatcher dispatching signal\n"); + + /* XXXX the order of these checks/activities may need adjustment */ + /* XXXX should also check (first) for a "cached" manual sample */ + /* HWC check for each LWP: required even if collection is paused */ + /* This should be first, otherwise it's likely to find the counters + * stopped due to an event/overflow during some of the other activities. + */ + /* XXXX HWC check performed every time (skipping if HWC profiling inactive) + * to avoid complexity of maintaining separate check times for each LWP + */ + __collector_ext_hwc_check (info, uctx); + + /* XXXX if sigemtpending, should perhaps skip __collector_ext_usage_sample + * (and get it next time through) + */ + + /* check for experiment past delay start */ + if (__collector_delay_start != 0) + { + hrtime_t now = __collector_gethrtime (); + if (__collector_delay_start < now) + { + TprintfT (0, "__collector_ext_usage_sample: now (%lld) > delay_start (%lld)\n", + (now - __collector_start_time), (__collector_delay_start - __collector_start_time)); + + /* resume the data collection */ + __collector_delay_start = 0; + __collector_resume (); + + /* don't take a periodic sample, just let the resume sample cover it */ + if (__collector_sample_period != 0) + { + /* this update should only be done for periodic samples */ + while (__collector_next_sample < now) + __collector_next_sample += ((hrtime_t) NANOSEC) * __collector_sample_period; + } + /* return; */ + } + } + /* check for periodic sampling */ + if (__collector_gethrtime () > __collector_next_sample) + __collector_ext_usage_sample (PERIOD_SMPL, "periodic"); + + /* check for experiment past termination time */ + if (__collector_exp_active && __collector_terminate_time != 0) + { + hrtime_t now = __collector_gethrtime (); + if (__collector_terminate_time < now) + { + TprintfT (0, "__collector_ext_usage_sample: now (%lld) > terminate_time (%lld); closing experiment\n", + (now - __collector_start_time), (__collector_terminate_time - __collector_start_time)); + /* close the experiment */ + __collector_close_experiment (); + } + } + + /* call the code to process the profile data, and generate the packet */ + /* (must always be called, otherwise profile data must be aggregated, + * but can be left till last, as already have the required data) + */ + __collector_ext_profile_handler (info, uctx); + } + else if (dispatch_mode == DISPATCH_TST) + { + collector_sigprof_entries++; + return; + } +} + +/* + * __collector_sigprof_install + */ +int +__collector_sigprof_install () +{ + TprintfT (DBG_LT2, "__collector_sigprof_install\n"); + struct sigaction oact; + if (__collector_sigaction (SIGPROF, NULL, &oact) != 0) + return COL_ERROR_DISPINIT; + if (oact.sa_sigaction == collector_sigprof_dispatcher) + /* signal handler is already in place; we are probably in a fork-child */ + TprintfT (DBG_LT1, "dispatcher: __collector_ext_dispatcher_install() collector_sigprof_dispatcher already installed\n"); + else + { + struct sigaction c_act; + CALL_UTIL (memset)(&c_act, 0, sizeof c_act); + sigemptyset (&c_act.sa_mask); + sigaddset (&c_act.sa_mask, HWCFUNCS_SIGNAL); /* block SIGEMT delivery in handler */ + c_act.sa_sigaction = collector_sigprof_dispatcher; + c_act.sa_flags = SA_RESTART | SA_SIGINFO; + if (__collector_sigaction (SIGPROF, &c_act, &original_sigprof_handler)) + return COL_ERROR_DISPINIT; + } + dispatch_mode = DISPATCH_OFF; /* don't dispatch yet */ + TprintfT (DBG_LT2, "__collector_sigprof_install done\n"); + return COL_ERROR_NONE; +} + +/* + * void __collector_ext_dispatcher_tsd_create_key() + * + * create tsd key for dispatcher + */ +void +__collector_ext_dispatcher_tsd_create_key () +{ + dispatcher_key = __collector_tsd_create_key (sizeof (timer_t), NULL, NULL); +} +/* + * int __collector_ext_dispatcher_install() + * + * installs a common handler/dispatcher (and itimer) for SIGPROF events + */ +int +__collector_ext_dispatcher_install () +{ + int timer_period; + TprintfT (DBG_LT2, "__collector_ext_dispatcher_install\n"); + + /* check period set for interval timer, which will be used as the basis + * for all timed activities: if not set, no role for SIGPROF dispatcher + */ + if (itimer_period_requested <= 0) + { + TprintfT (DBG_LT1, "No interval timer set: skipping dispatcher install!\n"); + return COL_ERROR_NONE; /* no itimer/dispatcher required */ + } + + /* check for an existing interval timer */ + if (collector_master_thread_timerid == NULL) + if (collector_timer_create (&collector_master_thread_timerid) < 0) + return COL_ERROR_ITMRINIT; + timer_t *timeridptr = __collector_tsd_get_by_key (dispatcher_key); + if (timeridptr != NULL) + *timeridptr = collector_master_thread_timerid; // store for per thread timer stop/start + TprintfT (DBG_LT3, "__collector_ext_dispatcher_install: collector_master_thread_timerid=%p\n", + collector_master_thread_timerid); + timer_period = collector_timer_gettime (collector_master_thread_timerid); + if (timer_period > 0) + { + TprintfT (DBG_LT1, "Overriding app-set interval timer with period %d\n", timer_period); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d->%d</event>\n", + SP_JCMD_CWARN, COL_WARN_ITMRPOVR, timer_period, itimer_period_requested); + } + /* install the interval timer used for all timed activities */ + if (collector_timer_settime (itimer_period_requested, collector_master_thread_timerid) < 0) + return COL_ERROR_ITMRINIT; + TprintfT (DBG_LT2, "__collector_ext_dispatcher_install done\n"); + dispatch_mode = DISPATCH_ON; /* activate SIGPROF dispatch to event handlers */ + return COL_ERROR_NONE; +} + +int +__collector_sigaction (int sig, const struct sigaction *nact, struct sigaction *oact) +{ + TprintfT (DBG_LT1, "__collector_sigaction: %d, %p\n", sig, nact ? nact->sa_sigaction : NULL); + if (NULL_PTR (sigaction)) + init_interposition_intf (); + + /* Whether we change the signal handler in the kernel + * or not make sure the real sigaction is aware about + * our new handler (6227565) + */ + return CALL_REAL (sigaction)(sig, nact, oact); +} + +/* + * We have special dispatchers for SIGPROF and HWCFUNCS_SIGNAL to + * decide whether the signal was intended for us or for the user. + * One special case is SIGDFL, in which case we don't have a + * user-function address to call. If the user did indeed set + * default disposition for one of these signals and sent that + * signal, we honor that action, even though it will lead to + * termination. + */ +void +__collector_SIGDFL_handler (int sig) +{ + /* remove our dispatcher, replacing it with the default disposition */ + struct sigaction act; + CALL_UTIL (memset)(&act, 0, sizeof (act)); + act.sa_handler = SIG_DFL; + if (__collector_sigaction (sig, &act, NULL)) + { + /* XXXXXX what are we supposed to do here? we're committing suicide anyhow */ + } + /* resend the signal we intercepted earlier */ + // XXXX Bug 18177509 - additional sigprof signal kills target program + kill (getpid (), sig); +} + +/* + * suspend/resume timer per thread + */ +void +__collector_ext_dispatcher_thread_timer_suspend () +{ + timer_t * timeridptr = __collector_tsd_get_by_key (dispatcher_key); + if (timeridptr != NULL && *timeridptr != NULL) + (void) collector_timer_settime (0, *timeridptr); + return; +} + +int +__collector_ext_dispatcher_thread_timer_resume () +{ + timer_t * timeridptr = __collector_tsd_get_by_key (dispatcher_key); + if (timeridptr == NULL) + return -1; + if (*timeridptr == NULL) + { // timer id not initialized yet + TprintfT (DBG_LT2, "__collector_ext_dispatcher_thread_timer_resume: timer not initialized yet, create it\n"); + if (collector_timer_create (timeridptr) == -1) + { + TprintfT (0, "__collector_ext_dispatcher_thread_timer_resume(): WARNING: No timer created\n"); + return -1; + } + } + return collector_timer_settime (itimer_period_requested, *timeridptr); +} + +void +__collector_ext_dispatcher_suspend () +{ + TprintfT (DBG_LT2, "__collector_ext_dispatcher_suspend\n"); + if (dispatch_mode == DISPATCH_NYI) + { + TprintfT (0, "__collector_ext_dispatcher_suspend(): WARNING: No dispatcher installed\n"); + return; + } + + /* disable SIGPROF dispatching */ + dispatch_mode = DISPATCH_OFF; + + /* disable the interval timer; ignore any failures */ + __collector_ext_dispatcher_thread_timer_suspend (); + return; +} + +void +__collector_ext_dispatcher_restart () +{ + TprintfT (DBG_LT2, "__collector_ext_dispatcher_restart(ip=%d)\n", itimer_period_requested); + if (dispatch_mode == DISPATCH_NYI) + { + TprintfT (0, "__collector_ext_dispatcher_restart(): WARNING: No dispatcher installed\n"); + return; + } + + /* restart the interval timer used for all timed activities */ + if (__collector_ext_dispatcher_thread_timer_resume () == 0) + dispatch_mode = DISPATCH_ON; /* re-activate SIGPROF dispatch to handlers */ + return; +} +/* + * void __collector_ext_dispatcher_deinstall() + * + * If installed, disables SIGPROF dispatch and interval timer. + * Includes checks for last SIGPROF dispatch time, interval timer period, + * and currently installed SIGPROF handler, with appropriate warnings logged. + * The dispatcher remains installed to handle pending collector SIGPROFs and + * forward non-collector SIGPROFs to the application's handler(s). + * If the decision is ever made actually to deinstall the dispatcher, + * consider bug 4183714 and what to do about any possible pending + * SIGPROFs. + */ + +void +__collector_ext_dispatcher_deinstall () +{ + TprintfT (DBG_LT1, "__collector_ext_dispatcher_deinstall()\n"); + if (dispatch_mode == DISPATCH_NYI) + { + TprintfT (0, "__collector_ext_dispatcher_deinstall(): WARNING: No dispatcher installed\n"); + return; + } + dispatch_mode = DISPATCH_OFF; /* disable SIGPROF dispatching */ + + /* verify that interval timer is still installed with expected period */ + int timer_period = collector_timer_gettime (collector_master_thread_timerid); + if (timer_period != itimer_period_actual) + { + TprintfT (DBG_LT2, "dispatcher: Collector interval timer period changed %d -> %d\n", + itimer_period_actual, timer_period); + if ((itimer_period_actual >= (timer_period + timer_period / 10)) || + (itimer_period_actual <= (timer_period - timer_period / 10))) + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", + SP_JCMD_CWARN, COL_WARN_ITMRREP, + itimer_period_actual, timer_period); + else + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", + SP_JCMD_COMMENT, COL_WARN_PROFRND, + itimer_period_actual, timer_period); + } + + /* Verify that SIGPROF dispatcher is still installed. + * (still required with sigaction interposition and management, + * since interposition is not done for attach experiments) + */ + struct sigaction curr; + if (__collector_sigaction (SIGPROF, NULL, &curr) == -1) + TprintfT (0, "ERROR: dispatcher sigaction check failed: errno=%d\n", errno); + else if (curr.sa_sigaction != collector_sigprof_dispatcher) + { + TprintfT (0, "ERROR: collector dispatcher replaced by %p!\n", curr.sa_handler); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%p</event>\n", + SP_JCMD_CWARN, COL_WARN_SIGPROF, curr.sa_handler); + } + else + TprintfT (DBG_LT2, "collector dispatcher integrity verified!\n"); + + /* disable the interval timer; ignore any failures */ + if (collector_master_thread_timerid != NULL) + { + (void) CALL_REAL (timer_delete)(collector_master_thread_timerid); + collector_master_thread_timerid = NULL; + } + dispatcher_key = COLLECTOR_TSD_INVALID_KEY; + itimer_period_requested = 0; + itimer_period_actual = 0; +} + +/* + * void __collector_ext_dispatcher_fork_child_cleanup() + * + * delete timer, clear timer interval + */ +void +__collector_ext_dispatcher_fork_child_cleanup () +{ + if (collector_master_thread_timerid != NULL) + { + (void) CALL_REAL (timer_delete)(collector_master_thread_timerid); + collector_master_thread_timerid = NULL; + } + __collector_mutex_init (&collector_clone_libc_lock); + dispatcher_key = COLLECTOR_TSD_INVALID_KEY; + itimer_period_requested = 0; + itimer_period_actual = 0; +} +/* + * int __collector_ext_itimer_set (int rperiod) + * + * set itimer period, if not yet set to a positive number of microseconds, + * (after rounding to sys_resolution if necessary) and return its value + */ +int +__collector_ext_itimer_set (int rperiod) +{ + int period; + /* if rperiod is negative, force setting */ + if (rperiod < 0) + { + itimer_period_actual = 0; + period = -rperiod; + } + else + period = rperiod; + + // ignore SIGPROF while testing itimer interval setting + int saved = dispatch_mode; + dispatch_mode = DISPATCH_OFF; + if (collector_timer_create (&collector_master_thread_timerid) == -1) + { + TprintfT (0, "__collector_ext_itimer_set(): WARNING: No timer created\n"); + return itimer_period_actual; + } + if (collector_timer_settime (period, collector_master_thread_timerid) == 0) + { + itimer_period_actual = collector_timer_gettime (collector_master_thread_timerid); + (void) collector_timer_settime (0, collector_master_thread_timerid); /* XXXX unset for now */ + itimer_period_requested = period; + if (itimer_period_requested != itimer_period_actual) + { + TprintfT (DBG_LT2, " itimer period %d adjusted to %d\n", + itimer_period_requested, itimer_period_actual); + // (void) __collector_log_write("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", + // SP_JCMD_CWARN, COL_WARN_PROFRND, itimer_period_requested, itimer_period_actual); + } + else + TprintfT (DBG_LT2, " itimer period %d accepted\n", period); + } + + // restore dispatching SIGPROF handler + dispatch_mode = saved; + TprintfT (0, "__collector_ext_itimer_set(%d), requested=%d, actual=%d)\n", + rperiod, itimer_period_requested, itimer_period_actual); + return (itimer_period_actual); +} + +static int +collector_timer_gettime (timer_t timerid) +{ + int timer_period; + struct itimerspec itimer; + if (timerid == NULL) + return (0); // timer was not initialized + if (CALL_REAL (timer_gettime)(timerid, &itimer) == -1) + { + /* this should never reasonably fail, so not worth logging */ + TprintfT (DBG_LT1, "WARNING: timer_gettime failed: errno=%d\n", errno); + return (-1); + } + timer_period = ((itimer.it_interval.tv_sec * NANOSEC) + + itimer.it_interval.tv_nsec) / 1000; + TprintfT (DBG_LT2, "collector_timer_gettime (period=%d)\n", timer_period); + return (timer_period); +} + +static int +collector_timer_create (timer_t * ptimerid) +{ + struct sigevent sigev; + if (NULL_PTR (timer_create)) + init_interposition_intf (); + TprintfT (DBG_LT2, "collector_timer_settime(): timer_create is %p\n", __real_timer_create); + sigev.sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL; + sigev.sigev_signo = SIGPROF; + sigev.sigev_value.sival_ptr = ptimerid; + sigev._sigev_un._tid = __collector_gettid (); + if (CALL_REAL (timer_create)(CLOCK_THREAD_CPUTIME_ID, &sigev, ptimerid) == -1) + { + TprintfT (DBG_LT2, "collector_timer_settime() failed! errno=%d\n", errno); + return -1; + } + return 0; +} + +static int +collector_timer_settime (int period, timer_t timerid) +{ + struct itimerspec itimer; + if (NULL_PTR (timer_settime)) + init_interposition_intf (); + TprintfT (DBG_LT2, "collector_timer_settime(period=%d)\n", period); + time_t NPM = 1000; + itimer.it_interval.tv_sec = NPM * period / NANOSEC; + itimer.it_interval.tv_nsec = (NPM * period) % NANOSEC; + itimer.it_value = itimer.it_interval; + if (CALL_REAL (timer_settime)(timerid, 0, &itimer, NULL) == -1) + { + TprintfT (DBG_LT2, "collector_timer_settime(%d) failed! errno=%d\n", period, errno); + return -1; + } + return 0; +} + +static void +protect_profiling_signals (sigset_t* lset) +{ + static unsigned int protected_sigprof = 0; + static unsigned int protected_sigemt = 0; + // T1 relies on thread signal masking, so best not to mess with it: + // T1 users have already been warned about the dangers of its use + if (__collector_libthread_T1) + return; + if (sigismember (lset, SIGPROF) && (dispatch_mode == DISPATCH_ON)) + { + TprintfT (0, "WARNING: ignoring %s block while profiling\n", "SIGPROF"); + if (protected_sigprof == 0) + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CWARN, COL_WARN_SIGMASK, "SIGPROF"); + sigdelset (lset, SIGPROF); + protected_sigprof++; + } + if (sigismember (lset, HWCFUNCS_SIGNAL) && __collector_ext_hwc_active ()) + { + TprintfT (0, "WARNING: ignoring %s block while profiling\n", "SIGEMT"); + if (protected_sigemt == 0) + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CWARN, COL_WARN_SIGMASK, HWCFUNCS_SIGNAL_STRING); + sigdelset (lset, HWCFUNCS_SIGNAL); + protected_sigemt++; + } +} + +#define SYS_SETITIMER_NAME "setitimer" +#define SYS_SIGACTION_NAME "sigaction" +#define SYS_SIGPROCMASK_NAME "sigprocmask" +#define SYS_PTHREAD_SIGMASK "pthread_sigmask" +#define SYS_THR_SIGSETMASK "thr_sigsetmask" + +static int +init_interposition_intf () +{ + if (__collector_dlsym_guard) + return 1; + void *dlflag; + /* Linux requires RTLD_LAZY, Solaris can do just RTLD_NOLOAD */ + void *handle = dlopen (SYS_LIBC_NAME, RTLD_LAZY | RTLD_NOLOAD); + +#if ARCH(SPARC) && WSIZE(64) + /* dlopen a bogus path to avoid CR 23608692 */ + dlopen ("/bogus_path_for_23608692_workaround/", RTLD_LAZY | RTLD_NOLOAD); +#endif + __real_setitimer = dlsym (RTLD_NEXT, SYS_SETITIMER_NAME); + + if (__real_setitimer == NULL) + { + __real_setitimer = dlsym (RTLD_DEFAULT, SYS_SETITIMER_NAME); + if (__real_setitimer == NULL) + { + TprintfT (DBG_LT2, "init_interposition_intf() setitimer not found\n"); + return 1; + } + dlflag = RTLD_DEFAULT; + } + else + dlflag = RTLD_NEXT; + + TprintfT (DBG_LT2, "init_interposition_intf() using RTLD_%s\n", + (dlflag == RTLD_DEFAULT) ? "DEFAULT" : "NEXT"); + TprintfT (DBG_LT2, "@%p __real_setitimer\n", __real_setitimer); + + __real_sigaction = dlsym (dlflag, SYS_SIGACTION_NAME); + TprintfT (DBG_LT2, "@%p __real_sigaction\n", __real_sigaction); + + /* also explicitly get libc.so/setitimer (as a backup) */ + __real_libc_setitimer = dlsym (handle, SYS_SETITIMER_NAME); + TprintfT (DBG_LT2, "@%p __real_libc_setitimer\n", __real_libc_setitimer); + + __real_sigprocmask = dlsym (dlflag, SYS_SIGPROCMASK_NAME); + TprintfT (DBG_LT2, "@%p __real_sigprocmask\n", __real_sigprocmask); + + __real_thr_sigsetmask = dlsym (dlflag, SYS_THR_SIGSETMASK); + TprintfT (DBG_LT2, "@%p __real_thr_sigsetmask\n", __real_thr_sigsetmask); + + __real_pthread_sigmask = dlsym (dlflag, SYS_PTHREAD_SIGMASK); + TprintfT (DBG_LT2, "@%p __real_pthread_sigmask\n", __real_pthread_sigmask); + +#if ARCH(Aarch64) + __real_pthread_create = dlvsym (dlflag, "pthread_create", SYS_PTHREAD_CREATE_VERSION); + __real_timer_create = dlsym (dlflag, "timer_create"); + __real_timer_settime = dlsym (dlflag, "timer_settime"); + __real_timer_delete = dlsym (dlflag, "timer_delete"); + __real_timer_gettime = dlsym (dlflag, "timer_gettime"); +#else + __real_pthread_create = dlvsym (dlflag, "pthread_create", SYS_PTHREAD_CREATE_VERSION); + TprintfT (DBG_LT2, "[%s] @%p __real_pthread_create\n", SYS_PTHREAD_CREATE_VERSION, __real_pthread_create); + __real_timer_create = dlvsym (dlflag, "timer_create", SYS_TIMER_X_VERSION); + TprintfT (DBG_LT2, "init_lineage_intf() [%s] @0x%p __real_timer_create\n", SYS_TIMER_X_VERSION, __real_timer_create); + __real_timer_settime = dlvsym (dlflag, "timer_settime", SYS_TIMER_X_VERSION); + TprintfT (DBG_LT2, "init_lineage_intf() [%s] @0x%p __real_timer_settime\n", SYS_TIMER_X_VERSION, __real_timer_settime); + __real_timer_delete = dlvsym (dlflag, "timer_delete", SYS_TIMER_X_VERSION); + TprintfT (DBG_LT2, "init_lineage_intf() [%s] @0x%p __real_timer_delete\n", SYS_TIMER_X_VERSION, __real_timer_delete); + __real_timer_gettime = dlvsym (dlflag, "timer_gettime", SYS_TIMER_X_VERSION); + TprintfT (DBG_LT2, "init_lineage_intf() [%s] @0x%p __real_timer_gettime\n", SYS_TIMER_X_VERSION, __real_timer_gettime); + __real_clone = dlsym (dlflag, "clone"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_clone\n", __real_clone); +#if ARCH(Intel) && WSIZE(32) + __real_pthread_create_2_1 = __real_pthread_create; + __real_pthread_create_2_0 = dlvsym (dlflag, "pthread_create", "GLIBC_2.0"); +#elif ARCH(Intel) && WSIZE(64) + __real_timer_create_2_3_3 = __real_timer_create; + __real_timer_create_2_2_5 = dlvsym (dlflag, "timer_create", "GLIBC_2.2.5"); +#elif ARCH(SPARC) && WSIZE(64) + __real_timer_create_2_3_3 = __real_timer_create; + __real_timer_create_2_2 = dlvsym (dlflag, "timer_create", "GLIBC_2.2"); +#endif /* ARCH() && SIZE() */ +#endif + return 0; +} + + +/*------------------------------------------------------------- sigaction */ + +/* NB: need a global interposing function called "sigaction" */ +int +sigaction (int sig, const struct sigaction *nact, struct sigaction *oact) +{ + int ret = 0; + int err = 0; + if (NULL_PTR (sigaction)) + err = init_interposition_intf (); + if (err) + return -1; + TprintfT (DBG_LT3, "sigaction(sig=%02d, nact=%p) interposing\n", sig, nact); + if (sig == SIGPROF && dispatch_mode != DISPATCH_NYI) + { + if (oact != NULL) + { + oact->sa_handler = original_sigprof_handler.sa_handler; + oact->sa_mask = original_sigprof_handler.sa_mask; + oact->sa_flags = original_sigprof_handler.sa_flags; + } + if (nact != NULL) + { + original_sigprof_handler.sa_handler = nact->sa_handler; + original_sigprof_handler.sa_mask = nact->sa_mask; + original_sigprof_handler.sa_flags = nact->sa_flags; + TprintfT (DBG_LT1, "dispatcher: new sigaction(sig=%02d) set\n", sig); + } + } + else if (sig == HWCFUNCS_SIGNAL) + ret = collector_sigemt_sigaction (nact, oact); + else + { + if (sig != SIGCHLD || collector_sigchld_sigaction (nact, oact)) + ret = CALL_REAL (sigaction)(sig, nact, oact); + TprintfT (DBG_LT3, "Real sigaction(sig=%02d) returned %d (oact=%p)\n", + sig, ret, oact); + /* but check for other important signals */ + /* check for sample and pause/resume signals; give warning once, if need be */ + if ((sig == __collector_sample_sig) && (__collector_sample_sig_warn == 0)) + { + /* give user a warning */ + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d</event>\n", + SP_JCMD_CWARN, COL_WARN_SAMPSIGUSED, __collector_sample_sig); + __collector_sample_sig_warn = 1; + } + if ((sig == __collector_pause_sig) && (__collector_pause_sig_warn == 0)) + { + /* give user a warning */ + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d</event>\n", + SP_JCMD_CWARN, COL_WARN_PAUSESIGUSED, __collector_pause_sig); + __collector_pause_sig_warn = 1; + } + } + TprintfT (DBG_LT3, "sigaction() returning %d (oact=%p)\n", ret, oact); + return ret; +} + +/* + * In addition to interposing on sigaction(), should we also interpose + * on other important signal functions like signal() or sigset()? + * - On Solaris, those other functions apparently call sigaction(). + * So, we only have to interpose on it. + * - On Linux, we should perhaps interpose on these other functions, + * but they are less portable than sigaction() and deprecated or even obsolete. + * So, we interpose, but don't overly worry about doing a good job. + */ +sighandler_t +signal (int sig, sighandler_t handler) +{ + struct sigaction nact; + struct sigaction oact; + TprintfT (DBG_LT3, "signal(sig=%02d, handler=%p) interposing\n", sig, handler); + sigemptyset (&nact.sa_mask); + nact.sa_handler = handler; + nact.sa_flags = SA_RESTART; + if (sigaction (sig, &nact, &oact)) + return SIG_ERR; + TprintfT (DBG_LT3, "signal() returning %p\n", oact.sa_handler); + return oact.sa_handler; +} + +sighandler_t +sigset (int sig, sighandler_t handler) +{ + TprintfT (DBG_LT3, "sigset(sig=%02d, handler=%p) interposing\n", sig, handler); + return signal (sig, handler); +} + +/*------------------------------------------------------------- timer_create */ + +// map interposed symbol versions +#if WSIZE(64) +#if ARCH(SPARC) || ARCH(Intel) +static int +__collector_timer_create_symver (int(real_timer_create) (), clockid_t clockid, struct sigevent *sevp, + timer_t *timerid); + +int +__collector_timer_create_2_3_3 (clockid_t clockid, struct sigevent *sevp, + timer_t *timerid) +{ + if (NULL_PTR (timer_create)) + init_interposition_intf (); + TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_timer_create_2_3_3@%p\n", CALL_REAL (timer_create_2_3_3)); + return __collector_timer_create_symver (CALL_REAL (timer_create_2_3_3), clockid, sevp, timerid); +} +__asm__(".symver __collector_timer_create_2_3_3,timer_create@@GLIBC_2.3.3"); +#endif /* ARCH(SPARC) || ARCH(Intel)*/ + +#if ARCH(SPARC) + +int +__collector_timer_create_2_2 (clockid_t clockid, struct sigevent *sevp, + timer_t *timerid) +{ + if (NULL_PTR (timer_create)) + init_interposition_intf (); + TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_timer_create_2_2@%p\n", CALL_REAL (timer_create_2_2)); + return __collector_timer_create_symver (CALL_REAL (timer_create_2_2), clockid, sevp, timerid); +} + +__asm__(".symver __collector_timer_create_2_2,timer_create@GLIBC_2.2"); + +#elif ARCH(Intel) + +int +__collector_timer_create_2_2_5 (clockid_t clockid, struct sigevent *sevp, + timer_t *timerid) +{ + if (NULL_PTR (timer_create)) + init_interposition_intf (); + TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_timer_create_2_2_5@%p\n", CALL_REAL (timer_create_2_2_5)); + return __collector_timer_create_symver (CALL_REAL (timer_create_2_2_5), clockid, sevp, timerid); +} +__asm__(".symver __collector_timer_create_2_2_5,timer_create@GLIBC_2.2.5"); +#endif /* ARCH() */ +#endif /* WSIZE(64) */ + +#if ARCH(Aarch64) || (ARCH(Intel) && WSIZE(32)) +int timer_create (clockid_t clockid, struct sigevent *sevp, timer_t *timerid) +#else +static int +__collector_timer_create_symver (int(real_timer_create) (), clockid_t clockid, + struct sigevent *sevp, timer_t *timerid) +#endif +{ + int ret; + + if (NULL_PTR (timer_create)) + init_interposition_intf (); + + /* collector reserves SIGPROF + */ + if (sevp == NULL || sevp->sigev_notify != SIGEV_SIGNAL + || sevp->sigev_signo != SIGPROF) + { +#if ARCH(Aarch64) || (ARCH(Intel) && WSIZE(32)) + ret = CALL_REAL (timer_create)(clockid, sevp, timerid); +#else + ret = (real_timer_create) (clockid, sevp, timerid); +#endif + TprintfT (DBG_LT2, "Real timer_create(%d) returned %d\n", + clockid, ret); + return ret; + } + + /* log that application's timer_create request is overridden */ + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d</event>\n", + SP_JCMD_CWARN, COL_WARN_ITMROVR, -1); + ret = -1; + errno = EBUSY; + TprintfT (DBG_LT2, "timer_create() returning %d\n", ret); + return ret; +} +/*------------------------------------------------------------- setitimer */ +int +_setitimer (int which, const struct itimerval *nval, + struct itimerval *oval) +{ + int ret; + int period; + + if (NULL_PTR (setitimer)) + init_interposition_intf (); + + if (nval == NULL) + period = -1; + else + period = (nval->it_interval.tv_sec * MICROSEC) + + nval->it_interval.tv_usec; + TprintfT (DBG_LT1, "setitimer(which=%d,nval=%dus) interposing\n", which, period); + + /* collector reserves ITIMER_REALPROF for its own use, and ITIMER_PROF + * uses the same signal (SIGPROF) so it must also be reserved + */ + if (((which != ITIMER_REALPROF) && (which != ITIMER_PROF)) || (nval == NULL)) + { + ret = CALL_REAL (setitimer)(which, nval, oval); + if (oval == NULL) + period = -1; + else + period = (oval->it_interval.tv_sec * MICROSEC) + + oval->it_interval.tv_usec; + TprintfT (DBG_LT2, "Real setitimer(%d) returned %d (oval=%dus)\n", + which, ret, period); + return ret; + } + /* log that application's setitimer request is overridden */ + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d</event>\n", + SP_JCMD_CWARN, COL_WARN_ITMROVR, period); + if (oval == NULL) + period = -1; + else + { + getitimer (which, oval); /* return current itimer setting */ + period = (oval->it_interval.tv_sec * MICROSEC) + + oval->it_interval.tv_usec; + } + ret = -1; + errno = EBUSY; + TprintfT (DBG_LT2, "setitimer() returning %d (oval=%dus)\n", ret, period); + return ret; +} + +/*--------------------------------------------------------------- sigprocmask */ +int +__collector_sigprocmask (int how, const sigset_t* iset, sigset_t* oset) +{ + int err = 0; + if (NULL_PTR (sigprocmask)) + err = init_interposition_intf (); + if (err) + return -1; + TprintfT (DBG_LT2, "__collector_sigprocmask(%d) interposing\n", how); + sigset_t lsigset; + sigset_t* lset = NULL; + if (iset) + { + lsigset = *iset; + lset = &lsigset; + if ((how == SIG_BLOCK) || (how == SIG_SETMASK)) + protect_profiling_signals (lset); + } + int ret = CALL_REAL (sigprocmask)(how, lset, oset); + TprintfT (DBG_LT2, "__collector_sigprocmask(%d) returning %d\n", how, ret); + return ret; +} + +/*------------------------------------------------------------ thr_sigsetmask */ +int +__collector_thr_sigsetmask (int how, const sigset_t* iset, sigset_t* oset) +{ + if (NULL_PTR (thr_sigsetmask)) + init_interposition_intf (); + TprintfT (DBG_LT1, "__collector_thr_sigsetmask(%d) interposing\n", how); + sigset_t lsigset; + sigset_t* lset = NULL; + if (iset) + { + lsigset = *iset; + lset = &lsigset; + if ((how == SIG_BLOCK) || (how == SIG_SETMASK)) + protect_profiling_signals (lset); + } + int ret = CALL_REAL (thr_sigsetmask)(how, lset, oset); + TprintfT (DBG_LT1, "__collector_thr_sigsetmask(%d) returning %d\n", how, ret); + return ret; +} + +/*----------------------------------------------------------- pthread_sigmask */ + +int +pthread_sigmask (int how, const sigset_t* iset, sigset_t* oset) +{ + if (NULL_PTR (pthread_sigmask)) + init_interposition_intf (); + TprintfT (DBG_LT1, "__collector_pthread_sigmask(%d) interposing\n", how); + sigset_t lsigset; + sigset_t* lset = NULL; + if (iset) + { + lsigset = *iset; + lset = &lsigset; + if ((how == SIG_BLOCK) || (how == SIG_SETMASK)) + protect_profiling_signals (lset); + } + int ret = CALL_REAL (pthread_sigmask)(how, lset, oset); + TprintfT (DBG_LT1, "__collector_pthread_sigmask(%d) returning %d\n", how, ret); + return ret; +} +/*----------------------------------------------------------- pthread_create */ +typedef struct _CollectorArgs +{ + void *(*func)(void*); + void *arg; + void *stack; + int isPthread; +} CollectorArgs; + +static void * +collector_root (void *cargs) +{ + /* save the real arguments and free cargs */ + void *(*func)(void*) = ((CollectorArgs*) cargs)->func; + void *arg = ((CollectorArgs*) cargs)->arg; + void *stack = ((CollectorArgs*) cargs)->stack; + int isPthread = ((CollectorArgs*) cargs)->isPthread; + __collector_freeCSize (__collector_heap, cargs, sizeof (CollectorArgs)); + + /* initialize tsd for this thread */ + if (__collector_tsd_allocate () == 0) + /* init tsd for unwind, called right after __collector_tsd_allocate()*/ + __collector_ext_unwind_key_init (isPthread, stack); + + if (!isPthread) + __collector_mutex_lock (&collector_clone_libc_lock); + + /* set the profile timer */ + timer_t *timeridptr = __collector_tsd_get_by_key (dispatcher_key); + timer_t timerid = NULL; + if (timeridptr != NULL) + { + collector_timer_create (timeridptr); + if (*timeridptr != NULL) + collector_timer_settime (itimer_period_requested, *timeridptr); + timerid = *timeridptr; + } + int hwc_rc = __collector_ext_hwc_lwp_init (); + + if (!isPthread) + __collector_mutex_unlock (&collector_clone_libc_lock); + /* call the real function */ + void *ret = func (arg); + if (!isPthread) + __collector_mutex_lock (&collector_clone_libc_lock); + if (timerid != NULL) + CALL_REAL (timer_delete)(timerid); + if (!hwc_rc) + /* pthread_kill not handled here */ + __collector_ext_hwc_lwp_fini (); + + if (!isPthread) + __collector_mutex_unlock (&collector_clone_libc_lock); + /* if we have this chance, release tsd */ + __collector_tsd_release (); + + return ret; +} + +// map interposed symbol versions +#if ARCH(Intel) && WSIZE(32) +static int +__collector_pthread_create_symver (int(real_pthread_create) (), + pthread_t *thread, + const pthread_attr_t *attr, + void *(*func)(void*), + void *arg); + +int +__collector_pthread_create_2_1 (pthread_t *thread, + const pthread_attr_t *attr, + void *(*func)(void*), + void *arg) +{ + if (NULL_PTR (pthread_create)) + init_interposition_intf (); + TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_pthread_create_2_1@%p\n", CALL_REAL (pthread_create_2_1)); + return __collector_pthread_create_symver (CALL_REAL (pthread_create_2_1), thread, attr, func, arg); +} + +int +__collector_pthread_create_2_0 (pthread_t *thread, + const pthread_attr_t *attr, + void *(*func)(void*), + void *arg) +{ + if (NULL_PTR (pthread_create)) + init_interposition_intf (); + TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_pthread_create_2_0@%p\n", CALL_REAL (pthread_create_2_0)); + return __collector_pthread_create_symver (CALL_REAL (pthread_create_2_0), thread, attr, func, arg); +} + +__asm__(".symver __collector_pthread_create_2_1,pthread_create@@GLIBC_2.1"); +__asm__(".symver __collector_pthread_create_2_0,pthread_create@GLIBC_2.0"); + +#endif + +#if ARCH(Intel) && WSIZE(32) +static int +__collector_pthread_create_symver (int(real_pthread_create) (), + pthread_t *thread, + const pthread_attr_t *attr, + void *(*func)(void*), + void *arg) +#else +int +pthread_create (pthread_t *thread, const pthread_attr_t *attr, + void *(*func)(void*), void *arg) +#endif +{ + if (NULL_PTR (pthread_create)) + init_interposition_intf (); + + TprintfT (DBG_LT1, "pthread_create interposition called\n"); + + if (dispatch_mode != DISPATCH_ON) + { +#if ARCH(Intel) && WSIZE(32) + return (real_pthread_create) (thread, attr, func, arg); +#else + return CALL_REAL (pthread_create)(thread, attr, func, arg); +#endif + } + CollectorArgs *cargs = __collector_allocCSize (__collector_heap, sizeof (CollectorArgs), 1); + + if (cargs == NULL) + { +#if ARCH(Intel) && WSIZE(32) + return (real_pthread_create) (thread, attr, func, arg); +#else + return CALL_REAL (pthread_create)(thread, attr, func, arg); +#endif + } + cargs->func = func; + cargs->arg = arg; + cargs->stack = NULL; + cargs->isPthread = 1; + int ret = -1; +#if ARCH(Intel) && WSIZE(32) + ret = (real_pthread_create) (thread, attr, &collector_root, cargs); +#else + ret = CALL_REAL (pthread_create)(thread, attr, &collector_root, cargs); +#endif + if (ret) + __collector_freeCSize (__collector_heap, cargs, sizeof (CollectorArgs)); + TprintfT (DBG_LT1, "pthread_create returning %d\n", ret); + return ret; +} + +int +__collector_ext_clone_pthread (int (*fn)(void *), void *child_stack, int flags, void *arg, + va_list va /* pid_t *ptid, struct user_desc *tls, pid_t *" ctid" */) +{ + if (NULL_PTR (clone)) + init_interposition_intf (); + TprintfT (0, "clone thread interposing\n"); + pid_t * ptid = NULL; + struct user_desc * tls = NULL; + pid_t * ctid = NULL; + int num_args = 0; + if (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) + { + ptid = va_arg (va, pid_t *); + tls = va_arg (va, struct user_desc*); + ctid = va_arg (va, pid_t *); + num_args = 3; + } + else if (flags & CLONE_SETTLS) + { + ptid = va_arg (va, pid_t *); + tls = va_arg (va, struct user_desc*); + num_args = 2; + } + else if (flags & CLONE_PARENT_SETTID) + { + ptid = va_arg (va, pid_t *); + num_args = 1; + } + int ret = 0; + if (dispatch_mode != DISPATCH_ON) + { + switch (num_args) + { + case 3: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls, ctid); + break; + case 2: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls); + break; + case 1: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid); + break; + default: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg); + break; + } + return ret; + } + CollectorArgs *cargs = __collector_allocCSize (__collector_heap, sizeof (CollectorArgs), 1); + if (cargs == NULL) + { + switch (num_args) + { + case 3: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls, ctid); + break; + case 2: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls); + break; + case 1: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid); + break; + default: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg); + break; + } + return ret; + } + + cargs->func = (void *(*)(void*))fn; + cargs->arg = arg; + cargs->stack = child_stack; + cargs->isPthread = 0; + + switch (num_args) + { + case 3: + ret = CALL_REAL (clone)((int(*)(void*))collector_root, child_stack, flags, cargs, ptid, tls, ctid); + break; + case 2: + ret = CALL_REAL (clone)((int(*)(void*))collector_root, child_stack, flags, cargs, ptid, tls); + break; + case 1: + ret = CALL_REAL (clone)((int(*)(void*))collector_root, child_stack, flags, cargs, ptid); + break; + default: + ret = CALL_REAL (clone)((int(*)(void*))collector_root, child_stack, flags, cargs); + break; + } + + if (ret < 0) + __collector_freeCSize (__collector_heap, cargs, sizeof (CollectorArgs)); + TprintfT (DBG_LT1, "clone thread returning %d\n", ret); + return ret; +} + +// weak symbols: +int sigprocmask () __attribute__ ((weak, alias ("__collector_sigprocmask"))); +int thr_sigsetmask () __attribute__ ((weak, alias ("__collector_thr_sigsetmask"))); +int setitimer () __attribute__ ((weak, alias ("_setitimer"))); diff --git a/gprofng/libcollector/envmgmt.c b/gprofng/libcollector/envmgmt.c new file mode 100644 index 0000000..b4418d6 --- /dev/null +++ b/gprofng/libcollector/envmgmt.c @@ -0,0 +1,840 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Routines for managing the target's environment array + */ + +#include "config.h" +#include "descendants.h" + +#define MAX_LD_PRELOADS 2 + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 +#define DBG_LT4 4 + +/* original environment settings to be saved for later restoration */ +static char *sp_preloads[MAX_LD_PRELOADS]; +static char *sp_libpaths[MAX_LD_PRELOADS]; +char **sp_env_backup; + +static const char *SP_ENV[]; +static const char *LD_ENV[]; +static const char *SP_PRELOAD[]; +static const char *LD_PRELOAD[]; +static const char *SP_LIBRARY_PATH[]; +static const char *LD_LIBRARY_PATH[]; +static int NUM_SP_ENV_VARS; +static int NUM_LD_ENV_VARS; +static int NUM_SP_PRELOADS; +static int NUM_LD_PRELOADS; +static int NUM_SP_LIBPATHS; +static int NUM_LD_LIBPATHS; + +static const char *SP_ENV[] = { + SP_COLLECTOR_PARAMS, /* data descriptor */ + SP_COLLECTOR_EXPNAME, /* experiment name */ + SP_COLLECTOR_FOLLOW_SPEC, /* linetrace */ + SP_COLLECTOR_FOUNDER, /* determine founder exp */ + SP_PRELOAD_STRINGS, /* LD_PRELOADs for data collection */ + SP_LIBPATH_STRINGS, /* LD_LIBRARY_PATHs for data collection */ + "SP_COLLECTOR_TRACELEVEL", /* tprintf */ +#if DEBUG + "SP_COLLECTOR_SIGACTION", /* dispatcher, hwprofile */ +#endif + /* JAVA* */ + /* LD_DEBUG=audit,bindings,detail */ + /* LD_ORIGIN=yes */ + NULL +}; + +static const char *LD_ENV[] = { + LD_PRELOAD_STRINGS, /* LD_PRELOADs */ + LD_LIBPATH_STRINGS, /* LD_LIBRARY_PATHs */ + JAVA_TOOL_OPTIONS, /* enable -agentlib:collector for JVMTI */ + NULL +}; + +static const char *SP_PRELOAD[] = { + SP_PRELOAD_STRINGS, + NULL +}; + +static const char *LD_PRELOAD[] = { + LD_PRELOAD_STRINGS, + NULL +}; + +static const char *SP_LIBRARY_PATH[] = { + SP_LIBPATH_STRINGS, + NULL +}; +static const char *LD_LIBRARY_PATH[] = { + LD_LIBPATH_STRINGS, + NULL +}; + +void +__collector_env_save_preloads () +{ + /* save the list of SP_PRELOADs */ + int v; + for (v = 0; SP_PRELOAD[v]; v++) + { + sp_preloads[v] = __collector_strdup (CALL_UTIL (getenv)(SP_PRELOAD[v])); + TprintfT (DBG_LT3, "__collector_env_save_preloads: %s=%s\n", SP_PRELOAD[v], sp_preloads[v]); + } + NUM_SP_PRELOADS = v; + for (v = 0; SP_LIBRARY_PATH[v]; v++) + { + sp_libpaths[v] = __collector_strdup (CALL_UTIL (getenv)(SP_LIBRARY_PATH[v])); + TprintfT (DBG_LT4, "__collector_env_save_preloads: %s=%s\n", SP_LIBRARY_PATH[v], + sp_libpaths[v] ? sp_libpaths[v] : "NULL"); + } + NUM_SP_LIBPATHS = v; + for (v = 0; LD_PRELOAD[v]; v++) + ; + NUM_LD_PRELOADS = v; + for (v = 0; LD_LIBRARY_PATH[v]; v++) + ; + NUM_LD_LIBPATHS = v; + for (v = 0; SP_ENV[v]; v++) + ; + NUM_SP_ENV_VARS = v; + for (v = 0; LD_ENV[v]; v++) + ; + NUM_LD_ENV_VARS = v; +} + +/* free the memory involved in backing up the environment */ +void +__collector_env_backup_free () +{ + int v = 0; + TprintfT (DBG_LT2, "env_backup_free()\n"); + for (v = 0; sp_env_backup[v]; v++) + { + TprintfT (DBG_LT2, "env_backup_free():sp_env_backup[%d]=%s \n", v, sp_env_backup[v]); + __collector_freeCSize (__collector_heap, (char *) sp_env_backup[v], __collector_strlen (sp_env_backup[v]) + 1); + } + __collector_freeCSize (__collector_heap, (char**) sp_env_backup, + (NUM_SP_ENV_VARS + NUM_LD_ENV_VARS + 1) * sizeof (char*)); +} + +char ** +__collector_env_backup () +{ + TprintfT (DBG_LT2, "env_backup_()\n"); + char **backup = __collector_env_allocate (NULL, 1); + __collector_env_update (backup); + TprintfT (DBG_LT2, "env_backup_()\n"); + return backup; +} + +/* + function: env_prepend() + given an <old_str>, check to see if <str> + is already defined by it. If not, allocate + a new string and concat <envvar>=<str><separator><old_str> + params: + old_str: original string + str: substring to prepend + return: pointer to updated string or NULL if string was not updated. + */ +static char * +env_prepend (const char *envvar, const char *str, const char *separator, + const char *old_str) +{ + if (!envvar || *envvar == 0 || !str || *str == 0) + { + /* nothing to do */ + TprintfT (DBG_LT2, "env_prepend(\"%s\", \"%s\", \"%s\", \"%s\") -- nothing to do\n", + envvar, str, separator, old_str); + + return NULL; + } + TprintfT (DBG_LT2, "env_prepend(\"%s\", \"%s\", \"%s\", \"%s\")\n", + envvar, str, separator, old_str); + char *ev; + size_t strsz; + if (!old_str || *old_str == 0) + { + strsz = __collector_strlen (envvar) + 1 + __collector_strlen (str) + 1; + ev = (char*) __collector_allocCSize (__collector_heap, strsz, 1); + if (ev) + { + CALL_UTIL (snprintf)(ev, strsz, "%s=%s", envvar, str); + assert (__collector_strlen (ev) + 1 == strsz); + } + else + TprintfT (DBG_LT2, "env_prepend(): could not allocate memory\n"); + } + else + { + char *p = CALL_UTIL (strstr)(old_str, str); + if (p) + { + TprintfT (DBG_LT2, "env_prepend(): %s=%s was already set\n", + envvar, old_str); + return NULL; + } + strsz = __collector_strlen (envvar) + 1 + __collector_strlen (str) + + __collector_strlen (separator) + __collector_strlen (old_str) + 1; + ev = (char*) __collector_allocCSize (__collector_heap, strsz, 1); + if (ev) + { + CALL_UTIL (snprintf)(ev, strsz, "%s=%s%s%s", envvar, str, separator, old_str); + assert (__collector_strlen (ev) + 1 == strsz); + } + else + TprintfT (DBG_LT2, "env_prepend(): could not allocate memory\n"); + } + TprintfT (DBG_LT2, "env_prepend(\"%s\", \"%s\", \"%s\", \"%s\") returns \"%s\"\n", + envvar, str, separator, old_str, (ev == NULL ? "NULL" : ev)); + return ev; +} + +/* + function: putenv_prepend() + get environment variable <envvar>, check to see if <str> + is already defined by it. If not prepend <str> + and put it back to environment. + params: + envvar: environment variable + str: substring to find + return: 0==success, nonzero on failure. + */ +int +putenv_prepend (const char *envvar, const char *str, const char *separator) +{ + if (!envvar || *envvar == 0) + return 1; + const char * old_str = CALL_UTIL (getenv)(envvar); + char * newstr = env_prepend (envvar, str, separator, old_str); + if (newstr) + // now put the new variable into the environment + if (CALL_UTIL (putenv)(newstr) != 0) + { + TprintfT (DBG_LT2, "putenv_prepend(): ERROR %s is not set!\n", newstr); + return 1; + } + return 0; +} + +/* + function: env_strip() + Finds substr in origstr; Removes + all characters from previous ':' or ' ' + up to and including any trailing ':' or ' '. + params: + env: environment variable contents + str: substring to find + return: count of instances removed from env + */ +static int +env_strip (char *origstr, const char *substr) +{ + int removed = 0; + char *p, *q; + if (origstr == NULL || substr == NULL || *substr == 0) + return 0; + while ((p = q = CALL_UTIL (strstr)(origstr, substr))) + { + p += __collector_strlen (substr); + while (*p == ':' || *p == ' ') /* strip trailing separator */ + p++; + while (*q != ':' && *q != ' ' && *q != '=' && q != origstr) /* strip path */ + q--; + if (q != origstr) /* restore leading separator (if any) */ + q++; + __collector_strlcpy (q, p, __collector_strlen (p) + 1); + removed++; + } + return removed; +} + +/* + function: env_ld_preload_strip() + Removes known libcollector shared objects from envv. + params: + var: shared object name (leading characters don't have to match) + return: 0 = so's removed, non-zero = so's not found. + */ +static int +env_ld_preload_strip (char *envv) +{ + if (!envv || *envv == 0) + { + TprintfT (DBG_LT2, "env_ld_preload_strip(): WARNING - envv is NULL\n"); + return -1; + } + for (int v = 0; SP_PRELOAD[v]; v++) + if (env_strip (envv, sp_preloads[v])) + return 0; + if (line_mode != LM_CLOSED) + TprintfT (DBG_LT2, "env_ld_preload_strip(): WARNING - could not strip SP_PRELOADS from '%s'\n", + envv); + return -2; +} + +void +__collector_env_print (char * label) +{ +#if DEBUG + TprintfT (DBG_LT2, "__collector_env_print(%s)\n", label); + for (int v = 0; v < MAX_LD_PRELOADS; v++) + TprintfT (DBG_LT2, " %s sp_preloads[%d] (0x%p)=%s\n", label, + v, sp_preloads[v], (sp_preloads[v] == NULL ? "NULL" : sp_preloads[v])); + for (int v = 0; SP_ENV[v]; v++) + { + char *s = CALL_UTIL (getenv)(SP_ENV[v]); + if (s == NULL) + s = "<null>"; + TprintfT (DBG_LT2, " %s SP_ENV[%d] (0x%p): %s=\"%s\"\n", label, v, SP_ENV[v], SP_ENV[v], s); + } + for (int v = 0; LD_ENV[v]; v++) + { + char *s = CALL_UTIL (getenv)(LD_ENV[v]); + if (s == NULL) + s = "<null>"; + TprintfT (DBG_LT2, " %s LD_ENV[%d] (0x%p): %s=\"%s\"\n", label, v, LD_ENV[v], LD_ENV[v], s); + } +#endif +} + +void +__collector_env_printall (char *label, char *envp[]) +{ +#if DEBUG + TprintfT (DBG_LT2, "__collector_env_printall(%s): environment @ 0x%p\n", label, envp); + for (int i = 0; envp[i]; i++) + Tprintf (DBG_LT2, "\tenv[%d]@0x%p == %s\n", i, envp[i], envp[i]); +#endif +} + +/* match collector environment variable */ +int +env_match (char *envp[], const char *envvar) +{ + int match = -1; + if (envp == NULL) + TprintfT (DBG_LT1, "env_match(%s): NULL envp!\n", envvar); + else + { + int i = 0; + while ((envp[i] != NULL) && (__collector_strStartWith (envp[i], envvar))) + i++; + if ((envp[i] == NULL) || (envp[i][__collector_strlen (envvar)] != '=')) + TprintfT (DBG_LT4, "env_match(): @%p []%s not defined in envp\n", envp, envvar); + else + { + TprintfT (DBG_LT4, "env_match(): @%p [%d]%s defined in envp\n", envp, i, envp[i]); + match = i; + } + } + TprintfT (DBG_LT1, "env_match(%s): found in slot %d\n", envvar, match); + return (match); +} + +/* allocate new environment with collector variables */ +/* 1) copy all current envp[] ptrs into a new array, coll_env[] */ +/* 2) if collector-related env ptrs not in envp[], append them to coll_env */ +/* from processes' "environ" (allocate_env==1) */ +/* or from sp_env_backup (allocate_env==0)*/ +/* If they already exist in envp, probably is an error... */ +/* 3) return coll_env */ + +/* __collector__env_update() need be called after this to set LD_ENV*/ +char ** +__collector_env_allocate (char *const old_env[], int allocate_env) +{ + extern char **environ; /* the process' actual environment */ + char **new_env; /* a new environment for collection */ + TprintfT (DBG_LT3, "__collector_env_allocate(old_env=0x%p %s environ=0x%p)\n", + old_env, (old_env == environ) ? "==" : "!=", environ); + /* set up a copy of the provided old_env for collector use */ + int old_env_size = 0; + + /* determine number of (used) slots in old_env */ + if (old_env) + while (old_env[old_env_size] != NULL) + old_env_size++; + /* allocate a new vector with additional slots */ + int new_env_alloc_sz = old_env_size + NUM_SP_ENV_VARS + NUM_LD_ENV_VARS + 1; + new_env = (char**) __collector_allocCSize (__collector_heap, new_env_alloc_sz * sizeof (char*), 1); + if (new_env == NULL) + return NULL; + TprintfT (DBG_LT4, "__collector_env_allocate(): old_env has %d entries, new_env @ 0x%p\n", old_env_size, new_env); + + /* copy provided old_env pointers to new collector environment */ + int new_env_size = 0; + for (new_env_size = 0; new_env_size < old_env_size; new_env_size++) + new_env[new_env_size] = old_env[new_env_size]; + + /* check each required environment variable, adding as required */ + const char * env_var; + int v; + for (v = 0; (env_var = SP_ENV[v]) != NULL; v++) + { + if (env_match ((char**) old_env, env_var) == -1) + { + int idx; + /* not found in old_env */ + if (allocate_env) + { + if ((idx = env_match (environ, env_var)) != -1) + { + /* found in environ */ + TprintfT (DBG_LT4, "__collector_env_allocate(): [%d]%s env restored!\n", + new_env_size, environ[idx]); + int varsz = __collector_strlen (environ[idx]) + 1; + char * var = (char*) __collector_allocCSize (__collector_heap, varsz, 1); + if (var == NULL) + return NULL; + __collector_strlcpy (var, environ[idx], varsz); + new_env[new_env_size++] = var; + } + else + { + /* not found in environ */ + if ((__collector_strcmp (env_var, SP_COLLECTOR_PARAMS) == 0) || + (__collector_strcmp (env_var, SP_COLLECTOR_EXPNAME) == 0)) + TprintfT (DBG_LT1, "__collector_env_allocate(): note: %s environment variable not found\n", + env_var); + } + } + else + { + if ((idx = env_match (sp_env_backup, env_var)) != -1) + { + /* found in backup */ + TprintfT (DBG_LT4, "__collector_env_allocate(): [%d]%s env restored!\n", + new_env_size, sp_env_backup[idx]); + new_env[new_env_size++] = sp_env_backup[idx]; + } + else + { + /* not found in environ */ + if ((__collector_strcmp (env_var, SP_COLLECTOR_PARAMS) == 0) || + (__collector_strcmp (env_var, SP_COLLECTOR_EXPNAME) == 0)) + TprintfT (DBG_LT1, "__collector_env_allocate(): note: %s environment variable not found\n", + env_var); + } + } + } + } + + for (v = 0; (env_var = LD_ENV[v]) != NULL; v++) + { + if (env_match ((char**) old_env, env_var) == -1) + { + int idx; + /* not found in old_env */ + if (allocate_env) + { + if ((idx = env_match (environ, env_var)) != -1) + { + /* found in environ */ + TprintfT (DBG_LT4, "__collector_env_allocate(): [%d]%s env restored!\n", + new_env_size, environ[idx]); + + int varsz = __collector_strlen (env_var) + 2; + char * var = (char*) __collector_allocCSize (__collector_heap, varsz, 1); + if (var == NULL) + return NULL; + // assume __collector_env_update() will fill content of env_var + CALL_UTIL (snprintf)(var, varsz, "%s=", env_var); + new_env[new_env_size++] = var; + } + } + else + { + if ((idx = env_match (sp_env_backup, env_var)) != -1) + { + /* found in backup */ + TprintfT (DBG_LT4, "__collector_env_allocate(): [%d]%s env restored!\n", + new_env_size, sp_env_backup[idx]); + new_env[new_env_size++] = sp_env_backup[idx]; + } + } + } + } + + /* ensure new_env vector ends with NULL */ + new_env[new_env_size] = NULL; + assert (new_env_size <= new_env_alloc_sz); + TprintfT (DBG_LT4, "__collector_env_allocate(): new_env has %d entries (%d added), new_env=0x%p\n", + new_env_size, new_env_size - old_env_size, new_env); + if (new_env_size != old_env_size && !allocate_env) + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d</event>\n", + SP_JCMD_CWARN, COL_WARN_EXECENV, new_env_size - old_env_size); + __collector_env_printall ("__collector_env_allocate", new_env); + return (new_env); +} + +/* unset collection environment variables */ +/* if they exist in env... */ +/* 1) push non-collectorized version to env */ + +/* Not mt safe */ +void +__collector_env_unset (char *envp[]) +{ + int v; + const char * env_name; + TprintfT (DBG_LT3, "env_unset(envp=0x%p)\n", envp); + if (envp == NULL) + { + for (v = 0; (env_name = LD_PRELOAD[v]); v++) + { + const char *env_val = CALL_UTIL (getenv)(env_name); + if (env_val && CALL_UTIL (strstr)(env_val, sp_preloads[v])) + { + size_t sz = __collector_strlen (env_name) + 1 + __collector_strlen (env_val) + 1; + char * ev = (char*) __collector_allocCSize (__collector_heap, sz, 1); + if (ev == NULL) + return; + CALL_UTIL (snprintf)(ev, sz, "%s=%s", env_name, env_val); + assert (__collector_strlen (ev) + 1 == sz); + TprintfT (DBG_LT4, "env_unset(): old %s\n", ev); + env_ld_preload_strip (ev); + CALL_UTIL (putenv)(ev); + TprintfT (DBG_LT4, "env_unset(): new %s\n", ev); + } + } + // unset JAVA_TOOL_OPTIONS + env_name = JAVA_TOOL_OPTIONS; + const char * env_val = CALL_UTIL (getenv)(env_name); + if (env_val && CALL_UTIL (strstr)(env_val, COLLECTOR_JVMTI_OPTION)) + { + size_t sz = __collector_strlen (env_name) + 1 + __collector_strlen (env_val) + 1; + char * ev = (char*) __collector_allocCSize (__collector_heap, sz, 1); + if (ev == NULL) + return; + CALL_UTIL (snprintf)(ev, sz, "%s=%s", env_name, env_val); + assert (__collector_strlen (ev) + 1 == sz); + TprintfT (DBG_LT4, "env_unset(): old %s\n", ev); + env_strip (ev, COLLECTOR_JVMTI_OPTION); + CALL_UTIL (putenv)(ev); + TprintfT (DBG_LT4, "env_unset(): new %s\n", ev); + } + __collector_env_print ("__collector_env_unset"); + } + else + { + __collector_env_printall ("__collector_env_unset, before", envp); + for (v = 0; (env_name = LD_PRELOAD[v]); v++) + { + int idx = env_match (envp, env_name); + if (idx != -1) + { + char *env_val = envp[idx]; + TprintfT (DBG_LT4, "env_unset(): old %s\n", env_val); + envp[idx] = "junk="; /* xxxx is it ok to use original string? */ + env_ld_preload_strip (env_val); + envp[idx] = env_val; + TprintfT (DBG_LT4, "env_unset(): new %s\n", envp[idx]); + } + } + // unset JAVA_TOOL_OPTIONS + env_name = JAVA_TOOL_OPTIONS; + int idx = env_match(envp, env_name); + if (idx != -1) { + char *env_val = envp[idx]; + TprintfT(DBG_LT4, "env_unset(): old %s\n", env_val); + envp[idx] = "junk="; /* xxxx is it ok to use original string? */ + env_strip(env_val, COLLECTOR_JVMTI_OPTION); + envp[idx] = env_val; + TprintfT(DBG_LT4, "env_unset(): new %s\n", envp[idx]); + } + __collector_env_printall ("__collector_env_unset, after", envp ); + } +} + +/* update collection environment variables */ +/* update LD_PRELOADs and push them */ +/* not mt safe */ +void +__collector_env_update (char *envp[]) +{ + const char *env_name; + TprintfT (DBG_LT1, "__collector_env_update(envp=0x%p)\n", envp); + extern char **environ; + if (envp == NULL) + { + int v; + TprintfT (DBG_LT2, "__collector_env_update(envp=NULL)\n"); + __collector_env_printall (" environ array, before", environ); + __collector_env_print (" env_update at entry "); + + /* SP_ENV */ + for (v = 0; (env_name = SP_ENV[v]) != NULL; v++) + { + if (env_match (environ, env_name) == -1) + { + int idx; + if ((idx = env_match (sp_env_backup, env_name)) != -1) + { + unsigned strsz = __collector_strlen (sp_env_backup[idx]) + 1; + char *ev = (char*) __collector_allocCSize (__collector_heap, strsz, 1); + CALL_UTIL (snprintf)(ev, strsz, "%s", sp_env_backup[idx]); + if (CALL_UTIL (putenv)(ev) != 0) + TprintfT (DBG_LT2, "__collector_env_update(): ERROR %s is not set!\n", + sp_env_backup[idx]); + } + } + } + __collector_env_print (" env_update after SP_ENV settings "); + + /* LD_LIBRARY_PATH */ + for (v = 0; (env_name = LD_LIBRARY_PATH[v]); v++) + /* assumes same index used between LD and SP vars */ + if (putenv_prepend (env_name, sp_libpaths[v], ":")) + TprintfT (DBG_LT2, "collector: ERROR %s=%s could not be set\n", + env_name, sp_libpaths[v]); + __collector_env_print (" env_update after LD_LIBRARY_PATH settings "); + + /* LD_PRELOAD */ + for (v = 0; (env_name = LD_PRELOAD[v]); v++) + /* assumes same index used between LD and SP vars */ + if (putenv_prepend (env_name, sp_preloads[v], " ")) + TprintfT (DBG_LT2, "collector: ERROR %s=%s could not be set\n", + env_name, sp_preloads[v]); + __collector_env_print (" env_update after LD_PRELOAD settings "); + + /* JAVA_TOOL_OPTIONS */ + if (java_mode) + if (putenv_prepend (JAVA_TOOL_OPTIONS, COLLECTOR_JVMTI_OPTION, " ")) + TprintfT (DBG_LT2, "collector: ERROR %s=%s could not be set\n", + JAVA_TOOL_OPTIONS, COLLECTOR_JVMTI_OPTION); + __collector_env_print (" env_update after JAVA_TOOL settings "); + } + else + { + int v; + int idx; + TprintfT (DBG_LT2, "__collector_env_update(envp=0x%p) not NULL\n", envp); + __collector_env_printall ("__collector_env_update, before", envp); + /* LD_LIBRARY_PATH */ + for (v = 0; (env_name = LD_LIBRARY_PATH[v]); v++) + { + int idx = env_match (envp, env_name); + if (idx != -1) + { + char *env_val = __collector_strchr (envp[idx], '='); + if (env_val) + env_val++; /* skip '=' */ + /* assumes same index used between LD and SP vars */ + char *new_str = env_prepend (env_name, sp_libpaths[v], + ":", env_val); + if (new_str) + envp[idx] = new_str; + } + } + + /* LD_PRELOAD */ + for (v = 0; (env_name = LD_PRELOAD[v]); v++) + { + int idx = env_match (envp, env_name); + if (idx != -1) + { + char *env_val = __collector_strchr (envp[idx], '='); + if (env_val) + env_val++; /* skip '=' */ + /* assumes same index used between LD and SP vars */ + char *new_str = env_prepend (env_name, sp_preloads[v], + " ", env_val); + if (new_str) + envp[idx] = new_str; + } + } + + /* JAVA_TOOL_OPTIONS */ + if (java_mode) + { + env_name = JAVA_TOOL_OPTIONS; + idx = env_match (envp, env_name); + if (idx != -1) + { + char *env_val = __collector_strchr (envp[idx], '='); + if (env_val) + env_val++; /* skip '=' */ + char *new_str = env_prepend (env_name, COLLECTOR_JVMTI_OPTION, + " ", env_val); + if (new_str) + envp[idx] = new_str; + } + } + } + __collector_env_printall ("__collector_env_update, after", environ); +} + + +/*------------------------------------------------------------- putenv */ +int putenv () __attribute__ ((weak, alias ("__collector_putenv"))); +int _putenv () __attribute__ ((weak, alias ("__collector_putenv"))); + +int +__collector_putenv (char * string) +{ + if (CALL_UTIL (putenv) == __collector_putenv || + CALL_UTIL (putenv) == NULL) + { // __collector_libc_funcs_init failed + CALL_UTIL (putenv) = (int(*)())dlsym (RTLD_NEXT, "putenv"); + if (CALL_UTIL (putenv) == NULL || CALL_UTIL (putenv) == __collector_putenv) + CALL_UTIL (putenv) = (int(*)())dlsym (RTLD_DEFAULT, "putenv"); + if (CALL_UTIL (putenv) == NULL || CALL_UTIL (putenv) == __collector_putenv) + { + TprintfT (DBG_LT2, "__collector_putenv(): ERROR: no pointer found.\n"); + errno = EBUSY; + return -1; + } + } + if (user_follow_mode == FOLLOW_NONE) + return CALL_UTIL (putenv)(string); + char * envp[] = {string, NULL}; + __collector_env_update (envp); + return CALL_UTIL (putenv)(envp[0]); +} + +/*------------------------------------------------------------- setenv */ +int setenv () __attribute__ ((weak, alias ("__collector_setenv"))); +int _setenv () __attribute__ ((weak, alias ("__collector_setenv"))); + +int +__collector_setenv (const char *name, const char *value, int overwrite) +{ + if (CALL_UTIL (setenv) == __collector_setenv || + CALL_UTIL (setenv) == NULL) + { // __collector_libc_funcs_init failed + CALL_UTIL (setenv) = (int(*)())dlsym (RTLD_NEXT, "setenv"); + if (CALL_UTIL (setenv) == NULL || CALL_UTIL (setenv) == __collector_setenv) + CALL_UTIL (setenv) = (int(*)())dlsym (RTLD_DEFAULT, "setenv"); + if (CALL_UTIL (setenv) == NULL || CALL_UTIL (setenv) == __collector_setenv) + { + TprintfT (DBG_LT2, "__collector_setenv(): ERROR: no pointer found.\n"); + errno = EBUSY; + return -1; + } + } + if (user_follow_mode == FOLLOW_NONE || !overwrite) + return CALL_UTIL (setenv)(name, value, overwrite); + size_t sz = __collector_strlen (name) + 1 + __collector_strlen (value) + 1; + char *ev = (char*) __collector_allocCSize (__collector_heap, sz, 1); + if (ev == NULL) + return CALL_UTIL (setenv)(name, value, overwrite); + CALL_UTIL (snprintf)(ev, sz, "%s=%s", name, value); + char * envp[] = {ev, NULL}; + __collector_env_update (envp); + if (envp[0] == ev) + { + __collector_freeCSize (__collector_heap, ev, sz); + return CALL_UTIL (setenv)(name, value, overwrite); + } + else + { + char *env_val = __collector_strchr (envp[0], '='); + if (env_val) + { + *env_val = '\0'; + env_val++; /* skip '=' */ + } + return CALL_UTIL (setenv)(envp[0], env_val, overwrite); + } +} + +/*------------------------------------------------------------- unsetenv */ +int unsetenv () __attribute__ ((weak, alias ("__collector_unsetenv"))); +int _unsetenv () __attribute__ ((weak, alias ("__collector_unsetenv"))); + +int +__collector_unsetenv (const char *name) +{ + if (CALL_UTIL (unsetenv) == __collector_unsetenv || + CALL_UTIL (unsetenv) == NULL) + { // __collector_libc_funcs_init failed + CALL_UTIL (unsetenv) = (int(*)())dlsym (RTLD_NEXT, "unsetenv"); + if (CALL_UTIL (unsetenv) == NULL || CALL_UTIL (unsetenv) == __collector_unsetenv) + CALL_UTIL (unsetenv) = (int(*)())dlsym (RTLD_DEFAULT, "unsetenv"); + if (CALL_UTIL (unsetenv) == NULL || CALL_UTIL (unsetenv) == __collector_unsetenv) + { + TprintfT (DBG_LT2, "__collector_unsetenv(): ERROR: no pointer found.\n"); + errno = EBUSY; + return -1; + } + } + int ret = CALL_UTIL (unsetenv)(name); + if (user_follow_mode == FOLLOW_NONE) + return ret; + TprintfT (DBG_LT2, "__collector_unsetenv(): %d.\n", user_follow_mode); + size_t sz = __collector_strlen (name) + 1 + 1; + char *ev = (char*) __collector_allocCSize (__collector_heap, sz, 1); + if (ev == NULL) + return ret; + CALL_UTIL (snprintf)(ev, sz, "%s=", name); + char * envp[] = {ev, NULL}; + __collector_env_update (envp); + if (envp[0] == ev) + __collector_freeCSize (__collector_heap, ev, sz); + else + CALL_UTIL (putenv)(envp[0]); + return ret; +} + +/*------------------------------------------------------------- clearenv */ +int clearenv () __attribute__ ((weak, alias ("__collector_clearenv"))); + +int +__collector_clearenv (void) +{ + if (CALL_UTIL (clearenv) == __collector_clearenv || CALL_UTIL (clearenv) == NULL) + { + /* __collector_libc_funcs_init failed; look up clearenv now */ + CALL_UTIL (clearenv) = (int(*)())dlsym (RTLD_NEXT, "clearenv"); + if (CALL_UTIL (clearenv) == NULL || CALL_UTIL (clearenv) == __collector_clearenv) + /* still not found; try again */ + CALL_UTIL (clearenv) = (int(*)())dlsym (RTLD_DEFAULT, "clearenv"); + if (CALL_UTIL (clearenv) == NULL || CALL_UTIL (clearenv) == __collector_clearenv) + { + /* still not found -- a fatal error */ + TprintfT (DBG_LT2, "__collector_clearenv(): ERROR: %s\n", dlerror ()); + CALL_UTIL (fprintf)(stderr, "__collector_clearenv(): ERROR: %s\n", dlerror ()); + errno = EBUSY; + return -1; + } + } + int ret = CALL_UTIL (clearenv)(); + if (user_follow_mode == FOLLOW_NONE) + return ret; + if (sp_env_backup == NULL) + { + TprintfT (DBG_LT2, "__collector_clearenv: ERROR sp_env_backup is not set!\n"); + return ret; + } + for (int v = 0; v < NUM_SP_ENV_VARS + NUM_LD_ENV_VARS; v++) + if (sp_env_backup[v] && CALL_UTIL (putenv)(sp_env_backup[v]) != 0) + TprintfT (DBG_LT2, "__collector_clearenv: ERROR %s is not set!\n", + sp_env_backup[v]); + return ret; +} diff --git a/gprofng/libcollector/gethrtime.c b/gprofng/libcollector/gethrtime.c new file mode 100644 index 0000000..f369cdc --- /dev/null +++ b/gprofng/libcollector/gethrtime.c @@ -0,0 +1,41 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <time.h> +#include "gp-time.h" + +/* + * CLOCK_MONOTONIC + * Clock that cannot be set and represents monotonic time since some + * unspecified starting point. + */ +static hrtime_t +linux_gethrtime () +{ + struct timespec tp; + hrtime_t rc = 0; + int r = clock_gettime (CLOCK_MONOTONIC_RAW, &tp); + if (r == 0) + rc = ((hrtime_t) tp.tv_sec)*1000000000 + (hrtime_t) tp.tv_nsec; + return rc; +} + +hrtime_t (*__collector_gethrtime)() = linux_gethrtime; diff --git a/gprofng/libcollector/heaptrace.c b/gprofng/libcollector/heaptrace.c new file mode 100644 index 0000000..470a269 --- /dev/null +++ b/gprofng/libcollector/heaptrace.c @@ -0,0 +1,503 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Heap tracing events + */ + +#include "config.h" +#include <dlfcn.h> + +#include "gp-defs.h" +#include "collector_module.h" +#include "gp-experiment.h" +#include "data_pckts.h" +#include "tsd.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 +#define DBG_LT4 4 + +/* define the packets to be written out */ +typedef struct Heap_packet +{ /* Malloc/free tracing packet */ + Common_packet comm; + Heap_type mtype; /* subtype of packet */ + Size_type size; /* size of malloc/realloc request */ + Vaddr_type vaddr; /* vaddr given to free or returned from malloc/realloc */ + Vaddr_type ovaddr; /* Previous vaddr given to realloc */ +} Heap_packet; + +static int init_heap_intf (); +static int open_experiment (const char *); +static int start_data_collection (void); +static int stop_data_collection (void); +static int close_experiment (void); +static int detach_experiment (void); + +static ModuleInterface module_interface = { + SP_HEAPTRACE_FILE, /* description */ + NULL, /* initInterface */ + open_experiment, /* openExperiment */ + start_data_collection, /* startDataCollection */ + stop_data_collection, /* stopDataCollection */ + close_experiment, /* closeExperiment */ + detach_experiment /* detachExperiment (fork child) */ +}; + +static CollectorInterface *collector_interface = NULL; +static int heap_mode = 0; +static CollectorModule heap_hndl = COLLECTOR_MODULE_ERR; +static unsigned heap_key = COLLECTOR_TSD_INVALID_KEY; + +#define CHCK_REENTRANCE(x) ( !heap_mode || ((x) = collector_interface->getKey( heap_key )) == NULL || (*(x) != 0) ) +#define PUSH_REENTRANCE(x) ((*(x))++) +#define POP_REENTRANCE(x) ((*(x))--) +#define CALL_REAL(x) (__real_##x) +#define NULL_PTR(x) (__real_##x == NULL) +#define gethrtime collector_interface->getHiResTime + +#ifdef DEBUG +#define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ ) +#define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ ) +#else +#define Tprintf(...) +#define TprintfT(...) +#endif + +static void *(*__real_malloc)(size_t) = NULL; +static void (*__real_free)(void *); +static void *(*__real_realloc)(void *, size_t); +static void *(*__real_memalign)(size_t, size_t); +static void *(*__real_calloc)(size_t, size_t); +static void *(*__real_valloc)(size_t); +static char *(*__real_strchr)(const char *, int); + +void *__libc_malloc (size_t); +void __libc_free (void *); +void *__libc_realloc (void *, size_t); + +static void +collector_memset (void *s, int c, size_t n) +{ + unsigned char *s1 = s; + while (n--) + *s1++ = (unsigned char) c; +} + +void +__collector_module_init (CollectorInterface *_collector_interface) +{ + if (_collector_interface == NULL) + return; + collector_interface = _collector_interface; + Tprintf (0, "heaptrace: __collector_module_init\n"); + heap_hndl = collector_interface->registerModule (&module_interface); + + /* Initialize next module */ + ModuleInitFunc next_init = (ModuleInitFunc) dlsym (RTLD_NEXT, "__collector_module_init"); + if (next_init != NULL) + next_init (_collector_interface); + return; +} + +static int +open_experiment (const char *exp) +{ + if (collector_interface == NULL) + { + Tprintf (0, "heaptrace: collector_interface is null.\n"); + return COL_ERROR_HEAPINIT; + } + if (heap_hndl == COLLECTOR_MODULE_ERR) + { + Tprintf (0, "heaptrace: handle create failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_HEAPINIT); + return COL_ERROR_HEAPINIT; + } + TprintfT (0, "heaptrace: open_experiment %s\n", exp); + if (NULL_PTR (malloc)) + init_heap_intf (); + + const char *params = collector_interface->getParams (); + while (params) + { + if ((params[0] == 'H') && (params[1] == ':')) + { + params += 2; + break; + } + params = CALL_REAL (strchr)(params, ';'); + if (params) + params++; + } + if (params == NULL) /* Heap data collection not specified */ + return COL_ERROR_HEAPINIT; + + heap_key = collector_interface->createKey (sizeof ( int), NULL, NULL); + if (heap_key == (unsigned) - 1) + { + Tprintf (0, "heaptrace: TSD key create failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_HEAPINIT); + return COL_ERROR_HEAPINIT; + } + collector_interface->writeLog ("<profile name=\"%s\">\n", SP_JCMD_HEAPTRACE); + collector_interface->writeLog (" <profdata fname=\"%s\"/>\n", + module_interface.description); + + /* Record Heap_packet description */ + Heap_packet *pp = NULL; + collector_interface->writeLog (" <profpckt kind=\"%d\" uname=\"Heap tracing data\">\n", HEAP_PCKT); + collector_interface->writeLog (" <field name=\"LWPID\" uname=\"Lightweight process id\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.lwp_id, sizeof (pp->comm.lwp_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"THRID\" uname=\"Thread number\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.thr_id, sizeof (pp->comm.thr_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"CPUID\" uname=\"CPU id\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.cpu_id, sizeof (pp->comm.cpu_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"High resolution timestamp\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.tstamp, sizeof (pp->comm.tstamp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.frinfo, sizeof (pp->comm.frinfo) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"HTYPE\" uname=\"Heap trace function type\" offset=\"%d\" type=\"%s\"/>\n", + &pp->mtype, sizeof (pp->mtype) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"HSIZE\" uname=\"Memory size\" offset=\"%d\" type=\"%s\"/>\n", + &pp->size, sizeof (pp->size) == 4 ? "UINT32" : "UINT64"); + collector_interface->writeLog (" <field name=\"HVADDR\" uname=\"Memory address\" offset=\"%d\" type=\"%s\"/>\n", + &pp->vaddr, sizeof (pp->vaddr) == 4 ? "UINT32" : "UINT64"); + collector_interface->writeLog (" <field name=\"HOVADDR\" uname=\"Previous memory address\" offset=\"%d\" type=\"%s\"/>\n", + &pp->ovaddr, sizeof (pp->ovaddr) == 4 ? "UINT32" : "UINT64"); + collector_interface->writeLog (" </profpckt>\n"); + collector_interface->writeLog ("</profile>\n"); + return COL_ERROR_NONE; +} + +static int +start_data_collection (void) +{ + heap_mode = 1; + Tprintf (0, "heaptrace: start_data_collection\n"); + return 0; +} + +static int +stop_data_collection (void) +{ + heap_mode = 0; + Tprintf (0, "heaptrace: stop_data_collection\n"); + return 0; +} + +static int +close_experiment (void) +{ + heap_mode = 0; + heap_key = COLLECTOR_TSD_INVALID_KEY; + Tprintf (0, "heaptrace: close_experiment\n"); + return 0; +} + +static int +detach_experiment (void) +/* fork child. Clean up state but don't write to experiment */ +{ + heap_mode = 0; + heap_key = COLLECTOR_TSD_INVALID_KEY; + Tprintf (0, "heaptrace: detach_experiment\n"); + return 0; +} + +static int in_init_heap_intf = 0; // Flag that we are in init_heap_intf() + +static int +init_heap_intf () +{ + void *dlflag; + in_init_heap_intf = 1; + __real_malloc = (void*(*)(size_t))dlsym (RTLD_NEXT, "malloc"); + if (__real_malloc == NULL) + { + /* We are probably dlopened after libthread/libc, + * try to search in the previously loaded objects + */ + __real_malloc = (void*(*)(size_t))dlsym (RTLD_DEFAULT, "malloc"); + if (__real_malloc == NULL) + { + Tprintf (0, "heaptrace: ERROR: real malloc not found\n"); + in_init_heap_intf = 0; + return 1; + } + Tprintf (DBG_LT1, "heaptrace: real malloc found with RTLD_DEFAULT\n"); + dlflag = RTLD_DEFAULT; + } + else + { + Tprintf (DBG_LT1, "heaptrace: real malloc found with RTLD_NEXT\n"); + dlflag = RTLD_NEXT; + } + __real_free = (void(*)(void *))dlsym (dlflag, "free"); + __real_realloc = (void*(*)(void *, size_t))dlsym (dlflag, "realloc"); + __real_memalign = (void*(*)(size_t, size_t))dlsym (dlflag, "memalign"); + __real_calloc = (void*(*)(size_t, size_t))dlsym (dlflag, "calloc"); + __real_valloc = (void*(*)(size_t))dlsym (dlflag, "valloc"); + __real_strchr = (char*(*)(const char *, int))dlsym (dlflag, "strchr"); + Tprintf (0, "heaptrace: init_heap_intf done\n"); + in_init_heap_intf = 0; + return 0; +} + +/*------------------------------------------------------------- malloc */ + +void * +malloc (size_t size) +{ + void *ret; + int *guard; + Heap_packet hpacket; + /* Linux startup workaround */ + if (!heap_mode) + { + void *ppp = (void *) __libc_malloc (size); + Tprintf (DBG_LT4, "heaptrace: LINUX malloc(%ld, %p)...\n", (long) size, ppp); + return ppp; + } + if (NULL_PTR (malloc)) + init_heap_intf (); + if (CHCK_REENTRANCE (guard)) + { + ret = (void *) CALL_REAL (malloc)(size); + Tprintf (DBG_LT4, "heaptrace: real malloc(%ld) = %p\n", (long) size, ret); + return ret; + } + PUSH_REENTRANCE (guard); + + ret = (void *) CALL_REAL (malloc)(size); + collector_memset (&hpacket, 0, sizeof ( Heap_packet)); + hpacket.comm.tsize = sizeof ( Heap_packet); + hpacket.comm.tstamp = gethrtime (); + hpacket.mtype = MALLOC_TRACE; + hpacket.size = (Size_type) size; + hpacket.vaddr = (Vaddr_type) ret; + hpacket.ovaddr = (Vaddr_type) 0; + hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket); + collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket); + POP_REENTRANCE (guard); + return (void *) ret; +} + +/*------------------------------------------------------------- free */ + +void +free (void *ptr) +{ + int *guard; + Heap_packet hpacket; + /* Linux startup workaround */ + if (!heap_mode) + { + // Tprintf(DBG_LT4,"heaptrace: LINUX free(%p)...\n",ptr); + __libc_free (ptr); + return; + } + if (NULL_PTR (malloc)) + init_heap_intf (); + if (CHCK_REENTRANCE (guard)) + { + CALL_REAL (free)(ptr); + return; + } + if (ptr == NULL) + return; + PUSH_REENTRANCE (guard); + + /* Get a timestamp before 'free' to enforce consistency */ + hrtime_t ts = gethrtime (); + CALL_REAL (free)(ptr); + collector_memset (&hpacket, 0, sizeof ( Heap_packet)); + hpacket.comm.tsize = sizeof ( Heap_packet); + hpacket.comm.tstamp = ts; + hpacket.mtype = FREE_TRACE; + hpacket.size = (Size_type) 0; + hpacket.vaddr = (Vaddr_type) ptr; + hpacket.ovaddr = (Vaddr_type) 0; + hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket); + collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket); + POP_REENTRANCE (guard); + return; +} + +/*------------------------------------------------------------- realloc */ +void * +realloc (void *ptr, size_t size) +{ + void *ret; + int *guard; + Heap_packet hpacket; + + /* Linux startup workaround */ + if (!heap_mode) + { + void * ppp = (void *) __libc_realloc (ptr, size); + Tprintf (DBG_LT4, "heaptrace: LINUX realloc(%ld, %p->%p)...\n", + (long) size, ptr, ppp); + return ppp; + } + if (NULL_PTR (realloc)) + init_heap_intf (); + if (CHCK_REENTRANCE (guard)) + { + ret = (void *) CALL_REAL (realloc)(ptr, size); + return ret; + } + PUSH_REENTRANCE (guard); + hrtime_t ts = gethrtime (); + ret = (void *) CALL_REAL (realloc)(ptr, size); + collector_memset (&hpacket, 0, sizeof ( Heap_packet)); + hpacket.comm.tsize = sizeof ( Heap_packet); + hpacket.comm.tstamp = ts; + hpacket.mtype = REALLOC_TRACE; + hpacket.size = (Size_type) size; + hpacket.vaddr = (Vaddr_type) ret; + hpacket.ovaddr = (Vaddr_type) ptr; + hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket); + collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket); + POP_REENTRANCE (guard); + return (void *) ret; +} + +/*------------------------------------------------------------- memalign */ +void * +memalign (size_t align, size_t size) +{ + void *ret; + int *guard; + Heap_packet hpacket; + if (NULL_PTR (memalign)) + init_heap_intf (); + if (CHCK_REENTRANCE (guard)) + { + ret = (void *) CALL_REAL (memalign)(align, size); + return ret; + } + PUSH_REENTRANCE (guard); + ret = (void *) CALL_REAL (memalign)(align, size); + collector_memset (&hpacket, 0, sizeof ( Heap_packet)); + hpacket.comm.tsize = sizeof ( Heap_packet); + hpacket.comm.tstamp = gethrtime (); + hpacket.mtype = MALLOC_TRACE; + hpacket.size = (Size_type) size; + hpacket.vaddr = (Vaddr_type) ret; + hpacket.ovaddr = (Vaddr_type) 0; + hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket); + collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- valloc */ + +void * +valloc (size_t size) +{ + void *ret; + int *guard; + Heap_packet hpacket; + if (NULL_PTR (memalign)) + init_heap_intf (); + if (CHCK_REENTRANCE (guard)) + { + ret = (void *) CALL_REAL (valloc)(size); + return ret; + } + PUSH_REENTRANCE (guard); + ret = (void *) CALL_REAL (valloc)(size); + collector_memset (&hpacket, 0, sizeof ( Heap_packet)); + hpacket.comm.tsize = sizeof ( Heap_packet); + hpacket.comm.tstamp = gethrtime (); + hpacket.mtype = MALLOC_TRACE; + hpacket.size = (Size_type) size; + hpacket.vaddr = (Vaddr_type) ret; + hpacket.ovaddr = (Vaddr_type) 0; + hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket); + collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- calloc */ +void * +calloc (size_t size, size_t esize) +{ + void *ret; + int *guard; + Heap_packet hpacket; + if (NULL_PTR (calloc)) + { + if (in_init_heap_intf != 0) + return NULL; // Terminate infinite loop + init_heap_intf (); + } + if (CHCK_REENTRANCE (guard)) + { + ret = (void *) CALL_REAL (calloc)(size, esize); + return ret; + } + PUSH_REENTRANCE (guard); + ret = (void *) CALL_REAL (calloc)(size, esize); + collector_memset (&hpacket, 0, sizeof ( Heap_packet)); + hpacket.comm.tsize = sizeof ( Heap_packet); + hpacket.comm.tstamp = gethrtime (); + hpacket.mtype = MALLOC_TRACE; + hpacket.size = (Size_type) (size * esize); + hpacket.vaddr = (Vaddr_type) ret; + hpacket.ovaddr = (Vaddr_type) 0; + hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket); + collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket); + POP_REENTRANCE (guard); + return ret; +} + +/* __collector_heap_record is used to record java allocations/deallocations. + * It uses the same facilities as regular heap tracing for now. + */ +void +__collector_heap_record (int mtype, size_t size, void *vaddr) +{ + int *guard; + Heap_packet hpacket; + if (CHCK_REENTRANCE (guard)) + return; + PUSH_REENTRANCE (guard); + collector_memset (&hpacket, 0, sizeof ( Heap_packet)); + hpacket.comm.tsize = sizeof ( Heap_packet); + hpacket.comm.tstamp = gethrtime (); + hpacket.mtype = mtype; + hpacket.size = (Size_type) size; + hpacket.vaddr = (Vaddr_type) vaddr; + hpacket.ovaddr = (Vaddr_type) 0; + hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket); + collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket); + POP_REENTRANCE (guard); + return; +} diff --git a/gprofng/libcollector/hwprofile.c b/gprofng/libcollector/hwprofile.c new file mode 100644 index 0000000..6fdaf1b --- /dev/null +++ b/gprofng/libcollector/hwprofile.c @@ -0,0 +1,905 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* Hardware counter profiling */ + +#include "config.h" +#include <alloca.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <sys/syscall.h> +#include <signal.h> +#include <ucontext.h> + +#include "gp-defs.h" +#define _STRING_H 1 /* XXX MEZ: temporary workaround */ +#include "hwcdrv.h" +#include "collector_module.h" +#include "gp-experiment.h" +#include "libcol_util.h" +#include "hwprofile.h" +#include "ABS.h" +#include "tsd.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 +#define DBG_LT4 4 +#define DBG_LT5 5 + +#define SD_OFF 0 /* before start or after close she shut down process */ +#define SD_PENDING 1 /* before running real_detach_experiment() */ +#define SD_COMPLETE 2 /* after running real_detach_experiment() */ + +static int init_interface (CollectorInterface*); +static int open_experiment (const char *); +static int start_data_collection (void); +static int stop_data_collection (void); +static int close_experiment (void); +static int detach_experiment (void); +static int real_detach_experiment (void); + +static ModuleInterface module_interface ={ + SP_HWCNTR_FILE, /* description */ + init_interface, /* initInterface */ + open_experiment, /* openExperiment */ + start_data_collection, /* startDataCollection */ + stop_data_collection, /* stopDataCollection */ + close_experiment, /* closeExperiment */ + detach_experiment /* detachExperiment (fork child) */ +}; + +static CollectorInterface *collector_interface = NULL; + + +/*---------------------------------------------------------------------------*/ +/* compile options and workarounds */ + +/* Solaris: We set ITIMER_REALPROF to ensure that counters get started on + * LWPs that existed before the collector initialization. + * + * In addition, if the appropriate #define's are set, we check for: + * lost-hw-overflow -- the HW counters rollover, but the overflow + * interrupt is not generated (counters keep running) + * lost-sigemt -- the interrupt is received by the kernel, + * which stops the counters, but the kernel fails + * to deliver the signal. + */ + +/*---------------------------------------------------------------------------*/ +/* typedefs */ + +typedef enum { + HWCMODE_OFF, /* before start or after close */ + HWCMODE_SUSPEND, /* stop_data_collection called */ + HWCMODE_ACTIVE, /* counters are defined and after start_data_collection() */ + HWCMODE_ABORT /* fatal error occured. Log a message, stop recording */ +} hwc_mode_t; + +/*---------------------------------------------------------------------------*/ +/* prototypes */ +static void init_ucontexts (void); +static int hwc_initialize_handlers (void); +static void collector_record_counter (ucontext_t*, + int timecvt, + ABST_type, hrtime_t, + unsigned, uint64_t); +static void collector_hwc_ABORT (int errnum, const char *msg); +static void hwclogwrite0 (); +static void hwclogwrite (Hwcentry *); +static void set_hwc_mode (hwc_mode_t); +static void collector_sigemt_handler (int sig, siginfo_t *si, void *puc); + +/*---------------------------------------------------------------------------*/ +/* static variables */ + +/* --- user counter selections and options */ +static int hwcdef_has_memspace; /* true to indicate use of extened packets */ +static unsigned hwcdef_cnt; /* number of *active* hardware counters */ +static unsigned hwcdef_num_sampling_ctrdefs; /* ctrs that use sampling */ +static unsigned hwcdef_num_overflow_ctrdefs; /* ctrs that use overflow */ +static Hwcentry **hwcdef; /* HWC definitions */ +static int cpcN_cpuver = CPUVER_UNDEFINED; +static int hwcdrv_inited; /* Don't call hwcdrv_init() in fork_child */ +static hwcdrv_api_t *hwc_driver = NULL; +static unsigned hwprofile_tsd_key = COLLECTOR_TSD_INVALID_KEY; +static int hwprofile_tsd_sz = 0; +static volatile hwc_mode_t hwc_mode = HWCMODE_OFF; +static volatile unsigned int nthreads_in_sighandler = 0; +static volatile unsigned int sd_state = SD_OFF; + +/* --- experiment logging state */ +static CollectorModule expr_hndl = COLLECTOR_MODULE_ERR; +static ucontext_t expr_dummy_uc; // used for hacked "collector" frames +static ucontext_t expr_out_of_range_uc; // used for "out-of-range" frames +static ucontext_t expr_frozen_uc; // used for "frozen" frames +static ucontext_t expr_nopc_uc; // used for not-program-related frames +static ucontext_t expr_lostcounts_uc; // used for lost_counts frames + +/* --- signal handler state */ +static struct sigaction old_sigemt_handler; //overwritten in fork-child + +/*---------------------------------------------------------------------------*/ +/* macros */ +#define COUNTERS_ENABLED() (hwcdef_cnt) +#define gethrtime collector_interface->getHiResTime + +#ifdef DEBUG +#define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ ) +#define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ ) +#else +#define Tprintf(...) +#define TprintfT(...) +#endif + + +/*---------------------------------------------------------------------------*/ + +/* Initialization routines */ +static hwcdrv_api_t * +get_hwc_driver () +{ + if (hwc_driver == NULL) + hwc_driver = __collector_get_hwcdrv (); + return hwc_driver; +} + +static void init_module () __attribute__ ((constructor)); +static void +init_module () +{ + __collector_dlsym_guard = 1; + RegModuleFunc reg_module = (RegModuleFunc) dlsym (RTLD_DEFAULT, "__collector_register_module"); + __collector_dlsym_guard = 0; + if (reg_module == NULL) + { + TprintfT (0, "hwprofile: init_module FAILED - reg_module = NULL\n"); + return; + } + expr_hndl = reg_module (&module_interface); + if (expr_hndl == COLLECTOR_MODULE_ERR) + { + TprintfT (0, "hwprofile: ERROR: handle not created.\n"); + if (collector_interface) + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_HWCINIT); + } +} + +static int +init_interface (CollectorInterface *_collector_interface) +{ + collector_interface = _collector_interface; + return COL_ERROR_NONE; +} + +static void * +hwprofile_get_tsd () +{ + return collector_interface->getKey (hwprofile_tsd_key); +} + +static int +open_experiment (const char *exp) +{ + if (collector_interface == NULL) + { + TprintfT (0, "hwprofile: ERROR: collector_interface is null.\n"); + return COL_ERROR_HWCINIT; + } + const char *params = collector_interface->getParams (); + while (params) + { + if (__collector_strStartWith (params, "h:*") == 0) + { + /* HWC counters set by default */ + collector_interface->writeLog ("<%s %s=\"1\"/>\n", + SP_TAG_SETTING, SP_JCMD_HWC_DEFAULT); + params += 3; + break; + } + else if (__collector_strStartWith (params, "h:") == 0) + { + params += 2; + break; + } + params = CALL_UTIL (strchr)(params, ';'); + if (params) + params++; + } + if (params == NULL) /* HWC profiling not specified */ + return COL_ERROR_HWCINIT; + char *s = CALL_UTIL (strchr)(params, (int) ';'); + int sz = s ? s - params : CALL_UTIL (strlen)(params); + char *defstring = (char*) alloca (sz + 1); + CALL_UTIL (strlcpy)(defstring, params, sz + 1); + TprintfT (0, "hwprofile: open_experiment %s -- %s\n", exp, defstring); + + int err = COL_ERROR_NONE; + /* init counter library */ + if (!hwcdrv_inited) + { /* do not call hwcdrv_init() from fork-child */ + hwcdrv_inited = 1; + get_hwc_driver (); + if (hwc_driver->hwcdrv_init (collector_hwc_ABORT, &hwprofile_tsd_sz) == 0) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_HWCINIT, defstring); + TprintfT (0, "hwprofile: ERROR: hwcfuncs_init() failed\n"); + return COL_ERROR_HWCINIT; + } + + if (hwc_driver->hwcdrv_enable_mt (hwprofile_get_tsd)) + { + // It is OK to call hwcdrv_enable_mt() before tsd key is created + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_HWCINIT, defstring); + TprintfT (0, "hwprofile: ERROR: hwcdrv_enable_mt() failed\n"); + return COL_ERROR_HWCINIT; + } + + hwc_driver->hwcdrv_get_info (&cpcN_cpuver, NULL, NULL, NULL, NULL); + if (cpcN_cpuver < 0) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_HWCINIT, defstring); + TprintfT (0, "hwprofile: ERROR: hwcdrv_get_info() failed\n"); + return COL_ERROR_HWCINIT; + } + } + + if (hwprofile_tsd_sz) + { + hwprofile_tsd_key = collector_interface->createKey (hwprofile_tsd_sz, NULL, NULL); + if (hwprofile_tsd_key == COLLECTOR_TSD_INVALID_KEY) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_HWCINIT, defstring); + TprintfT (0, "hwprofile: ERROR: TSD createKey failed\n"); + return COL_ERROR_HWCINIT; + } + } + hwcdef_cnt = 0; + hwcdef_has_memspace = 0; + + /* create counters based on hwcdef[] */ + err = __collector_hwcfuncs_bind_descriptor (defstring); + if (err) + { + err = err == HWCFUNCS_ERROR_HWCINIT ? COL_ERROR_HWCINIT : COL_ERROR_HWCARGS; + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CERROR, err, defstring); + TprintfT (0, "hwprofile: ERROR: open_experiment() failed, RC=%d \n", err); + return err; + } + + /* generate an array of counter structures for each requested counter */ + hwcdef = __collector_hwcfuncs_get_ctrs (&hwcdef_cnt); + hwcdef_num_sampling_ctrdefs = hwcdef_num_overflow_ctrdefs = 0; + int idx; + for (idx = 0; idx < hwcdef_cnt; idx++) + { + if (HWCENTRY_USES_SAMPLING (hwcdef[idx])) + { + hwcdef_num_sampling_ctrdefs++; + } + else + { + hwcdef_num_overflow_ctrdefs++; + } + } + + init_ucontexts (); + + /* initialize the SIGEMT handler, and the periodic HWC checker */ + err = hwc_initialize_handlers (); + if (err != COL_ERROR_NONE) + { + hwcdef_cnt = 0; + TprintfT (0, "hwprofile: ERROR: open_experiment() failed, RC=%d \n", err); + /* log written by hwc_initialize_handlers() */ + return err; + } + + for (idx = 0; idx < hwcdef_cnt; idx++) + if (ABST_BACKTRACK_ENABLED (hwcdef[idx]->memop)) + hwcdef_has_memspace = 1; + + /* record the hwc definitions in the log, based on the counter array */ + hwclogwrite0 (); + for (idx = 0; idx < hwcdef_cnt; idx++) + hwclogwrite (hwcdef[idx]); + return COL_ERROR_NONE; +} + +int +__collector_ext_hwc_lwp_init () +{ + return get_hwc_driver ()->hwcdrv_lwp_init (); +} + +void +__collector_ext_hwc_lwp_fini () +{ + get_hwc_driver ()->hwcdrv_lwp_fini (); +} + +int +__collector_ext_hwc_lwp_suspend () +{ + return get_hwc_driver ()->hwcdrv_lwp_suspend (); +} + +int +__collector_ext_hwc_lwp_resume () +{ + return get_hwc_driver ()->hwcdrv_lwp_resume (); +} + +/* Dummy routine, used to provide a context for non-program related profiles */ +void +__collector_not_program_related () { } + +/* Dummy routine, used to provide a context for lost counts (perf_events) */ +void +__collector_hwc_samples_lost () { } + +/* Dummy routine, used to provide a context */ +void +__collector_hwcs_frozen () { } + +/* Dummy routine, used to provide a context */ +void +__collector_hwcs_out_of_range () { } +/* initialize some structures */ +static void +init_ucontexts (void) +{ + /* initialize dummy context for "collector" frames */ + getcontext (&expr_dummy_uc); + SETFUNCTIONCONTEXT (&expr_dummy_uc, NULL); + + /* initialize dummy context for "out-of-range" frames */ + getcontext (&expr_out_of_range_uc); + SETFUNCTIONCONTEXT (&expr_out_of_range_uc, &__collector_hwcs_out_of_range); + + /* initialize dummy context for "frozen" frames */ + getcontext (&expr_frozen_uc); + SETFUNCTIONCONTEXT (&expr_frozen_uc, &__collector_hwcs_frozen); + + /* initialize dummy context for non-program-related frames */ + getcontext (&expr_nopc_uc); + SETFUNCTIONCONTEXT (&expr_nopc_uc, &__collector_not_program_related); + + /* initialize dummy context for lost-counts-related frames */ + getcontext (&expr_lostcounts_uc); + SETFUNCTIONCONTEXT (&expr_lostcounts_uc, &__collector_hwc_samples_lost); +} +/* initialize the signal handler */ +static int +hwc_initialize_handlers (void) +{ + /* install the signal handler for SIGEMT */ + struct sigaction oact; + if (__collector_sigaction (HWCFUNCS_SIGNAL, NULL, &oact) != 0) + { + TprintfT (0, "hwc_initialize_handlers(): ERROR: hwc_initialize_handlers(): __collector_sigaction() failed to get oact\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">old handler could not be determined</event>\n", SP_JCMD_CERROR, COL_ERROR_HWCINIT); + return COL_ERROR_HWCINIT; + } + if (oact.sa_sigaction == collector_sigemt_handler) + { + /* signal handler is already in place; we are probably in a fork-child */ + TprintfT (DBG_LT1, "hwc_initialize_handlers(): hwc_initialize_handlers() collector_sigemt_handler already installed\n"); + } + else + { + /* set our signal handler */ + struct sigaction c_act; + CALL_UTIL (memset)(&c_act, 0, sizeof c_act); + sigemptyset (&c_act.sa_mask); + sigaddset (&c_act.sa_mask, SIGPROF); /* block SIGPROF delivery in handler */ + /* XXXX should probably also block sample_sig & pause_sig */ + c_act.sa_sigaction = collector_sigemt_handler; /* note: used to set sa_handler instead */ + c_act.sa_flags = SA_RESTART | SA_SIGINFO; + if (__collector_sigaction (HWCFUNCS_SIGNAL, &c_act, &old_sigemt_handler) != 0) + { + TprintfT (0, "hwc_initialize_handlers(): ERROR: hwc_initialize_handlers(): __collector_sigaction() failed to set cact\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">event handler could not be installed</event>\n", SP_JCMD_CERROR, COL_ERROR_HWCINIT); + return COL_ERROR_HWCINIT; + } + } + return COL_ERROR_NONE; +} + +static int +close_experiment (void) +{ + /* note: stop_data_collection() should have already been called by + * collector_close_experiment() + */ + if (!COUNTERS_ENABLED ()) + return COL_ERROR_NONE; + detach_experiment (); + + /* cpc or libperfctr may still generate sigemts for a while */ + /* verify that SIGEMT handler is still installed */ + /* (still required with sigaction interposition and management, + since interposition is not done for attach experiments) + */ + struct sigaction curr; + if (__collector_sigaction (HWCFUNCS_SIGNAL, NULL, &curr) == -1) + { + TprintfT (0, "hwprofile close_experiment: ERROR: hwc sigaction check failed: errno=%d\n", errno); + } + else if (curr.sa_sigaction != collector_sigemt_handler) + { + TprintfT (DBG_LT1, "hwprofile close_experiment: WARNING: collector sigemt handler replaced by 0x%p!\n", curr.sa_handler); + (void) collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">0x%p</event>\n", + SP_JCMD_CWARN, COL_WARN_SIGEMT, curr.sa_handler); + } + else + TprintfT (DBG_LT1, "hwprofile close_experiment: collector sigemt handler integrity verified!\n"); + TprintfT (0, "hwprofile: close_experiment\n"); + return 0; +} + +static int +detach_experiment (void) +{ + /* fork child. Clean up state but don't write to experiment */ + /* note: stop_data_collection() has already been called by the fork_prologue */ + // detach_experiment() can be called asynchronously + // from anywhere, even from within a sigemt handler + // via DBX detach. + // Important: stop_data_collection() _must_ be called + // before detach_experiment() is called. + if (!COUNTERS_ENABLED ()) + return COL_ERROR_NONE; + TprintfT (0, "hwprofile: detach_experiment()\n"); + if (SD_OFF != __collector_cas_32 (&sd_state, SD_OFF, SD_PENDING)) + return 0; + // one and only one call should ever make it here here. + if (hwc_mode == HWCMODE_ACTIVE) + { + TprintfT (0, "hwprofile: ERROR: stop_data_collection() should have been called before detach_experiment()\n"); + stop_data_collection (); + } + + // Assumption: The only calls to sigemt_handler + // we should see at this point + // will be those that were already in-flight before + // stop_new_sigemts() was called. + if (nthreads_in_sighandler > 0) + { + // sigemt handlers should see + // SD_PENDING and should call real_detach_experiment() + // when the last handler is finished. + TprintfT (DBG_LT1, "hwprofile: detach in the middle of signal handler.\n"); + return 0; + } + + // If we get here, there should be no remaining + // sigemt handlers. However, we don't really know + // if there were ever any in flight, so call + // real_detach_experiment() here: + return real_detach_experiment (); // multiple calls to this OK +} + +static int +real_detach_experiment (void) +{ + /*multiple calls to this routine are OK.*/ + if (SD_PENDING != __collector_cas_32 (&sd_state, SD_PENDING, SD_COMPLETE)) + return 0; + // only the first caller to this routine should get here. + hwcdef_cnt = 0; /* since now deinstalled */ + hwcdef = NULL; + set_hwc_mode (HWCMODE_OFF); + if (SD_COMPLETE != __collector_cas_32 (&sd_state, SD_COMPLETE, SD_OFF)) + { + TprintfT (0, "hwprofile: ERROR: unexpected sd_state in real_detach_experiment()\n"); + sd_state = SD_OFF; + } + hwprofile_tsd_key = COLLECTOR_TSD_INVALID_KEY; + TprintfT (DBG_LT0, "hwprofile: real_detach_experiment() detached from experiment.\n"); + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Record counter values. */ + +/* <value> should already be adjusted to be "zero-based" (counting up from 0).*/ +static void +collector_record_counter_internal (ucontext_t *ucp, int timecvt, + ABST_type ABS_memop, hrtime_t time, + unsigned tag, uint64_t value, uint64_t pc, + uint64_t va, uint64_t latency, + uint64_t data_source) +{ + MHwcntr_packet pckt; + CALL_UTIL (memset)(&pckt, 0, sizeof ( MHwcntr_packet)); + pckt.comm.tstamp = time; + pckt.tag = tag; + if (timecvt > 1) + { + if (HWCVAL_HAS_ERR (value)) + { + value = HWCVAL_CLR_ERR (value); + value *= timecvt; + value = HWCVAL_SET_ERR (value); + } + else + value *= timecvt; + } + pckt.interval = value; + pckt.comm.type = HW_PCKT; + pckt.comm.tsize = sizeof (Hwcntr_packet); + TprintfT (DBG_LT4, "hwprofile: %llu sample %lld tag %u recorded\n", + (unsigned long long) time, (long long) value, tag); + if (ABS_memop == ABST_NOPC) + ucp = &expr_nopc_uc; + pckt.comm.frinfo = collector_interface->getFrameInfo (expr_hndl, pckt.comm.tstamp, FRINFO_FROM_UC, ucp); + collector_interface->writeDataRecord (expr_hndl, (Common_packet*) & pckt); +} + +static void +collector_record_counter (ucontext_t *ucp, int timecvt, ABST_type ABS_memop, + hrtime_t time, unsigned tag, uint64_t value) +{ + collector_record_counter_internal (ucp, timecvt, ABS_memop, time, tag, value, + HWCFUNCS_INVALID_U64, HWCFUNCS_INVALID_U64, + HWCFUNCS_INVALID_U64, HWCFUNCS_INVALID_U64); +} + + +/*---------------------------------------------------------------------------*/ +/* Signal handlers */ + +/* SIGEMT -- relayed from libcpc, when the counter overflows */ + +/* Generates the appropriate event or events, and resets the counters */ +static void +collector_sigemt_handler (int sig, siginfo_t *si, void *puc) +{ + int rc; + hwc_event_t sample, lost_samples; + if (sig != HWCFUNCS_SIGNAL) + { + TprintfT (0, "hwprofile: ERROR: %s: unexpected signal %d\n", "collector_sigemt_handler", sig); + return; + } + if (!COUNTERS_ENABLED ()) + { /* apparently deinstalled */ + TprintfT (0, "hwprofile: WARNING: SIGEMT detected after close_experiment()\n"); + /* kills future sigemts since hwcdrv_sighlr_restart() not called */ + return; + } + + /* Typically, we expect HWC overflow signals to come from the kernel: si_code > 0. + * On Linux, however, dbx might be "forwarding" a signal using tkill()/tgkill(). + * For more information on what si_code values can be expected on Linux, check: + * cmn_components/Collector_Interface/hwcdrv_pcl.c hwcdrv_overflow() + * cmn_components/Collector_Interface/hwcdrv_perfctr.c hdrv_perfctr_overflow() + */ + if (puc == NULL || si == NULL || (si->si_code <= 0 && si->si_code != SI_TKILL)) + { + TprintfT (DBG_LT3, "hwprofile: collector_sigemt_handler SIG%02d\n", sig); + if (old_sigemt_handler.sa_handler == SIG_DFL) + __collector_SIGDFL_handler (HWCFUNCS_SIGNAL); + else if (old_sigemt_handler.sa_handler != SIG_IGN && + old_sigemt_handler.sa_sigaction != &collector_sigemt_handler) + { + /* Redirect the signal to the previous signal handler */ + (old_sigemt_handler.sa_sigaction)(sig, si, puc); + TprintfT (DBG_LT1, "hwprofile: collector_sigemt_handler SIG%02d redirected to original handler\n", sig); + } + return; + } + rc = get_hwc_driver ()->hwcdrv_overflow (si, &sample, &lost_samples); + if (rc) + { + /* hwcdrv_sighlr_restart() should not be called */ + TprintfT (0, "hwprofile: ERROR: collector_sigemt_handler: hwcdrv_overflow() failed\n"); + return; + } + + if (hwc_mode == HWCMODE_ACTIVE) + { + /* record the event only if counters are active */ + /* The following has been copied from dispatcher.c */ +#if ARCH(SPARC) + /* 23340823 signal handler third argument should point to a ucontext_t */ + /* Convert sigcontext to ucontext_t on sparc-Linux */ + ucontext_t uctxmem; + struct sigcontext *sctx = (struct sigcontext*) puc; + ucontext_t *uctx = &uctxmem; + uctx->uc_link = NULL; +#if WSIZE(32) + uctx->uc_mcontext.gregs[REG_PC] = sctx->si_regs.pc; + __collector_memcpy (&uctx->uc_mcontext.gregs[3], + sctx->si_regs.u_regs, + sizeof (sctx->si_regs.u_regs)); +#else + uctx->uc_mcontext.mc_gregs[MC_PC] = sctx->sigc_regs.tpc; + __collector_memcpy (&uctx->uc_mcontext.mc_gregs[3], + sctx->sigc_regs.u_regs, + sizeof (sctx->sigc_regs.u_regs)); +#endif /* WSIZE() */ +#else + ucontext_t *uctx = (ucontext_t*) puc; +#endif /* ARCH() */ + + for (int ii = 0; ii < hwcdef_cnt; ii++) + if (lost_samples.ce_pic[ii]) + collector_record_counter (&expr_lostcounts_uc, hwcdef[ii]->timecvt, + hwcdef[ii]->memop, lost_samples.ce_hrt, + hwcdef[ii]->sort_order, lost_samples.ce_pic[ii]); + for (int ii = 0; ii < hwcdef_cnt; ii++) + if (sample.ce_pic[ii]) + collector_record_counter (uctx, hwcdef[ii]->timecvt, + hwcdef[ii]->memop, sample.ce_hrt, + hwcdef[ii]->sort_order, sample.ce_pic[ii]); + } + rc = get_hwc_driver ()->hwcdrv_sighlr_restart (NULL); +} +/* SIGPROF -- not installed as handler, but + * __collector_ext_hwc_check: called by (SIGPROF) dispatcher. + * Periodical check of integrity of HWC count/signal mechanism, + * as required for various chip/system bugs/workarounds. + */ +void +__collector_ext_hwc_check (siginfo_t *info, ucontext_t *vcontext) { } + +/*---------------------------------------------------------------------------*/ +int +collector_sigemt_sigaction (const struct sigaction *nact, + struct sigaction *oact) +{ + struct sigaction oact_check; + /* Error codes and messages that refer to HWC are tricky. + * E.g., HWC profiling might not even be on; we might + * encounter an error here simply because the user is + * trying to set a handler for a signal that happens to + * be HWCFUNCS_SIGNAL, which we aren't even using. + */ + if (__collector_sigaction (HWCFUNCS_SIGNAL, NULL, &oact_check) != 0) + { + TprintfT (0, "hwprofile: ERROR: collector_sigemt_sigaction(): request to set handler for signal %d, but check on existing handler failed\n", HWCFUNCS_SIGNAL); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">old handler for signal %d could not be determined</event>\n", SP_JCMD_CERROR, COL_ERROR_HWCINIT, HWCFUNCS_SIGNAL); + return COL_ERROR_HWCINIT; + } + + if (oact_check.sa_sigaction == collector_sigemt_handler) + { + /* dispatcher is in place, so nact/oact apply to old_sigemt_handler */ + if (oact != NULL) + { + oact->sa_handler = old_sigemt_handler.sa_handler; + oact->sa_mask = old_sigemt_handler.sa_mask; + oact->sa_flags = old_sigemt_handler.sa_flags; + } + if (nact != NULL) + { + old_sigemt_handler.sa_handler = nact->sa_handler; + old_sigemt_handler.sa_mask = nact->sa_mask; + old_sigemt_handler.sa_flags = nact->sa_flags; + } + return COL_ERROR_NONE; + } + else /* no dispatcher in place, so just act like normal sigaction() */ + return __collector_sigaction (HWCFUNCS_SIGNAL, nact, oact); +} + +static void +collector_hwc_ABORT (int errnum, const char *msg) +{ + TprintfT (0, "hwprofile: collector_hwc_ABORT: [%d] %s\n", errnum, msg); + if (hwc_mode == HWCMODE_ABORT) /* HWC collection already aborted! */ + return; + set_hwc_mode (HWCMODE_ABORT); /* set global flag to disable handlers and indicate abort */ + + /* Write the error message to the experiment */ + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s: errno=%d</event>\n", + SP_JCMD_CERROR, COL_ERROR_HWCFAIL, msg, errnum); + +#ifdef REAL_DEBUG + abort (); +#else + TprintfT (0, "hwprofile: Continuing without HWC collection...\n"); +#endif +} + +static int +start_data_collection (void) +{ + hwc_mode_t old_mode = hwc_mode; + if (!COUNTERS_ENABLED ()) + return COL_ERROR_NONE; + TprintfT (0, "hwprofile: start_data_collection (hwc_mode=%d)\n", old_mode); + switch (old_mode) + { + case HWCMODE_OFF: + if (get_hwc_driver ()->hwcdrv_start ()) + { + TprintfT (0, "hwprofile: ERROR: start_data_collection() failed in hwcdrv_start()\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s: errno=%d</event>\n", + SP_JCMD_CERROR, COL_ERROR_HWCFAIL, + "start_data_collection()", errno); + return COL_ERROR_HWCINIT; + } + set_hwc_mode (HWCMODE_ACTIVE); /* start handling events on signals */ + break; + case HWCMODE_SUSPEND: + if (get_hwc_driver ()->hwcdrv_lwp_resume ()) + { + TprintfT (0, "hwprofile: ERROR: start_data_collection() failed in hwcdrv_lwp_resume()\n"); + /* ignore errors from lwp_resume() */ + } + set_hwc_mode (HWCMODE_ACTIVE); /* start handling events on signals */ + break; + default: + TprintfT (0, "hwprofile: ERROR: start_data_collection() invalid mode\n"); + return COL_ERROR_HWCINIT; + } + return COL_ERROR_NONE; +} + +static int +stop_data_collection (void) +{ + hwc_mode_t old_mode = hwc_mode; + if (!COUNTERS_ENABLED ()) + return COL_ERROR_NONE; + TprintfT (0, "hwprofile: stop_data_collection (hwc_mode=%d)\n", old_mode); + switch (old_mode) + { + case HWCMODE_SUSPEND: + return COL_ERROR_NONE; + case HWCMODE_ACTIVE: + set_hwc_mode (HWCMODE_SUSPEND); /* stop handling signals */ + break; + default: + /* Don't change the mode, but attempt to suspend anyway... */ + break; + } + + if (get_hwc_driver ()->hwcdrv_lwp_suspend ()) + /* ignore errors from lwp_suspend() */ + TprintfT (0, "hwprofile: ERROR: stop_data_collection() failed in hwcdrv_lwp_suspend()\n"); + + /* + * hwcdrv_lwp_suspend() cannot guarantee that all SIGEMTs will stop + * but hwc_mode will prevent logging and counters will overflow once + * then stay frozen. + */ + /* There may still be pending SIGEMTs so don't reset the SIG_DFL handler. + */ + /* see comment in dispatcher.c */ + /* ret = __collector_sigaction( SIGEMT, &old_sigemt_handler, NULL ); */ + return COL_ERROR_NONE; +} + +/*---------------------------------------------------------------------------*/ + +/* utilities */ +static void +set_hwc_mode (hwc_mode_t md) +{ + TprintfT (DBG_LT1, "hwprofile: set_hwc_mode(%d)\n", md); + hwc_mode = md; +} + +int +__collector_ext_hwc_active () +{ + return (hwc_mode == HWCMODE_ACTIVE); +} + +static void +hwclogwrite0 () +{ + collector_interface->writeLog ("<profdata fname=\"%s\"/>\n", + module_interface.description); + /* Record Hwcntr_packet description */ + Hwcntr_packet *pp = NULL; + collector_interface->writeLog ("<profpckt kind=\"%d\" uname=\"" STXT ("Hardware counter profiling data") "\">\n", HW_PCKT); + collector_interface->writeLog (" <field name=\"LWPID\" uname=\"" STXT ("Lightweight process id") "\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.lwp_id, sizeof (pp->comm.lwp_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"THRID\" uname=\"" STXT ("Thread number") "\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.thr_id, sizeof (pp->comm.thr_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"CPUID\" uname=\"" STXT ("CPU id") "\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.cpu_id, sizeof (pp->comm.cpu_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"" STXT ("High resolution timestamp") "\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.tstamp, sizeof (pp->comm.tstamp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.frinfo, sizeof (pp->comm.frinfo) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"HWCTAG\" uname=\"" STXT ("Hardware counter index") "\" offset=\"%d\" type=\"%s\"/>\n", + &pp->tag, sizeof (pp->tag) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"HWCINT\" uname=\"" STXT ("Hardware counter interval") "\" offset=\"%d\" type=\"%s\"/>\n", + &pp->interval, sizeof (pp->interval) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog ("</profpckt>\n"); + if (hwcdef_has_memspace) + { + /* Record MHwcntr_packet description */ + MHwcntr_packet *xpp = NULL; + collector_interface->writeLog ("<profpckt kind=\"%d\" uname=\"" STXT ("Hardware counter profiling data") "\">\n", MHWC_PCKT); + collector_interface->writeLog (" <field name=\"LWPID\" uname=\"" STXT ("Lightweight process id") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->comm.lwp_id, sizeof (xpp->comm.lwp_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"THRID\" uname=\"" STXT ("Thread number") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->comm.thr_id, sizeof (xpp->comm.thr_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"CPUID\" uname=\"" STXT ("CPU id") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->comm.cpu_id, sizeof (xpp->comm.cpu_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"" STXT ("High resolution timestamp") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->comm.tstamp, sizeof (xpp->comm.tstamp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->comm.frinfo, sizeof (xpp->comm.frinfo) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"HWCTAG\" uname=\"" STXT ("Hardware counter index") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->tag, sizeof (xpp->tag) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"HWCINT\" uname=\"" STXT ("Hardware counter interval") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->interval, sizeof (xpp->interval) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"VADDR\" uname=\"" STXT ("Virtual address (data)") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->ea_vaddr, sizeof (xpp->ea_vaddr) == 4 ? "UINT32" : "UINT64"); + collector_interface->writeLog (" <field name=\"PADDR\" uname=\"" STXT ("Physical address (data)") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->ea_paddr, sizeof (xpp->ea_paddr) == 4 ? "UINT32" : "UINT64"); + collector_interface->writeLog (" <field name=\"VIRTPC\" uname=\"" STXT ("Virtual address (instruction)") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->pc_vaddr, sizeof (xpp->pc_vaddr) == 4 ? "UINT32" : "UINT64"); + collector_interface->writeLog (" <field name=\"PHYSPC\" uname=\"" STXT ("Physical address (instruction)") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->pc_paddr, sizeof (xpp->pc_paddr) == 4 ? "UINT32" : "UINT64"); + collector_interface->writeLog (" <field name=\"EA_PAGESIZE\" uname=\"" STXT ("Page size (data)") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->ea_pagesz, sizeof (xpp->ea_pagesz) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"PC_PAGESIZE\" uname=\"" STXT ("Page size (instruction)") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->pc_pagesz, sizeof (xpp->pc_pagesz) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"EA_LGRP\" uname=\"" STXT ("Page locality group (data)") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->ea_lgrp, sizeof (xpp->ea_lgrp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"PC_LGRP\" uname=\"" STXT ("Page locality group (instruction)") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->pc_lgrp, sizeof (xpp->pc_lgrp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"LWP_LGRP_HOME\" uname=\"" STXT ("LWP home lgroup id") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->lgrp_lwp, sizeof (xpp->lgrp_lwp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"PS_LGRP_HOME\" uname=\"" STXT ("Process home lgroup id") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->lgrp_ps, sizeof (xpp->lgrp_ps) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"MEM_LAT\" uname=\"" STXT ("Memory Latency Cycles") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->latency, sizeof (xpp->latency) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"MEM_SRC\" uname=\"" STXT ("Memory Data Source") "\" offset=\"%d\" type=\"%s\"/>\n", + &xpp->data_source, sizeof (xpp->data_source) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog ("</profpckt>\n"); + } +} + +static void +hwclogwrite (Hwcentry * ctr) +{ + TprintfT (DBG_LT1, "hwprofile: writeLog(%s %u %s %d %u %d)\n", + SP_JCMD_HW_COUNTER, cpcN_cpuver, ctr->name ? ctr->name : "NULL", + ctr->val, ctr->sort_order, ctr->memop); + collector_interface->writeLog ("<profile name=\"%s\"", SP_JCMD_HW_COUNTER); + collector_interface->writeLog (" cpuver=\"%u\"", cpcN_cpuver); + collector_interface->writeLog (" hwcname=\"%s\"", ctr->name); + collector_interface->writeLog (" int_name=\"%s\"", ctr->int_name); + collector_interface->writeLog (" interval=\"%d\"", ctr->val); + collector_interface->writeLog (" tag=\"%u\"", ctr->sort_order); + collector_interface->writeLog (" memop=\"%d\"", ctr->memop); + collector_interface->writeLog ("/>\n"); +} diff --git a/gprofng/libcollector/hwprofile.h b/gprofng/libcollector/hwprofile.h new file mode 100644 index 0000000..4d01bf1 --- /dev/null +++ b/gprofng/libcollector/hwprofile.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _HWPROFILE_H +#define _HWPROFILE_H + +#include <data_pckts.h> + +typedef struct Hwcntr_packet +{ /* HW counter profiling packet */ + Common_packet comm; + uint32_t tag; /* hw counter index, register */ + uint64_t interval; /* overflow value */ +} Hwcntr_packet; + +typedef struct MHwcntr_packet +{ /* extended (superset) Hwcntr_packet */ + Common_packet comm; + uint32_t tag; /* hw counter index, register */ + uint64_t interval; /* overflow value */ + Vaddr_type ea_vaddr; /* virtual addr causing HWC event */ + Vaddr_type pc_vaddr; /* candidate eventPC */ + uint64_t ea_paddr; /* physical address for ea_vaddr */ + uint64_t pc_paddr; /* physical address for pc_vaddr */ + uint64_t ea_pagesz; /* pagesz (bytes) for ea_paddr */ + uint64_t pc_pagesz; /* pagesz (bytes) for pc_paddr */ + uint32_t ea_lgrp; /* latency group of ea_paddr */ + uint32_t pc_lgrp; /* latency group of pc_paddr */ + uint32_t lgrp_lwp; /* locality group of lwp */ + uint32_t lgrp_ps; /* locality group of process */ + uint64_t latency; /* latency in cycles (sampling only) */ + uint64_t data_source; /* data source (sampling only) */ +} MHwcntr_packet; + +#if ARCH(SPARC) +#define CONTEXT_PC MC_PC +#define CONTEXT_SP MC_O6 +#define CONTEXT_FP MC_O7 +#define SETFUNCTIONCONTEXT(ucp,funcp) \ + (ucp)->uc_mcontext.gregs[CONTEXT_PC] = (greg_t)(funcp); \ + (ucp)->uc_mcontext.gregs[CONTEXT_SP] = 0; \ + (ucp)->uc_mcontext.gregs[CONTEXT_FP] = 0; + +#elif ARCH(Intel) +#include <sys/reg.h> + +#if WSIZE(64) +#define CONTEXT_PC REG_RIP +#define CONTEXT_FP REG_RBP +#define CONTEXT_SP REG_RSP + +#elif WSIZE(32) +#define CONTEXT_PC REG_EIP +#define CONTEXT_FP REG_EBP +#define CONTEXT_SP REG_ESP +#endif /* WSIZE() */ +#define SETFUNCTIONCONTEXT(ucp,funcp) \ + (ucp)->uc_mcontext.gregs[CONTEXT_PC] = (greg_t)(funcp); \ + (ucp)->uc_mcontext.gregs[CONTEXT_SP] = 0; \ + (ucp)->uc_mcontext.gregs[CONTEXT_FP] = 0; + +#elif ARCH(Aarch64) +#define CONTEXT_PC 15 +#define CONTEXT_FP 14 +#define CONTEXT_SP 13 +#define SETFUNCTIONCONTEXT(ucp,funcp) \ + (ucp)->uc_mcontext.regs[CONTEXT_PC] = (greg_t)(funcp); \ + (ucp)->uc_mcontext.regs[CONTEXT_SP] = 0; \ + (ucp)->uc_mcontext.regs[CONTEXT_FP] = 0; +#endif /* ARCH() */ + +#endif diff --git a/gprofng/libcollector/iolib.c b/gprofng/libcollector/iolib.c new file mode 100644 index 0000000..6881f02 --- /dev/null +++ b/gprofng/libcollector/iolib.c @@ -0,0 +1,1156 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <dlfcn.h> +#include <pthread.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include "gp-defs.h" +#include "collector.h" +#include "gp-experiment.h" +#include "memmgr.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +/* ------------- Data and prototypes for block management --------- */ +#define IO_BLK 0 /* Concurrent requests */ +#define IO_SEQ 1 /* All requests are sequential, f.e. JAVA_CLASSES */ +#define IO_TXT 2 /* Sequential requests. Text strings. */ +#define ST_INIT 0 /* Initial state. Not allocated */ +#define ST_FREE 1 /* Available */ +#define ST_BUSY 2 /* Not available */ + +/* IO_BLK, IO_SEQ */ +#define NCHUNKS 64 + +/* IO_TXT */ +#define NBUFS 64 /* Number of text buffers */ +#define CUR_BUSY(x) ((uint32_t) ((x)>>63)) /* bit 63 */ +#define CUR_INDX(x) ((uint32_t) (((x)>>57) & 0x3fULL)) /* bits 62:57 */ +#define CUR_FOFF(x) ((x) & 0x01ffffffffffffffULL) /* bits 56: 0 */ +#define CUR_MAKE(busy, indx, foff) ((((uint64_t)(busy))<<63) | (((uint64_t)(indx))<<57) | ((uint64_t)(foff)) ) + +typedef struct Buffer +{ + uint8_t *vaddr; + uint32_t left; /* bytes left */ + uint32_t state; /* ST_FREE or ST_BUSY */ +} Buffer; + +typedef struct DataHandle +{ + Pckt_type kind; /* obsolete (to be removed) */ + int iotype; /* IO_BLK, IO_SEQ, IO_TXT */ + int active; + char fname[MAXPATHLEN]; /* data file name */ + + /* IO_BLK, IO_SEQ */ + uint32_t nflow; /* number of data flows */ + uint32_t *blkstate; /* block states, nflow*NCHUNKS array */ + uint32_t *blkoff; /* block offset, nflow*NCHUNKS array */ + uint32_t nchnk; /* number of active chunks, probably small for IO_BLK */ + uint8_t *chunks[NCHUNKS]; /* chunks (nflow contiguous blocks in virtual memory) */ + uint32_t chblk[NCHUNKS]; /* number of active blocks in a chunk */ + uint32_t nblk; /* number of blocks in data file */ + int exempt; /* if exempt from experiment size limit */ + + /* IO_TXT */ + Buffer *buffers; /* array of text buffers */ + uint64_t curpos; /* current buffer and file offset */ +} DataHandle; + +#define PROFILE_DATAHNDL_MAX 16 +static DataHandle data_hndls[PROFILE_DATAHNDL_MAX]; +static int initialized = 0; +static long blksz; /* Block size. Multiple of page size. Power of two to make (x%blksz)==(x&(blksz-1)) fast. */ +static long log2blksz; /* log2(blksz) to make (x/blksz)==(x>>log2blksz) fast. */ +static uint32_t size_limit; /* Experiment size limit */ +static uint32_t cur_size; /* Current experiment size */ +static void init (); +static void deleteHandle (DataHandle *hndl); +static int exp_size_ck (int nblocks, char *fname); + +/* IO_BLK, IO_SEQ */ +static int allocateChunk (DataHandle *hndl, unsigned ichunk); +static uint8_t *getBlock (DataHandle *hndl, unsigned iflow, unsigned ichunk); +static int remapBlock (DataHandle *hndl, unsigned iflow, unsigned ichunk); +static int newBlock (DataHandle *hndl, unsigned iflow, unsigned ichunk); +static void deleteBlock (DataHandle *hndl, unsigned iflow, unsigned ichunk); + +/* IO_TXT */ +static int is_not_the_log_file (char *fname); +static int mapBuffer (char *fname, Buffer *buf, off64_t foff); +static int newBuffer (DataHandle *hndl, uint64_t pos); +static void writeBuffer (Buffer *buf, int blk_off, char *src, int len); +static void deleteBuffer (Buffer *buf); + +/* + * Common buffer management routines + */ +static void +init () +{ + /* set the block size */ + long pgsz = CALL_UTIL (sysconf)(_SC_PAGESIZE); + blksz = pgsz; + log2blksz = 16; /* ensure a minimum size */ + while ((1 << log2blksz) < blksz) + log2blksz += 1; + blksz = 1L << log2blksz; /* ensure that blksz is a power of two */ + TprintfT (DBG_LT1, "iolib init: page size=%ld (0x%lx) blksz=%ld (0x%lx) log2blksz=%ld\n", + pgsz, pgsz, (long) blksz, (long) blksz, (long) log2blksz); + size_limit = 0; + cur_size = 0; + initialized = 1; +} + +DataHandle * +__collector_create_handle (char *descp) +{ + int exempt = 0; + char *desc = descp; + if (desc[0] == '*') + { + desc++; + exempt = 1; + } + if (!initialized) + init (); + + /* set up header for file, file name, etc. */ + if (__collector_exp_dir_name == NULL) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">__collector_exp_dir_name==NULL</event>\n", + SP_JCMD_CERROR, COL_ERROR_EXPOPEN); + return NULL; + } + char fname[MAXPATHLEN]; + CALL_UTIL (strlcpy)(fname, __collector_exp_dir_name, sizeof (fname)); + CALL_UTIL (strlcat)(fname, "/", sizeof (fname)); + Pckt_type kind = 0; + int iotype = IO_BLK; + if (__collector_strcmp (desc, SP_HEAPTRACE_FILE) == 0) + kind = HEAP_PCKT; + else if (__collector_strcmp (desc, SP_SYNCTRACE_FILE) == 0) + kind = SYNC_PCKT; + else if (__collector_strcmp (desc, SP_IOTRACE_FILE) == 0) + kind = IOTRACE_PCKT; + else if (__collector_strcmp (desc, SP_RACETRACE_FILE) == 0) + kind = RACE_PCKT; + else if (__collector_strcmp (desc, SP_PROFILE_FILE) == 0) + kind = PROF_PCKT; + else if (__collector_strcmp (desc, SP_OMPTRACE_FILE) == 0) + kind = OMP_PCKT; + else if (__collector_strcmp (desc, SP_HWCNTR_FILE) == 0) + kind = HW_PCKT; + else if (__collector_strcmp (desc, SP_DEADLOCK_FILE) == 0) + kind = DEADLOCK_PCKT; + else if (__collector_strcmp (desc, SP_FRINFO_FILE) == 0) + CALL_UTIL (strlcat)(fname, "data.", sizeof (fname)); + else if (__collector_strcmp (desc, SP_LOG_FILE) == 0) + iotype = IO_TXT; + else if (__collector_strcmp (desc, SP_MAP_FILE) == 0) + iotype = IO_TXT; + else if (__collector_strcmp (desc, SP_JCLASSES_FILE) == 0) + iotype = IO_SEQ; + else + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">iolib unknown file desc %s</event>\n", + SP_JCMD_CERROR, COL_ERROR_EXPOPEN, desc); + return NULL; + } + + CALL_UTIL (strlcat)(fname, desc, sizeof (fname)); + TprintfT (DBG_LT1, "createHandle calling open on fname = `%s', desc = `%s' %s\n", + fname, desc, (exempt == 0 ? "non-exempt" : "exempt")); + + /* allocate a handle -- not mt-safe */ + DataHandle *hndl = NULL; + for (int i = 0; i < PROFILE_DATAHNDL_MAX; ++i) + if (data_hndls[i].active == 0) + { + hndl = &data_hndls[i]; + break; + } + + /* out of handles? */ + if (hndl == NULL) + { + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_NOHNDL, fname); + return NULL; + } + + hndl->kind = kind; + hndl->nblk = 0; + hndl->exempt = exempt; + CALL_UTIL (strlcpy)(hndl->fname, fname, sizeof (hndl->fname)); + int fd = CALL_UTIL (open)(hndl->fname, + O_RDWR | O_CREAT | O_TRUNC | O_EXCL, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) + { + TprintfT (0, "createHandle open failed -- hndl->fname = `%s', SP_LOG_FILE = `%s': %s\n", + hndl->fname, SP_LOG_FILE, CALL_UTIL (strerror)(errno)); + if (is_not_the_log_file (hndl->fname) == 0) + { + char errbuf[4096]; + /* If we are trying to create the handle for the log file, write to stderr, not the experiment */ + CALL_UTIL (snprintf)(errbuf, sizeof (errbuf), + "create_handle: COL_ERROR_LOG_OPEN %s: %s\n", hndl->fname, CALL_UTIL (strerror)(errno)); + CALL_UTIL (write)(2, errbuf, CALL_UTIL (strlen)(errbuf)); + + } + else + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s: create_handle</event>\n", + SP_JCMD_CERROR, COL_ERROR_FILEOPN, errno, hndl->fname); + return NULL; + } + CALL_UTIL (close)(fd); + + hndl->iotype = iotype; + if (hndl->iotype == IO_TXT) + { + /* allocate our buffers in virtual memory */ + /* later, we will remap buffers individually to the file */ + uint8_t *memory = (uint8_t*) CALL_UTIL (mmap64)(0, + (size_t) (NBUFS * blksz), + PROT_READ | PROT_WRITE, +#if ARCH(SPARC) + MAP_SHARED | MAP_ANON, +#else + MAP_PRIVATE | MAP_ANON, +#endif + -1, + (off64_t) 0); + if (memory == MAP_FAILED) + { + TprintfT (0, "create_handle: can't mmap MAP_ANON (for %s): %s\n", hndl->fname, CALL_UTIL (strerror)(errno)); + /* see if this is the log file */ + if (is_not_the_log_file (hndl->fname) == 0) + { + /* If we are trying to map the log file, write to stderr, not to the experiment */ + char errbuf[4096]; + CALL_UTIL (snprintf)(errbuf, sizeof (errbuf), + "create_handle: can't mmap MAP_ANON (for %s): %s\n", hndl->fname, CALL_UTIL (strerror)(errno)); + CALL_UTIL (write)(2, errbuf, CALL_UTIL (strlen)(errbuf)); + } + else /* write the error message into the experiment */ + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">MAP_ANON (for %s); create_handle</event>\n", + SP_JCMD_CERROR, COL_ERROR_FILEMAP, errno, hndl->fname); + return NULL; + } + TprintfT (DBG_LT2, " create_handle IO_TXT data buffer length=%ld (0x%lx) file='%s' memory=%p -- %p\n", + (long) (NBUFS * blksz), (long) (NBUFS * blksz), hndl->fname, + memory, memory + (NBUFS * blksz) - 1); + + /* set up an array of buffers, pointing them to the virtual addresses */ + TprintfT (DBG_LT2, "create_handle IO_TXT Buffer structures fname = `%s', NBUFS= %d, size = %ld (0x%lx)\n", fname, + NBUFS, (long) NBUFS * sizeof (Buffer), (long) NBUFS * sizeof (Buffer)); + hndl->buffers = (Buffer*) __collector_allocCSize (__collector_heap, NBUFS * sizeof (Buffer), 1); + if (hndl->buffers == NULL) + { + TprintfT (0, "create_handle allocCSize for hndl->buffers failed\n"); + CALL_UTIL (munmap)(memory, NBUFS * blksz); + return NULL; + } + for (int i = 0; i < NBUFS; i++) + { + Buffer *buf = &hndl->buffers[i]; + buf->vaddr = memory + i * blksz; + buf->state = ST_FREE; + } + /* set the file pointer to the beginning of the file */ + hndl->curpos = CUR_MAKE (0, 0, 0); + } + else + { + if (hndl->iotype == IO_BLK) + { + long nflow = CALL_UTIL (sysconf)(_SC_NPROCESSORS_ONLN); + if (nflow < 16) + nflow = 16; + hndl->nflow = (uint32_t) nflow; + } + else if (hndl->iotype == IO_SEQ) + hndl->nflow = 1; + TprintfT (DBG_LT2, "create_handle calling allocCSize blkstate fname=`%s' nflow=%d NCHUNKS=%d size=%ld (0x%lx)\n", + fname, hndl->nflow, NCHUNKS, + (long) (hndl->nflow * NCHUNKS * sizeof (uint32_t)), + (long) (hndl->nflow * NCHUNKS * sizeof (uint32_t))); + uint32_t *blkstate = (uint32_t*) __collector_allocCSize (__collector_heap, hndl->nflow * NCHUNKS * sizeof (uint32_t), 1); + if (blkstate == NULL) + return NULL; + for (int j = 0; j < hndl->nflow * NCHUNKS; ++j) + blkstate[j] = ST_INIT; + hndl->blkstate = blkstate; + TprintfT (DBG_LT2, "create_handle calling allocCSize blkoff fname=`%s' nflow=%d NCHUNKS=%d size=%ld (0x%lx)\n", + fname, hndl->nflow, NCHUNKS, + (long) (hndl->nflow * NCHUNKS * sizeof (uint32_t)), + (long) (hndl->nflow * NCHUNKS * sizeof (uint32_t))); + hndl->blkoff = (uint32_t*) __collector_allocCSize (__collector_heap, hndl->nflow * NCHUNKS * sizeof (uint32_t), 1); + if (hndl->blkoff == NULL) + return NULL; + hndl->nchnk = 0; + for (int j = 0; j < NCHUNKS; ++j) + { + hndl->chunks[j] = NULL; + hndl->chblk[j] = 0; + } + } + hndl->active = 1; + return hndl; +} + +static void +deleteHandle (DataHandle *hndl) +{ + if (hndl->active == 0) + return; + hndl->active = 0; + + if (hndl->iotype == IO_BLK || hndl->iotype == IO_SEQ) + { + /* Delete all blocks. */ + /* Since access to hndl->active is not synchronized it's still + * possible that we leave some blocks undeleted. + */ + for (int j = 0; j < hndl->nflow * NCHUNKS; ++j) + { + uint32_t oldstate = hndl->blkstate[j]; + if (oldstate != ST_FREE) + continue; + /* Mark as busy */ + uint32_t state = __collector_cas_32 (hndl->blkstate + j, oldstate, ST_BUSY); + if (state != oldstate) + continue; + deleteBlock (hndl, j / NCHUNKS, j % NCHUNKS); + } + } + else if (hndl->iotype == IO_TXT) + { + /* + * First, make sure that buffers are in some "coherent" state: + * + * At this point, the handle is no longer active. But some threads + * might already have passed the active-handle check and are now + * trying to schedule writes. So, set the handle pointer to "busy". + * This will prevent new writes from being scheduled. Threads that + * polling will time out. + */ + hrtime_t timeout = __collector_gethrtime () + 10 * ((hrtime_t) 1000000000); + volatile uint32_t busy = 0; + while (1) + { + uint32_t indx; + uint64_t opos, npos, foff; + int blk_off; + /* read the current pointer */ + opos = hndl->curpos; + busy = CUR_BUSY (opos); + indx = CUR_INDX (opos); + foff = CUR_FOFF (opos); + if (busy == 1) + { + if (__collector_gethrtime () > timeout) + { + TprintfT (0, "deleteHandle ERROR: timeout cleaning up handle for %s\n", hndl->fname); + return; + } + continue; + } + blk_off = foff & (blksz - 1); + if (blk_off > 0) + foff += blksz - blk_off; + npos = CUR_MAKE (1, indx, foff); + + /* try to update the handle position atomically */ + if (__collector_cas_64p (&hndl->curpos, &opos, &npos) != opos) + continue; + + /* + * If the last buffer won't be filled, account for + * the white space at the end so that the buffer will + * be deleted properly. + */ + if (blk_off > 0) + { + Buffer *buf = &hndl->buffers[indx]; + if (__collector_subget_32 (&buf->left, blksz - blk_off) == 0) + deleteBuffer (buf); + } + break; + } + /* wait for buffers to be deleted */ + timeout = __collector_gethrtime () + 10 * ((hrtime_t) 1000000000); + for (int i = 0; i < NBUFS; i++) + { + Buffer *buf = &hndl->buffers[i]; + while (__collector_cas_32 (&buf->state, ST_FREE, ST_INIT) != ST_FREE) + { + if (__collector_gethrtime () > timeout) + { + TprintfT (0, "deleteHandle ERROR: timeout waiting for buffer %d for %s\n", i, hndl->fname); + return; + } + } + CALL_UTIL (munmap)(buf->vaddr, blksz); + } + + /* free buffer array */ + __collector_freeCSize (__collector_heap, hndl->buffers, NBUFS * sizeof (Buffer)); + } +} + +void +__collector_delete_handle (DataHandle *hndl) +{ + if (hndl == NULL) + return; + deleteHandle (hndl); +} + +static int +exp_size_ck (int nblocks, char *fname) +{ + if (size_limit == 0) + return 0; + /* do an atomic add to the cur_size */ + uint32_t old_size = cur_size; + uint32_t new_size; + for (;;) + { + new_size = __collector_cas_32 (&cur_size, old_size, old_size + nblocks); + if (new_size == old_size) + { + new_size = old_size + nblocks; + break; + } + old_size = new_size; + } + TprintfT (DBG_LT2, "exp_size_ck() adding %d block(s); new_size = %d, limit = %d blocks; fname = %s\n", + nblocks, new_size, size_limit, fname); + + /* pause the entire collector if we have exceeded the limit */ + if (old_size < size_limit && new_size >= size_limit) + { + TprintfT (0, "exp_size_ck() experiment size limit exceeded; new_size = %ld, limit = %ld blocks; fname = %s\n", + (long) new_size, (long) size_limit, fname); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%ld blocks (each %ld bytes)</event>\n", + SP_JCMD_CWARN, COL_ERROR_SIZELIM, (long) size_limit, (long) blksz); + __collector_pause_m ("size-limit"); + __collector_terminate_expt (); + return -1; + } + return 0; +} + +int +__collector_set_size_limit (char *par) +{ + if (!initialized) + init (); + + int lim = CALL_UTIL (strtol)(par, &par, 0); + size_limit = (uint32_t) ((uint64_t) lim * 1024 * 1024 / blksz); + TprintfT (DBG_LT0, "collector_size_limit set to %d MB. = %d blocks\n", + lim, size_limit); + (void) __collector_log_write ("<setting limit=\"%d\"/>\n", lim); + return COL_ERROR_NONE; +} + +/* + * IO_BLK and IO_SEQ files + */ + +/* + * Allocate a chunk (nflow blocks) contiguously in virtual memory. + * Its blocks will be mmapped to the file individually. + */ +static int +allocateChunk (DataHandle *hndl, unsigned ichunk) +{ + /* + * hndl->chunks[ichunk] is one of: + * - NULL (initial value) + * - CHUNK_BUSY (transition state when allocating the chunk) + * - some address (the allocated chunk) + */ + uint8_t *CHUNK_BUSY = (uint8_t *) 1; + hrtime_t timeout = 0; + while (1) + { + if (hndl->chunks[ichunk] > CHUNK_BUSY) + return 0; /* the chunk has already been allocated */ + /* try to allocate the chunk (change: NULL => CHUNK_BUSY) */ + if (__collector_cas_ptr (&hndl->chunks[ichunk], NULL, CHUNK_BUSY) == NULL) + { + /* allocate virtual memory */ + uint8_t *newchunk = (uint8_t*) CALL_UTIL (mmap64)(0, + (size_t) (blksz * hndl->nflow), + PROT_READ | PROT_WRITE, +#if ARCH(SPARC) + MAP_SHARED | MAP_ANON, +#else + MAP_PRIVATE | MAP_ANON, +#endif + -1, (off64_t) 0); + if (newchunk == MAP_FAILED) + { + deleteHandle (hndl); + TprintfT (DBG_LT1, " allocateChunk mmap: start=0x%x length=%ld (0x%lx), offset=%d ret=%p\n", + 0, (long) (blksz * hndl->nflow), + (long) (blksz * hndl->nflow), 0, newchunk); + TprintfT (0, "allocateChunk: can't mmap MAP_ANON (for %s): %s\n", hndl->fname, CALL_UTIL (strerror) (errno)); + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">MAP_ANON (for %s)</event>\n", + SP_JCMD_CERROR, COL_ERROR_FILEMAP, errno, hndl->fname); + return 1; + } + + /* assign allocated address to our chunk */ + if (__collector_cas_ptr (&hndl->chunks[ichunk], CHUNK_BUSY, newchunk) != CHUNK_BUSY) + { + TprintfT (0, "allocateChunk: can't release chunk CAS lock for %s\n", hndl->fname); + __collector_log_write ("<event kind=\"%s\" id=\"%d\">couldn't release chunk CAS lock (%s)</event>\n", + SP_JCMD_CERROR, COL_ERROR_GENERAL, hndl->fname); + } + __collector_inc_32 (&hndl->nchnk); + return 0; + } + + /* check for time out */ + if (timeout == 0) + timeout = __collector_gethrtime () + 10 * ((hrtime_t) 1000000000); + if (__collector_gethrtime () > timeout) + { + TprintfT (0, "allocateChunk: timeout for %s\n", hndl->fname); + __collector_log_write ("<event kind=\"%s\" id=\"%d\">timeout allocating chunk for %s</event>\n", + SP_JCMD_CERROR, COL_ERROR_GENERAL, hndl->fname); + return 1; + } + } +} + +/* + * Get the address for block (iflow,ichunk). + */ +static uint8_t * +getBlock (DataHandle *hndl, unsigned iflow, unsigned ichunk) +{ + return hndl->chunks[ichunk] + iflow * blksz; +} + +/* + * Map block (iflow,ichunk) to the next part of the file. + */ +static int +remapBlock (DataHandle *hndl, unsigned iflow, unsigned ichunk) +{ + int rc = 0; + int fd; + /* Get the old file nblk and increment it atomically. */ + uint32_t oldblk = hndl->nblk; + for (;;) + { + uint32_t newblk = __collector_cas_32 (&hndl->nblk, oldblk, oldblk + 1); + if (newblk == oldblk) + break; + oldblk = newblk; + } + off64_t offset = (off64_t) oldblk * blksz; + + /* 6618470: disable thread cancellation */ + int old_cstate; + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old_cstate); + + /* Open the file. */ + int iter = 0; + hrtime_t tso = __collector_gethrtime (); + for (;;) + { + fd = CALL_UTIL (open)(hndl->fname, O_RDWR, 0); + if (fd < 0) + { + if (errno == EMFILE) + { + /* too many open files */ + iter++; + if (iter > 1000) + { + /* we've tried 1000 times; kick error back to caller */ + char errmsg[MAXPATHLEN + 50]; + hrtime_t teo = __collector_gethrtime (); + double deltato = (double) (teo - tso) / 1000000.; + (void) CALL_UTIL (snprintf) (errmsg, sizeof (errmsg), " t=%d, %s: open-retries-failed = %d, %3.6f ms.; remap", + __collector_thr_self (), hndl->fname, iter, deltato); + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_COMMENT, COL_COMMENT_NONE, errmsg); + rc = 1; + goto exit; + } + /* keep trying */ + continue; + } + deleteHandle (hndl); + TprintfT (0, "remapBlock: can't open file: %s: %s\n", hndl->fname, STR (CALL_UTIL (strerror)(errno))); + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">t=%llu, %s: remap </event>\n", + SP_JCMD_CERROR, COL_ERROR_FILEOPN, errno, + (unsigned long long) __collector_thr_self (), + hndl->fname); + rc = 1; + goto exit; + } + else + break; + } + + /* report number of retries of the open due to too many open fd's */ + if (iter > 0) + { + char errmsg[MAXPATHLEN + 50]; + hrtime_t teo = __collector_gethrtime (); + double deltato = (double) (teo - tso) / 1000000.; + (void) CALL_UTIL (snprintf) (errmsg, sizeof (errmsg), " t=%d, %s: open-retries = %d, %3.6f ms.; remap", + __collector_thr_self (), hndl->fname, iter, deltato); + __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_COMMENT, COL_COMMENT_NONE, errmsg); + } + + /* Ensure disk space is allocated and the block offset is 0 */ + uint32_t zero = 0; + int n = CALL_UTIL (pwrite64)(fd, &zero, sizeof (zero), (off64_t) (offset + blksz - sizeof (zero))); + if (n <= 0) + { + deleteHandle (hndl); + TprintfT (0, "remapBlock: can't pwrite file: %s : errno=%d\n", hndl->fname, errno); + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s: remap</event>\n", + SP_JCMD_CERROR, COL_ERROR_NOSPACE, errno, hndl->fname); + CALL_UTIL (close)(fd); + rc = 1; + goto exit; + } + hndl->blkoff[iflow * NCHUNKS + ichunk] = 0; + + /* Map block to file */ + uint8_t *bptr = getBlock (hndl, iflow, ichunk); + uint8_t *vaddr = (uint8_t *) CALL_UTIL (mmap64)( + (void*) bptr, + (size_t) blksz, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, + fd, + offset); + + if (vaddr != bptr) + { + deleteHandle (hndl); + TprintfT (DBG_LT1, " remapBlock mmap: start=%p length=%ld (0x%lx) offset=0x%llx ret=%p\n", + bptr, (long) blksz, (long) blksz, (long long) offset, vaddr); + TprintfT (0, "remapBlock: can't mmap file: %s : errno=%d\n", hndl->fname, errno); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s: remap</event>\n", + SP_JCMD_CERROR, COL_ERROR_FILEMAP, errno, hndl->fname); + CALL_UTIL (close)(fd); + rc = 1; + goto exit; + } + CALL_UTIL (close)(fd); + + if (hndl->exempt == 0) + exp_size_ck (1, hndl->fname); + else + Tprintf (DBG_LT1, "exp_size_ck() bypassed for %d block(s); exempt fname = %s\n", + 1, hndl->fname); +exit: + /* Restore the previous cancellation state */ + pthread_setcancelstate (old_cstate, NULL); + + return rc; +} + +static int +newBlock (DataHandle *hndl, unsigned iflow, unsigned ichunk) +{ + if (allocateChunk (hndl, ichunk) != 0) + return 1; + if (remapBlock (hndl, iflow, ichunk) != 0) + return 1; + + /* Update the number of active blocks */ + __collector_inc_32 (hndl->chblk + ichunk); + return 0; +} + +static void +deleteBlock (DataHandle *hndl, unsigned iflow, unsigned ichunk) +{ + uint8_t *bptr = getBlock (hndl, iflow, ichunk); + CALL_UTIL (munmap)((void*) bptr, blksz); + hndl->blkstate[iflow * NCHUNKS + ichunk] = ST_INIT; + + /* Update the number of active blocks */ + __collector_dec_32 (hndl->chblk + ichunk); +} + +int +__collector_write_record (DataHandle *hndl, Common_packet *pckt) +{ + if (hndl == NULL || !hndl->active) + return 1; + /* fill in the fields of the common packet structure */ + if (pckt->type == 0) + pckt->type = hndl->kind; + if (pckt->tstamp == 0) + pckt->tstamp = __collector_gethrtime (); + if (pckt->lwp_id == 0) + pckt->lwp_id = __collector_lwp_self (); + if (pckt->thr_id == 0) + pckt->thr_id = __collector_thr_self (); + if (pckt->cpu_id == 0) + pckt->cpu_id = CALL_UTIL (getcpuid)(); + if (pckt->tsize == 0) + pckt->tsize = sizeof (Common_packet); + TprintfT (DBG_LT3, "collector_write_record to %s, type:%d tsize:%d\n", + hndl->fname, pckt->type, pckt->tsize); + return __collector_write_packet (hndl, (CM_Packet*) pckt); +} + +int +__collector_write_packet (DataHandle *hndl, CM_Packet *pckt) +{ + if (hndl == NULL || !hndl->active) + return 1; + + /* if the experiment is not open, there should be no writes */ + if (__collector_expstate != EXP_OPEN) + { +#ifdef DEBUG + char *xstate; + switch (__collector_expstate) + { + case EXP_INIT: + xstate = "EXP_INIT"; + break; + case EXP_OPEN: + xstate = "EXP_OPEN"; + break; + case EXP_PAUSED: + xstate = "EXP_PAUSED"; + break; + case EXP_CLOSED: + xstate = "EXP_CLOSED"; + break; + default: + xstate = "Unknown"; + break; + } + TprintfT (0, "collector_write_packet: write to %s while experiment state is %s\n", + hndl->fname, xstate); +#endif + return 1; + } + int recsz = pckt->tsize; + if (recsz > blksz) + { + TprintfT (0, "collector_write_packet: packet too long: %d (max %ld)\n", recsz, blksz); + return 1; + } + unsigned tid = (__collector_no_threads ? __collector_lwp_self () : __collector_thr_self ()); + unsigned iflow = tid % hndl->nflow; + + /* Acquire block */ + uint32_t *sptr = &hndl->blkstate[iflow * NCHUNKS]; + uint32_t state = ST_BUSY; + unsigned ichunk; + for (ichunk = 0; ichunk < NCHUNKS; ++ichunk) + { + uint32_t oldstate = sptr[ichunk]; + if (oldstate == ST_BUSY) + continue; + /* Mark as busy */ + state = __collector_cas_32 (sptr + ichunk, oldstate, ST_BUSY); + if (state == oldstate) + break; + if (state == ST_BUSY) + continue; + /* It's possible the state changed from ST_INIT to ST_FREE */ + oldstate = state; + state = __collector_cas_32 (sptr + ichunk, oldstate, ST_BUSY); + if (state == oldstate) + break; + } + + if (state == ST_BUSY || ichunk == NCHUNKS) + { + /* We are out of blocks for this data flow. + * We might switch to another flow but for now report and return. + */ + TprintfT (0, "collector_write_packet: all %d blocks on flow %d for %s are busy\n", + NCHUNKS, iflow, hndl->fname); + return 1; + } + + if (state == ST_INIT && newBlock (hndl, iflow, ichunk) != 0) + return 1; + uint8_t *bptr = getBlock (hndl, iflow, ichunk); + uint32_t blkoff = hndl->blkoff[iflow * NCHUNKS + ichunk]; + if (blkoff + recsz > blksz) + { + /* The record doesn't fit. Close the block */ + if (blkoff < blksz) + { + Common_packet *closed = (Common_packet *) (bptr + blkoff); + closed->type = CLOSED_PCKT; + closed->tsize = blksz - blkoff; /* redundant */ + } + if (remapBlock (hndl, iflow, ichunk) != 0) + return 1; + blkoff = hndl->blkoff[iflow * NCHUNKS + ichunk]; + } + if (blkoff + recsz < blksz) + { + /* Set the empty padding */ + Common_packet *empty = (Common_packet *) (bptr + blkoff + recsz); + empty->type = EMPTY_PCKT; + empty->tsize = blksz - blkoff - recsz; + } + __collector_memcpy (bptr + blkoff, pckt, recsz); + + /* Release block */ + if (hndl->active == 0) + { + deleteBlock (hndl, iflow, ichunk); + return 0; + } + hndl->blkoff[iflow * NCHUNKS + ichunk] += recsz; + sptr[ichunk] = ST_FREE; + return 0; +} + +/* + * IO_TXT files + * + * IO_TXT covers the case where many threads are trying to write text messages + * sequentially (atomically) to a file. Examples include SP_LOG_FILE and SP_MAP_FILE. + * + * The file is not written directly, but by writing to mmapped virtual memory. + * The granularity of the mapping is a "Buffer". There may be as many as + * NBUFS buffers at any one time. + * + * The current position of the file is handled via hndl->curpos. + * + * * It is accessed atomically with 64-bit CAS instructions. + * + * * This 64-bit word encapsulates: + * - busy: a bit to lock access to hndl->curpos + * - indx: an index indicating which Buffer to use for the current position + * - foff: the file offset + * + * * The contents are accessed with: + * - unpack macros: CUR_BUSY CUR_INDX CUR_FOFF + * - pack macro : CUR_MAKE + * + * Conceptually, what happens when a thread wants to write a message is: + * - acquire the hndl->curpos "busy" lock + * . acquire and map new Buffers if needed to complete the message + * . update the file offset + * . release the lock + * - write to the corresponding buffers + * + * Each Buffer has a buf->left field that tracks how many more bytes + * need to be written to the Buffer. After a thread writes to a Buffer, + * it decrements buf->left atomically. When buf->left reaches 0, the + * Buffer (mapping) is deleted, freeing the Buffer for a new mapping. + * + * The actual implementation has some twists: + * + * * If the entire text message fits into the current Buffer -- that is, + * no new Buffers are needed -- the thread does not acquire the lock. + * It simply updates hndl->curpos atomically to the new file offset. + * + * * There are various timeouts to prevent hangs in case of abnormalities. + */ +static int +is_not_the_log_file (char *fname) +{ + if (CALL_UTIL (strstr)(fname, SP_LOG_FILE) == NULL) + return 1; + return 0; +} + +static int +mapBuffer (char *fname, Buffer *buf, off64_t foff) +{ + int rc = 0; + /* open fname */ + int fd = CALL_UTIL (open)(fname, O_RDWR, 0); + if (fd < 0) + { + TprintfT (0, "mapBuffer ERROR: can't open file: %s\n", fname); + if (is_not_the_log_file (fname)) + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s: mapBuffer</event>\n", + SP_JCMD_CERROR, COL_ERROR_FILEOPN, errno, fname); + return 1; + } + TprintfT (DBG_LT2, "mapBuffer pwrite file %s at 0x%llx\n", fname, (long long) foff); + + /* ensure disk space is allocated */ + char nl = '\n'; + int n = CALL_UTIL (pwrite64)(fd, &nl, sizeof (nl), (off64_t) (foff + blksz - sizeof (nl))); + if (n <= 0) + { + TprintfT (0, "mapBuffer ERROR: can't pwrite file %s at 0x%llx\n", fname, + (long long) (foff + blksz - sizeof (nl))); + if (is_not_the_log_file (fname)) + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s: mapBuffer</event>\n", + SP_JCMD_CERROR, COL_ERROR_FILETRNC, errno, fname); + rc = 1; + goto exit; + } + /* mmap buf->vaddr to fname at foff */ + uint8_t *vaddr = CALL_UTIL (mmap64)(buf->vaddr, (size_t) blksz, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, foff); + if (vaddr != buf->vaddr) + { + TprintfT (DBG_LT1, " mapBuffer mmap: start=%p length=%ld (0x%lx) offset=0x%llx ret=%p\n", + buf->vaddr, blksz, blksz, (long long) foff, vaddr); + TprintfT (0, "mapBuffer ERROR: can't mmap %s: vaddr=%p size=%ld (0x%lx) ret=%p off=0x%llx errno=%d\n", + fname, buf->vaddr, blksz, blksz, vaddr, (long long) foff, errno); + if (is_not_the_log_file (fname)) + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s: mapBuffer</event>\n", + SP_JCMD_CERROR, COL_ERROR_FILEMAP, errno, fname); + rc = 1; + } + else + buf->left = blksz; +exit: + CALL_UTIL (close)(fd); + + /* Should we check buffer size? Let's not since: + * - IO_TXT is typically not going to be that big + * - we want log.xml to be treated specially + */ + /* exp_size_ck( 1, fname ); */ + return rc; +} + +static int +newBuffer (DataHandle *hndl, uint64_t foff) +{ + /* find a ST_FREE buffer and mark it ST_BUSY */ + int ibuf; + for (ibuf = 0; ibuf < NBUFS; ibuf++) + if (__collector_cas_32 (&hndl->buffers[ibuf].state, ST_FREE, ST_BUSY) == ST_FREE) + break; + if (ibuf >= NBUFS) + { + TprintfT (0, "newBuffer ERROR: all buffers busy for %s\n", hndl->fname); + return -1; + } + Buffer *nbuf = hndl->buffers + ibuf; + + /* map buffer */ + if (mapBuffer (hndl->fname, nbuf, foff) != 0) + { + nbuf->state = ST_FREE; + ibuf = -1; + goto exit; + } +exit: + return ibuf; +} + +static void +writeBuffer (Buffer *buf, int blk_off, char *src, int len) +{ + __collector_memcpy (buf->vaddr + blk_off, src, len); + if (__collector_subget_32 (&buf->left, len) == 0) + deleteBuffer (buf); +} + +static void +deleteBuffer (Buffer *buf) +{ + buf->state = ST_FREE; +} + +int +__collector_write_string (DataHandle *hndl, char *src, int len) +{ + if (hndl == NULL || !hndl->active) + return 1; + if (len <= 0) + return 0; + + hrtime_t timeout = __collector_gethrtime () + 20 * ((hrtime_t) 1000000000); + volatile uint32_t busy = 0; + while (1) + { + uint32_t indx; + uint64_t opos, foff, base; + int blk_off, buf_indices[NBUFS], ibuf, nbufs; + + /* read and decode the current pointer */ + opos = hndl->curpos; + busy = CUR_BUSY (opos); + indx = CUR_INDX (opos); + foff = CUR_FOFF (opos); + if (busy == 1) + { + if (__collector_gethrtime () > timeout) + { + /* + * E.g., if another thread deleted the handle + * after we checked hndl->active. + */ + TprintfT (0, "__collector_write_string ERROR: timeout writing length=%d to text file: %s\n", len, hndl->fname); + return 1; + } + continue; + } + + /* initial block offset */ + blk_off = foff & (blksz - 1); + + /* number of new buffers to map */ + int lastbuf = ((foff + len - 1) >> log2blksz); /* last block file index we will write */ + int firstbuf = ((foff - 1) >> log2blksz); /* last block file index we have written */ + nbufs = lastbuf - firstbuf; + TprintfT (DBG_LT2, "__collector_write_string firstbuf = %d, lastbuf = %d, nbufs = %d, log2blksz = %ld\n", + firstbuf, lastbuf, nbufs, log2blksz); + if (nbufs >= NBUFS) + { + Tprintf (0, "__collector_write_string ERROR: string of length %d too long to be written to text file: %s\n", len, hndl->fname); + return 1; + } + + /* things are simple if we don't need new buffers */ + if (nbufs == 0) + { + /* try to update the handle position atomically */ + uint64_t npos = CUR_MAKE (0, indx, foff + len); + if (__collector_cas_64p (&hndl->curpos, &opos, &npos) != opos) + continue; + + /* success! copy our string and we're done */ + TprintfT (DBG_LT2, "__collector_write_string writeBuffer[%d]: vaddr = %p, len = %d, foff = %lld, '%s'\n", + indx, hndl->buffers[indx].vaddr, len, (long long) foff, src); + writeBuffer (&hndl->buffers[indx], foff & (blksz - 1), src, len); + break; + } + + /* initialize the new signal mask */ + sigset_t new_mask; + sigset_t old_mask; + CALL_UTIL (sigfillset)(&new_mask); + + /* 6618470: disable thread cancellation */ + int old_cstate; + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old_cstate); + /* block all signals */ + CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask); + + /* but if we need new buffers, "lock" the handle pointer */ + uint64_t lpos = CUR_MAKE (1, indx, foff); + if (__collector_cas_64p (&hndl->curpos, &opos, &lpos) != opos) + { + /* restore signal mask */ + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + /* Restore the previous cancellation state */ + pthread_setcancelstate (old_cstate, NULL); + continue; + } + + /* map new buffers */ + base = ((foff - 1) & ~(blksz - 1)); /* last buffer to have been mapped */ + for (ibuf = 0; ibuf < nbufs; ibuf++) + { + base += blksz; + buf_indices[ibuf] = newBuffer (hndl, base); + if (buf_indices[ibuf] < 0) + break; + } + + /* "unlock" the handle pointer */ + uint64_t npos = CUR_MAKE (0, indx, foff); + if (ibuf == nbufs) + npos = CUR_MAKE (0, buf_indices[nbufs - 1], foff + len); + if (__collector_cas_64p (&hndl->curpos, &lpos, &npos) != lpos) + { + TprintfT (0, "__collector_write_string ERROR: file handle corrupted: %s\n", hndl->fname); + /* + * At this point, the handle is apparently corrupted and + * presumably locked. No telling what's going on. Still + * let's proceed and write our data and let a later thread + * raise an error if it encounters one. + */ + } + + /* restore signal mask */ + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + /* Restore the previous cancellation state */ + pthread_setcancelstate (old_cstate, NULL); + + /* if we couldn't map all the buffers we needed, don't write any part of the string */ + if (ibuf < nbufs) + { + TprintfT (0, "__collector_write_string ERROR: can't map new buffer: %s\n", hndl->fname); + return 1; + } + + /* write any data to the old block */ + if (blk_off > 0) + { + TprintfT (DBG_LT2, "__collector_write_string partial writeBuffer[%d]: len=%ld, foff = %d '%s'\n", + indx, blksz - blk_off, blk_off, src); + writeBuffer (&hndl->buffers[indx], blk_off, src, blksz - blk_off); + src += blksz - blk_off; + len -= blksz - blk_off; + } + + /* write data to the new blocks */ + for (ibuf = 0; ibuf < nbufs; ibuf++) + { + int clen = blksz; + if (clen > len) + clen = len; + TprintfT (DBG_LT2, "__collector_write_string continue writeBuffer[%d]: len= %d, %s", + ibuf, clen, src); + writeBuffer (&hndl->buffers[buf_indices[ibuf]], 0, src, clen); + src += clen; + len -= clen; + } + break; + } + return 0; +} + diff --git a/gprofng/libcollector/iotrace.c b/gprofng/libcollector/iotrace.c new file mode 100644 index 0000000..462ccf2 --- /dev/null +++ b/gprofng/libcollector/iotrace.c @@ -0,0 +1,3728 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * IO events + */ +#include "config.h" +#include <dlfcn.h> +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> + +// create() and others are defined in fcntl.h. +// Our 'create' should not have the __nonnull attribute +#undef __nonnull +#define __nonnull(x) +#include <fcntl.h> + +#include "gp-defs.h" +#include "collector_module.h" +#include "gp-experiment.h" +#include "data_pckts.h" +#include "tsd.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LTT 0 // for interposition on GLIBC functions +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +/* define the packet that will be written out */ +typedef struct IOTrace_packet +{ /* IO tracing packet */ + Common_packet comm; + IOTrace_type iotype; /* IO type */ + int32_t fd; /* file descriptor */ + Size_type nbyte; /* number of bytes */ + hrtime_t requested; /* time of IO requested */ + int32_t ofd; /* original file descriptor */ + FileSystem_type fstype; /* file system type */ + char fname; /* file name */ +} IOTrace_packet; + +typedef long long offset_t; + +static int open_experiment (const char *); +static int start_data_collection (void); +static int stop_data_collection (void); +static int close_experiment (void); +static int detach_experiment (void); +static int init_io_intf (); + +static ModuleInterface module_interface ={ + SP_IOTRACE_FILE, /* description */ + NULL, /* initInterface */ + open_experiment, /* openExperiment */ + start_data_collection, /* startDataCollection */ + stop_data_collection, /* stopDataCollection */ + close_experiment, /* closeExperiment */ + detach_experiment /* detachExperiment (fork child) */ +}; + +static CollectorInterface *collector_interface = NULL; +static struct Heap *io_heap = NULL; +static int io_mode = 0; +static CollectorModule io_hndl = COLLECTOR_MODULE_ERR; +static unsigned io_key = COLLECTOR_TSD_INVALID_KEY; + +#define CHCK_REENTRANCE(x) (!io_mode || ((x) = collector_interface->getKey( io_key )) == NULL || (*(x) != 0)) +#define RECHCK_REENTRANCE(x) (!io_mode || ((x) = collector_interface->getKey( io_key )) == NULL || (*(x) == 0)) +#define PUSH_REENTRANCE(x) ((*(x))++) +#define POP_REENTRANCE(x) ((*(x))--) + +#define CALL_REAL(x) (__real_##x) +#define NULL_PTR(x) (__real_##x == NULL) + +#define gethrtime collector_interface->getHiResTime + +#ifdef DEBUG +#define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ ) +#define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ ) +#else +#define Tprintf(...) +#define TprintfT(...) +#endif + +/* interposition function handles */ +static int (*__real_open)(const char *path, int oflag, ...) = NULL; +static int (*__real_fcntl)(int fildes, int cmd, ...) = NULL; +static int (*__real_openat)(int fildes, const char *path, int oflag, ...) = NULL; +static int (*__real_close)(int fildes) = NULL; +static FILE *(*__real_fopen)(const char *filename, const char *mode) = NULL; +static int (*__real_fclose)(FILE *stream) = NULL; +static int (*__real_dup)(int fildes) = NULL; +static int (*__real_dup2)(int fildes, int fildes2) = NULL; +static int (*__real_pipe)(int fildes[2]) = NULL; +static int (*__real_socket)(int domain, int type, int protocol) = NULL; +static int (*__real_mkstemp)(char *template) = NULL; +static int (*__real_mkstemps)(char *template, int slen) = NULL; +static int (*__real_creat)(const char *path, mode_t mode) = NULL; +static FILE *(*__real_fdopen)(int fildes, const char *mode) = NULL; +static ssize_t (*__real_read)(int fildes, void *buf, size_t nbyte) = NULL; +static ssize_t (*__real_write)(int fildes, const void *buf, size_t nbyte) = NULL; +static ssize_t (*__real_readv)(int fildes, const struct iovec *iov, int iovcnt) = NULL; +static ssize_t (*__real_writev)(int fildes, const struct iovec *iov, int iovcnt) = NULL; +static size_t (*__real_fread)(void *ptr, size_t size, size_t nitems, FILE *stream) = NULL; +static size_t (*__real_fwrite)(const void *ptr, size_t size, size_t nitems, FILE *stream) = NULL; +static ssize_t (*__real_pread)(int fildes, void *buf, size_t nbyte, off_t offset) = NULL; +static ssize_t (*__real_pwrite)(int fildes, const void *buf, size_t nbyte, off_t offset) = NULL; +static ssize_t (*__real_pwrite64)(int fildes, const void *buf, size_t nbyte, off64_t offset) = NULL; +static char *(*__real_fgets)(char *s, int n, FILE *stream) = NULL; +static int (*__real_fputs)(const char *s, FILE *stream) = NULL; +static int (*__real_fputc)(int c, FILE *stream) = NULL; +static int (*__real_fprintf)(FILE *stream, const char *format, ...) = NULL; +static int (*__real_vfprintf)(FILE *stream, const char *format, va_list ap) = NULL; +static off_t (*__real_lseek)(int fildes, off_t offset, int whence) = NULL; +static offset_t (*__real_llseek)(int fildes, offset_t offset, int whence) = NULL; +static int (*__real_chmod)(const char *path, mode_t mode) = NULL; +static int (*__real_access)(const char *path, int amode) = NULL; +static int (*__real_rename)(const char *old, const char *new) = NULL; +static int (*__real_mkdir)(const char *path, mode_t mode) = NULL; +static int (*__real_getdents)(int fildes, struct dirent *buf, size_t nbyte) = NULL; +static int (*__real_unlink)(const char *path) = NULL; +static int (*__real_fseek)(FILE *stream, long offset, int whence) = NULL; +static void (*__real_rewind)(FILE *stream) = NULL; +static long (*__real_ftell)(FILE *stream) = NULL; +static int (*__real_fgetpos)(FILE *stream, fpos_t *pos) = NULL; +static int (*__real_fsetpos)(FILE *stream, const fpos_t *pos) = NULL; +static int (*__real_fsync)(int fildes) = NULL; +static struct dirent *(*__real_readdir)(DIR *dirp) = NULL; +static int (*__real_flock)(int fd, int operation) = NULL; +static int (*__real_lockf)(int fildes, int function, off_t size) = NULL; +static int (*__real_fflush)(FILE *stream) = NULL; + +#if WSIZE(32) +static int (*__real_open64)(const char *path, int oflag, ...) = NULL; +static int (*__real_creat64)(const char *path, mode_t mode) = NULL; +static int (*__real_fgetpos64)(FILE *stream, fpos64_t *pos) = NULL; +static int (*__real_fsetpos64)(FILE *stream, const fpos64_t *pos) = NULL; + +#if ARCH(Intel) +static FILE *(*__real_fopen_2_1)(const char *filename, const char *mode) = NULL; +static int (*__real_fclose_2_1)(FILE *stream) = NULL; +static FILE *(*__real_fdopen_2_1)(int fildes, const char *mode) = NULL; +static int (*__real_fgetpos_2_2)(FILE *stream, fpos_t *pos) = NULL; +static int (*__real_fsetpos_2_2)(FILE *stream, const fpos_t *pos) = NULL; +static int (*__real_fgetpos64_2_2)(FILE *stream, fpos64_t *pos) = NULL; +static int (*__real_fsetpos64_2_2)(FILE *stream, const fpos64_t *pos) = NULL; +static int (*__real_open64_2_2)(const char *path, int oflag, ...) = NULL; +static ssize_t (*__real_pread_2_2)(int fildes, void *buf, size_t nbyte, off_t offset) = NULL; +static ssize_t (*__real_pwrite_2_2)(int fildes, const void *buf, size_t nbyte, off_t offset) = NULL; +static ssize_t (*__real_pwrite64_2_2)(int fildes, const void *buf, size_t nbyte, off64_t offset) = NULL; +static FILE *(*__real_fopen_2_0)(const char *filename, const char *mode) = NULL; +static int (*__real_fclose_2_0)(FILE *stream) = NULL; +static FILE *(*__real_fdopen_2_0)(int fildes, const char *mode) = NULL; +static int (*__real_fgetpos_2_0)(FILE *stream, fpos_t *pos) = NULL; +static int (*__real_fsetpos_2_0)(FILE *stream, const fpos_t *pos) = NULL; +static int (*__real_fgetpos64_2_1)(FILE *stream, fpos64_t *pos) = NULL; +static int (*__real_fsetpos64_2_1)(FILE *stream, const fpos64_t *pos) = NULL; +static int (*__real_open64_2_1)(const char *path, int oflag, ...) = NULL; +static ssize_t (*__real_pread_2_1)(int fildes, void *buf, size_t nbyte, off_t offset) = NULL; +static ssize_t (*__real_pwrite_2_1)(int fildes, const void *buf, size_t nbyte, off_t offset) = NULL; +static ssize_t (*__real_pwrite64_2_1)(int fildes, const void *buf, size_t nbyte, off64_t offset) = NULL; +#endif /* ARCH() */ +#endif /* WSIZE(32) */ + +static int +collector_align_pktsize (int sz) +{ + int pktSize = sz; + if (sz <= 0) + return sz; + if ((sz % 8) != 0) + { + pktSize = (sz / 8) + 1; + pktSize *= 8; + } + return pktSize; +} + +static void +collector_memset (void *s, int c, size_t n) +{ + unsigned char *s1 = s; + while (n--) + *s1++ = (unsigned char) c; +} + +static size_t +collector_strlen (const char *s) +{ + if (s == NULL) + return 0; + int len = -1; + while (s[++len] != '\0') + ; + return len; +} + +static size_t +collector_strncpy (char *dst, const char *src, size_t dstsize) +{ + size_t i; + for (i = 0; i < dstsize; i++) + { + dst[i] = src[i]; + if (src[i] == '\0') + break; + } + return i; +} + +static char * +collector_strchr (const char *s, int c) +{ + do + { + if (*s == (char) c) + return ((char *) s); + } + while (*s++); + return (NULL); +} + +static FileSystem_type +collector_fstype (const char *path) +{ + return UNKNOWNFS_TYPE; +} + +void +__collector_module_init (CollectorInterface *_collector_interface) +{ + if (_collector_interface == NULL) + return; + collector_interface = _collector_interface; + Tprintf (0, "iotrace: __collector_module_init\n"); + io_hndl = collector_interface->registerModule (&module_interface); + /* Initialize next module */ + ModuleInitFunc next_init = (ModuleInitFunc) dlsym (RTLD_NEXT, "__collector_module_init"); + if (next_init != NULL) + next_init (_collector_interface); + return; +} + +static int +open_experiment (const char *exp) +{ + if (collector_interface == NULL) + { + Tprintf (0, "iotrace: collector_interface is null.\n"); + return COL_ERROR_IOINIT; + } + if (io_hndl == COLLECTOR_MODULE_ERR) + { + Tprintf (0, "iotrace: handle create failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_IOINIT); + return COL_ERROR_IOINIT; + } + TprintfT (0, "iotrace: open_experiment %s\n", exp); + if (NULL_PTR (fopen)) + init_io_intf (); + if (io_heap == NULL) + { + io_heap = collector_interface->newHeap (); + if (io_heap == NULL) + { + Tprintf (0, "iotrace: new heap failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">new iotrace heap not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_IOINIT); + return COL_ERROR_IOINIT; + } + } + + const char *params = collector_interface->getParams (); + while (params) + { + if ((params[0] == 'i') && (params[1] == ':')) + { + params += 2; + break; + } + params = collector_strchr (params, ';'); + if (params) + params++; + } + if (params == NULL) /* IO data collection not specified */ + return COL_ERROR_IOINIT; + + io_key = collector_interface->createKey (sizeof ( int), NULL, NULL); + if (io_key == (unsigned) - 1) + { + Tprintf (0, "iotrace: TSD key create failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_IOINIT); + return COL_ERROR_IOINIT; + } + + collector_interface->writeLog ("<profile name=\"%s\">\n", SP_JCMD_IOTRACE); + collector_interface->writeLog (" <profdata fname=\"%s\"/>\n", + module_interface.description); + /* Record IOTrace_packet description */ + IOTrace_packet *pp = NULL; + collector_interface->writeLog (" <profpckt kind=\"%d\" uname=\"IO tracing data\">\n", IOTRACE_PCKT); + collector_interface->writeLog (" <field name=\"LWPID\" uname=\"Lightweight process id\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.lwp_id, sizeof (pp->comm.lwp_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"THRID\" uname=\"Thread number\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.thr_id, sizeof (pp->comm.thr_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"CPUID\" uname=\"CPU id\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.cpu_id, sizeof (pp->comm.cpu_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"High resolution timestamp\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.tstamp, sizeof (pp->comm.tstamp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.frinfo, sizeof (pp->comm.frinfo) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"IOTYPE\" uname=\"IO trace function type\" offset=\"%d\" type=\"%s\"/>\n", + &pp->iotype, sizeof (pp->iotype) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"IOFD\" uname=\"File descriptor\" offset=\"%d\" type=\"%s\"/>\n", + &pp->fd, sizeof (pp->fd) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"IONBYTE\" uname=\"Number of bytes\" offset=\"%d\" type=\"%s\"/>\n", + &pp->nbyte, sizeof (pp->nbyte) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"IORQST\" uname=\"Time of IO requested\" offset=\"%d\" type=\"%s\"/>\n", + &pp->requested, sizeof (pp->requested) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"IOOFD\" uname=\"Original file descriptor\" offset=\"%d\" type=\"%s\"/>\n", + &pp->ofd, sizeof (pp->ofd) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"IOFSTYPE\" uname=\"File system type\" offset=\"%d\" type=\"%s\"/>\n", + &pp->fstype, sizeof (pp->fstype) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"IOFNAME\" uname=\"File name\" offset=\"%d\" type=\"%s\"/>\n", + &pp->fname, "STRING"); + collector_interface->writeLog (" </profpckt>\n"); + collector_interface->writeLog ("</profile>\n"); + return COL_ERROR_NONE; +} + +static int +start_data_collection (void) +{ + io_mode = 1; + Tprintf (0, "iotrace: start_data_collection\n"); + return 0; +} + +static int +stop_data_collection (void) +{ + io_mode = 0; + Tprintf (0, "iotrace: stop_data_collection\n"); + return 0; +} + +static int +close_experiment (void) +{ + io_mode = 0; + io_key = COLLECTOR_TSD_INVALID_KEY; + if (io_heap != NULL) + { + collector_interface->deleteHeap (io_heap); + io_heap = NULL; + } + Tprintf (0, "iotrace: close_experiment\n"); + return 0; +} + +static int +detach_experiment (void) +{ + /* fork child. Clean up state but don't write to experiment */ + io_mode = 0; + io_key = COLLECTOR_TSD_INVALID_KEY; + if (io_heap != NULL) + { + collector_interface->deleteHeap (io_heap); + io_heap = NULL; + } + Tprintf (0, "iotrace: detach_experiment\n"); + return 0; +} + +static int +init_io_intf () +{ + void *dlflag; + int rc = 0; + /* if we detect recursion/reentrance, SEGV so we can get a stack */ + static int init_io_intf_started; + static int init_io_intf_finished; + init_io_intf_started++; + if (!init_io_intf_finished && init_io_intf_started >= 3) + { + /* pull the plug if recursion occurs... */ + abort (); + } + + /* lookup fprint to print fatal error message */ + void *ptr = dlsym (RTLD_NEXT, "fprintf"); + if (ptr) + __real_fprintf = (int (*)(FILE*, const char*, ...)) ptr; + else + abort (); + +#if ARCH(Intel) +#if WSIZE(32) +#define SYS_FOPEN_X_VERSION "GLIBC_2.1" +#define SYS_FGETPOS_X_VERSION "GLIBC_2.2" +#define SYS_FGETPOS64_X_VERSION "GLIBC_2.2" +#define SYS_OPEN64_X_VERSION "GLIBC_2.2" +#define SYS_PREAD_X_VERSION "GLIBC_2.2" +#define SYS_PWRITE_X_VERSION "GLIBC_2.2" +#define SYS_PWRITE64_X_VERSION "GLIBC_2.2" +#else /* WSIZE(64) */ +#define SYS_FOPEN_X_VERSION "GLIBC_2.2.5" +#define SYS_FGETPOS_X_VERSION "GLIBC_2.2.5" +#endif +#elif ARCH(SPARC) +#if WSIZE(32) +#define SYS_FOPEN_X_VERSION "GLIBC_2.1" +#define SYS_FGETPOS_X_VERSION "GLIBC_2.2" +#else /* WSIZE(64) */ +#define SYS_FOPEN_X_VERSION "GLIBC_2.2" +#define SYS_FGETPOS_X_VERSION "GLIBC_2.2" +#endif +#elif ARCH(Aarch64) +#define SYS_FOPEN_X_VERSION "GLIBC_2.17" +#define SYS_FGETPOS_X_VERSION "GLIBC_2.17" +#endif /* ARCH() */ + +#if WSIZE(32) + dlflag = RTLD_NEXT; + __real_fopen = (FILE * (*)(const char*, const char*))dlvsym (dlflag, "fopen", SYS_FOPEN_X_VERSION); + if (__real_fopen == NULL) + { + /* We are probably dlopened after libc, + * try to search in the previously loaded objects + */ + __real_fopen = (FILE * (*)(const char*, const char*))dlvsym (RTLD_DEFAULT, "fopen", SYS_FOPEN_X_VERSION); + if (__real_fopen != NULL) + { + Tprintf (0, "iotrace: WARNING: init_io_intf() using RTLD_DEFAULT for Linux io routines\n"); + dlflag = RTLD_DEFAULT; + } + else + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fopen\n"); + rc = COL_ERROR_IOINIT; + } + } + + __real_fclose = (int (*)(FILE*))dlvsym (dlflag, "fclose", SYS_FOPEN_X_VERSION); + if (__real_fclose == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fclose\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fdopen = (FILE * (*)(int, const char*))dlvsym (dlflag, "fdopen", SYS_FOPEN_X_VERSION); + if (__real_fdopen == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fdopen\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fgetpos = (int (*)(FILE*, fpos_t*))dlvsym (dlflag, "fgetpos", SYS_FGETPOS_X_VERSION); + if (__real_fgetpos == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fgetpos\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fsetpos = (int (*)(FILE*, const fpos_t*))dlvsym (dlflag, "fsetpos", SYS_FGETPOS_X_VERSION); + if (__real_fsetpos == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fsetpos\n"); + rc = COL_ERROR_IOINIT; + } + + +#if ARCH(Intel) + __real_fopen_2_1 = __real_fopen; + __real_fclose_2_1 = __real_fclose; + __real_fdopen_2_1 = __real_fdopen; + __real_fgetpos_2_2 = __real_fgetpos; + __real_fsetpos_2_2 = __real_fsetpos; + + __real_fopen_2_0 = (FILE * (*)(const char*, const char*))dlvsym (dlflag, "fopen", "GLIBC_2.0"); + if (__real_fopen_2_0 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fopen_2_0\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fclose_2_0 = (int (*)(FILE*))dlvsym (dlflag, "fclose", "GLIBC_2.0"); + if (__real_fclose_2_0 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fclose_2_0\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fdopen_2_0 = (FILE * (*)(int, const char*))dlvsym (dlflag, "fdopen", "GLIBC_2.0"); + if (__real_fdopen_2_0 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fdopen_2_0\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fgetpos_2_0 = (int (*)(FILE*, fpos_t*))dlvsym (dlflag, "fgetpos", "GLIBC_2.0"); + if (__real_fgetpos_2_0 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fgetpos_2_0\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fsetpos_2_0 = (int (*)(FILE*, const fpos_t*))dlvsym (dlflag, "fsetpos", "GLIBC_2.0"); + if (__real_fsetpos_2_0 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fsetpos_2_0\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fgetpos64_2_1 = (int (*)(FILE*, fpos64_t*))dlvsym (dlflag, "fgetpos64", "GLIBC_2.1"); + if (__real_fgetpos64_2_1 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fgetpos64_2_1\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fsetpos64_2_1 = (int (*)(FILE*, const fpos64_t*))dlvsym (dlflag, "fsetpos64", "GLIBC_2.1"); + if (__real_fsetpos64_2_1 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fsetpos64_2_1\n"); + rc = COL_ERROR_IOINIT; + } + + __real_open64_2_1 = (int (*)(const char*, int, ...))dlvsym (dlflag, "open64", "GLIBC_2.1"); + if (__real_open64_2_1 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT open64_2_1\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pread_2_1 = (int (*)(int fildes, void *buf, size_t nbyte, off_t offset))dlvsym (dlflag, "pread", "GLIBC_2.1"); + if (__real_pread_2_1 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pread_2_1\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pwrite_2_1 = (int (*)(int fildes, const void *buf, size_t nbyte, off_t offset))dlvsym (dlflag, "pwrite", "GLIBC_2.1"); + if (__real_pwrite_2_1 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pwrite_2_1\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pwrite64_2_1 = (int (*)(int fildes, const void *buf, size_t nbyte, off64_t offset))dlvsym (dlflag, "pwrite64", "GLIBC_2.1"); + if (__real_pwrite64_2_1 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pwrite64_2_1\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fgetpos64_2_2 = (int (*)(FILE*, fpos64_t*))dlvsym (dlflag, "fgetpos64", SYS_FGETPOS64_X_VERSION); + if (__real_fgetpos64_2_2 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fgetpos64_2_2\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fsetpos64_2_2 = (int (*)(FILE*, const fpos64_t*))dlvsym (dlflag, "fsetpos64", SYS_FGETPOS64_X_VERSION); + if (__real_fsetpos64_2_2 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fsetpos64_2_2\n"); + rc = COL_ERROR_IOINIT; + } + + __real_open64_2_2 = (int (*)(const char*, int, ...))dlvsym (dlflag, "open64", SYS_OPEN64_X_VERSION); + if (__real_open64_2_2 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT open64_2_2\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pread_2_2 = (int (*)(int fildes, void *buf, size_t nbyte, off_t offset))dlvsym (dlflag, "pread", SYS_PREAD_X_VERSION); + if (__real_pread_2_2 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pread_2_2\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pwrite_2_2 = (int (*)(int fildes, const void *buf, size_t nbyte, off_t offset))dlvsym (dlflag, "pwrite", SYS_PWRITE_X_VERSION); + if (__real_pwrite_2_2 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pwrite_2_2\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pwrite64_2_2 = (int (*)(int fildes, const void *buf, size_t nbyte, off64_t offset))dlvsym (dlflag, "pwrite64", SYS_PWRITE64_X_VERSION); + if (__real_pwrite64_2_2 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pwrite64_2_2\n"); + rc = COL_ERROR_IOINIT; + } + +#endif + +#else /* WSIZE(64) */ + dlflag = RTLD_NEXT; + __real_fopen = (FILE * (*)(const char*, const char*))dlvsym (dlflag, "fopen", SYS_FOPEN_X_VERSION); + if (__real_fopen == NULL) + { + /* We are probably dlopened after libc, + * try to search in the previously loaded objects + */ + __real_fopen = (FILE * (*)(const char*, const char*))dlvsym (RTLD_DEFAULT, "fopen", SYS_FOPEN_X_VERSION); + if (__real_fopen != NULL) + { + Tprintf (0, "iotrace: WARNING: init_io_intf() using RTLD_DEFAULT for Linux io routines\n"); + dlflag = RTLD_DEFAULT; + } + else + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fopen\n"); + rc = COL_ERROR_IOINIT; + } + } + + __real_fclose = (int (*)(FILE*))dlvsym (dlflag, "fclose", SYS_FOPEN_X_VERSION); + if (__real_fclose == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fclose\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fdopen = (FILE * (*)(int, const char*))dlvsym (dlflag, "fdopen", SYS_FOPEN_X_VERSION); + if (__real_fdopen == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fdopen\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fgetpos = (int (*)(FILE*, fpos_t*))dlvsym (dlflag, "fgetpos", SYS_FGETPOS_X_VERSION); + if (__real_fgetpos == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fgetpos\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fsetpos = (int (*)(FILE*, const fpos_t*))dlvsym (dlflag, "fsetpos", SYS_FGETPOS_X_VERSION); + if (__real_fsetpos == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fsetpos\n"); + rc = COL_ERROR_IOINIT; + } +#endif /* WSIZE(32) */ + + __real_open = (int (*)(const char*, int, ...))dlsym (dlflag, "open"); + if (__real_open == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT open\n"); + rc = COL_ERROR_IOINIT; + } + +#if WSIZE(32) + __real_open64 = (int (*)(const char*, int, ...))dlsym (dlflag, "open64"); + if (__real_open64 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT open64\n"); + rc = COL_ERROR_IOINIT; + } +#endif + + __real_fcntl = (int (*)(int, int, ...))dlsym (dlflag, "fcntl"); + if (__real_fcntl == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fcntl\n"); + rc = COL_ERROR_IOINIT; + } + + __real_openat = (int (*)(int, const char*, int, ...))dlsym (dlflag, "openat"); + if (__real_openat == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT openat\n"); + rc = COL_ERROR_IOINIT; + } + + __real_close = (int (*)(int))dlsym (dlflag, "close"); + if (__real_close == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT close\n"); + rc = COL_ERROR_IOINIT; + } + + __real_dup = (int (*)(int))dlsym (dlflag, "dup"); + if (__real_dup == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT dup\n"); + rc = COL_ERROR_IOINIT; + } + + __real_dup2 = (int (*)(int, int))dlsym (dlflag, "dup2"); + if (__real_dup2 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT dup2\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pipe = (int (*)(int[]))dlsym (dlflag, "pipe"); + if (__real_pipe == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pipe\n"); + rc = COL_ERROR_IOINIT; + } + + __real_socket = (int (*)(int, int, int))dlsym (dlflag, "socket"); + if (__real_socket == NULL) + { + __real_socket = (int (*)(int, int, int))dlsym (RTLD_NEXT, "socket"); + if (__real_socket == NULL) + { +#if 0 + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERXXX_IOINIT socket\n"); + rc = COL_ERROR_IOINIT; +#endif + } + } + + __real_mkstemp = (int (*)(char*))dlsym (dlflag, "mkstemp"); + if (__real_mkstemp == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT mkstemp\n"); + rc = COL_ERROR_IOINIT; + } + + __real_mkstemps = (int (*)(char*, int))dlsym (dlflag, "mkstemps"); + if (__real_mkstemps == NULL) + { +#if 0 + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERXXX_IOINIT mkstemps\n"); + rc = COL_ERROR_IOINIT; +#endif + } + + __real_creat = (int (*)(const char*, mode_t))dlsym (dlflag, "creat"); + if (__real_creat == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT creat\n"); + rc = COL_ERROR_IOINIT; + } + +#if WSIZE(32) + __real_creat64 = (int (*)(const char*, mode_t))dlsym (dlflag, "creat64"); + if (__real_creat64 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT creat64\n"); + rc = COL_ERROR_IOINIT; + } +#endif + + __real_read = (ssize_t (*)(int, void*, size_t))dlsym (dlflag, "read"); + if (__real_read == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT read\n"); + rc = COL_ERROR_IOINIT; + } + + __real_write = (ssize_t (*)(int, const void*, size_t))dlsym (dlflag, "write"); + if (__real_write == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT write\n"); + rc = COL_ERROR_IOINIT; + } + + __real_readv = (ssize_t (*)(int, const struct iovec*, int))dlsym (dlflag, "readv"); + if (__real_readv == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT readv\n"); + rc = COL_ERROR_IOINIT; + } + + __real_writev = (ssize_t (*)(int, const struct iovec*, int))dlsym (dlflag, "writev"); + if (__real_writev == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT writev\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fread = (size_t (*)(void*, size_t, size_t, FILE*))dlsym (dlflag, "fread"); + if (__real_fread == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fread\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fwrite = (size_t (*)(const void*, size_t, size_t, FILE*))dlsym (dlflag, "fwrite"); + if (__real_fwrite == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fwrite\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pread = (ssize_t (*)(int, void*, size_t, off_t))dlsym (dlflag, "pread"); + if (__real_pread == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pread\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pwrite = (ssize_t (*)(int, const void*, size_t, off_t))dlsym (dlflag, "pwrite"); + if (__real_pwrite == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pwrite\n"); + rc = COL_ERROR_IOINIT; + } + + __real_pwrite64 = (ssize_t (*)(int, const void*, size_t, off64_t))dlsym (dlflag, "pwrite64"); + if (__real_pwrite64 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT pwrite64\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fgets = (char* (*)(char*, int, FILE*))dlsym (dlflag, "fgets"); + if (__real_fgets == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fgets\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fputs = (int (*)(const char*, FILE*))dlsym (dlflag, "fputs"); + if (__real_fputs == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fputs\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fputc = (int (*)(int, FILE*))dlsym (dlflag, "fputc"); + if (__real_fputc == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fputc\n"); + rc = COL_ERROR_IOINIT; + } + + __real_vfprintf = (int (*)(FILE*, const char*, va_list))dlsym (dlflag, "vfprintf"); + if (__real_vfprintf == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT vfprintf\n"); + rc = COL_ERROR_IOINIT; + } + + + __real_lseek = (off_t (*)(int, off_t, int))dlsym (dlflag, "lseek"); + if (__real_lseek == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT lseek\n"); + rc = COL_ERROR_IOINIT; + } + + __real_llseek = (offset_t (*)(int, offset_t, int))dlsym (dlflag, "llseek"); + if (__real_llseek == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT llseek\n"); + rc = COL_ERROR_IOINIT; + } + + __real_chmod = (int (*)(const char*, mode_t))dlsym (dlflag, "chmod"); + if (__real_chmod == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT chmod\n"); + rc = COL_ERROR_IOINIT; + } + + __real_access = (int (*)(const char*, int))dlsym (dlflag, "access"); + if (__real_access == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT access\n"); + rc = COL_ERROR_IOINIT; + } + + __real_rename = (int (*)(const char*, const char*))dlsym (dlflag, "rename"); + if (__real_rename == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT rename\n"); + rc = COL_ERROR_IOINIT; + } + + __real_mkdir = (int (*)(const char*, mode_t))dlsym (dlflag, "mkdir"); + if (__real_mkdir == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT mkdir\n"); + rc = COL_ERROR_IOINIT; + } + + __real_getdents = (int (*)(int, struct dirent*, size_t))dlsym (dlflag, "getdents"); + if (__real_getdents == NULL) + { +#if 0 + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERXXX_IOINIT getdents\n"); + rc = COL_ERROR_IOINIT; +#endif + } + + __real_unlink = (int (*)(const char*))dlsym (dlflag, "unlink"); + if (__real_unlink == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT unlink\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fseek = (int (*)(FILE*, long, int))dlsym (dlflag, "fseek"); + if (__real_fseek == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fseek\n"); + rc = COL_ERROR_IOINIT; + } + + __real_rewind = (void (*)(FILE*))dlsym (dlflag, "rewind"); + if (__real_rewind == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT rewind\n"); + rc = COL_ERROR_IOINIT; + } + + __real_ftell = (long (*)(FILE*))dlsym (dlflag, "ftell"); + if (__real_ftell == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT ftell\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fsync = (int (*)(int))dlsym (dlflag, "fsync"); + if (__real_fsync == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fsync\n"); + rc = COL_ERROR_IOINIT; + } + + __real_readdir = (struct dirent * (*)(DIR*))dlsym (dlflag, "readdir"); + if (__real_readdir == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT readdir\n"); + rc = COL_ERROR_IOINIT; + } + + __real_flock = (int (*)(int, int))dlsym (dlflag, "flock"); + if (__real_flock == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT flock\n"); + rc = COL_ERROR_IOINIT; + } + + __real_lockf = (int (*)(int, int, off_t))dlsym (dlflag, "lockf"); + if (__real_lockf == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT lockf\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fflush = (int (*)(FILE*))dlsym (dlflag, "fflush"); + if (__real_fflush == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fflush\n"); + rc = COL_ERROR_IOINIT; + } + +#if WSIZE(32) + __real_fgetpos64 = (int (*)(FILE*, fpos64_t*))dlsym (dlflag, "fgetpos64"); + if (__real_fgetpos64 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fgetpos64\n"); + rc = COL_ERROR_IOINIT; + } + + __real_fsetpos64 = (int (*)(FILE*, const fpos64_t*))dlsym (dlflag, "fsetpos64"); + if (__real_fsetpos64 == NULL) + { + CALL_REAL (fprintf)(stderr, "iotrace_init COL_ERROR_IOINIT fsetpos64\n"); + rc = COL_ERROR_IOINIT; + } +#endif + init_io_intf_finished++; + return rc; +} + +/*------------------------------------------------------------- open */ +int +open (const char *path, int oflag, ...) +{ + int *guard; + int fd; + void *packet; + IOTrace_packet *iopkt; + mode_t mode; + va_list ap; + size_t sz; + unsigned pktSize; + + va_start (ap, oflag); + mode = va_arg (ap, mode_t); + va_end (ap); + + if (NULL_PTR (open)) + init_io_intf (); + + if (CHCK_REENTRANCE (guard) || path == NULL) + return CALL_REAL (open)(path, oflag, mode); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (open)(path, oflag, mode); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (path); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (fd != -1) + iopkt->iotype = OPEN_TRACE; + else + iopkt->iotype = OPEN_TRACE_ERROR; + iopkt->fd = fd; + iopkt->fstype = collector_fstype (path); + collector_strncpy (&(iopkt->fname), path, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: open cannot allocate memory\n"); + return -1; + } + POP_REENTRANCE (guard); + return fd; +} + +/*------------------------------------------------------------- open64 */ +#if ARCH(Intel) && WSIZE(32) +// map interposed symbol versions +static int +__collector_open64_symver (int(real_open64) (const char *, int, ...), + const char *path, int oflag, mode_t mode); + +int +__collector_open64_2_2 (const char *path, int oflag, ...) +{ + mode_t mode; + va_list ap; + va_start (ap, oflag); + mode = va_arg (ap, mode_t); + va_end (ap); + TprintfT (DBG_LTT, + "iotrace: __collector_open64_2_2@%p(path=%s, oflag=0%o, mode=0%o\n", + CALL_REAL (open64_2_2), path ? path : "NULL", oflag, mode); + if (NULL_PTR (open64)) + init_io_intf (); + return __collector_open64_symver (CALL_REAL (open64_2_2), path, oflag, mode); +} + +int +__collector_open64_2_1 (const char *path, int oflag, ...) +{ + mode_t mode; + va_list ap; + va_start (ap, oflag); + mode = va_arg (ap, mode_t); + va_end (ap); + TprintfT (DBG_LTT, + "iotrace: __collector_open64_2_1@%p(path=%s, oflag=0%o, mode=0%o\n", + CALL_REAL (open64_2_1), path ? path : "NULL", oflag, mode); + if (NULL_PTR (open64)) + init_io_intf (); + return __collector_open64_symver (CALL_REAL (open64_2_1), path, oflag, mode); +} + +__asm__(".symver __collector_open64_2_2,open64@@GLIBC_2.2"); +__asm__(".symver __collector_open64_2_1,open64@GLIBC_2.1"); + +#endif /* ARCH(Intel) && WSIZE(32) */ +#if WSIZE(32) +#if ARCH(Intel) && WSIZE(32) + +static int +__collector_open64_symver (int(real_open64) (const char *, int, ...), + const char *path, int oflag, mode_t mode) +{ + int *guard; + int fd; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (open64)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || path == NULL) + return (real_open64) (path, oflag, mode); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = real_open64 (path, oflag, mode); +#else /* ^ARCH(Intel) && WSIZE(32) */ + +int +open64 (const char *path, int oflag, ...) +{ + int *guard; + int fd; + void *packet; + IOTrace_packet *iopkt; + mode_t mode; + va_list ap; + size_t sz; + unsigned pktSize; + + va_start (ap, oflag); + mode = va_arg (ap, mode_t); + va_end (ap); + if (NULL_PTR (open64)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || path == NULL) + return CALL_REAL (open64)(path, oflag, mode); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (open64)(path, oflag, mode); +#endif /* ^ARCH(Intel) && WSIZE(32) */ + + if (RECHCK_REENTRANCE (guard) || path == NULL) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (path); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (fd != -1) + iopkt->iotype = OPEN_TRACE; + else + iopkt->iotype = OPEN_TRACE_ERROR; + iopkt->fd = fd; + iopkt->fstype = collector_fstype (path); + collector_strncpy (&(iopkt->fname), path, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: open64 cannot allocate memory\n"); + return -1; + } + POP_REENTRANCE (guard); + return fd; +} +#endif + +#define F_ERROR_ARG 0 +#define F_INT_ARG 1 +#define F_LONG_ARG 2 +#define F_VOID_ARG 3 + +/* + * The following macro is not defined in the + * older versions of Linux. + * #define F_DUPFD_CLOEXEC 1030 + * + * Instead use the command that is defined below + * until we start compiling mpmt on the newer + * versions of Linux. + */ +#define TMP_F_DUPFD_CLOEXEC 1030 + +/*------------------------------------------------------------- fcntl */ +int +fcntl (int fildes, int cmd, ...) +{ + int *guard; + int fd = 0; + IOTrace_packet iopkt; + long long_arg = 0; + int int_arg = 0; + int which_arg = F_ERROR_ARG; + va_list ap; + switch (cmd) + { + case F_DUPFD: + case TMP_F_DUPFD_CLOEXEC: + case F_SETFD: + case F_SETFL: + case F_SETOWN: + case F_SETSIG: + case F_SETLEASE: + case F_NOTIFY: + case F_SETLK: + case F_SETLKW: + case F_GETLK: + va_start (ap, cmd); + long_arg = va_arg (ap, long); + va_end (ap); + which_arg = F_LONG_ARG; + break; + case F_GETFD: + case F_GETFL: + case F_GETOWN: + case F_GETLEASE: + case F_GETSIG: + which_arg = F_VOID_ARG; + break; + } + if (NULL_PTR (fcntl)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + { + switch (which_arg) + { + case F_INT_ARG: + return CALL_REAL (fcntl)(fildes, cmd, int_arg); + case F_LONG_ARG: + return CALL_REAL (fcntl)(fildes, cmd, long_arg); + case F_VOID_ARG: + return CALL_REAL (fcntl)(fildes, cmd); + case F_ERROR_ARG: + Tprintf (0, "iotrace: ERROR: Unsupported fcntl command\n"); + return -1; + } + return -1; + } + if (cmd != F_DUPFD && cmd != TMP_F_DUPFD_CLOEXEC) + { + switch (which_arg) + { + case F_INT_ARG: + return CALL_REAL (fcntl)(fildes, cmd, int_arg); + case F_LONG_ARG: + return CALL_REAL (fcntl)(fildes, cmd, long_arg); + case F_VOID_ARG: + return CALL_REAL (fcntl)(fildes, cmd); + case F_ERROR_ARG: + Tprintf (0, "iotrace: ERROR: Unsupported fcntl command\n"); + return -1; + } + return -1; + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + switch (cmd) + { + case F_DUPFD: + case TMP_F_DUPFD_CLOEXEC: + fd = CALL_REAL (fcntl)(fildes, cmd, long_arg); + break; + } + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (fd != -1) + iopkt.iotype = OPEN_TRACE; + else + iopkt.iotype = OPEN_TRACE_ERROR; + iopkt.fd = fd; + iopkt.ofd = fildes; + iopkt.fstype = UNKNOWNFS_TYPE; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return fd; +} + +/*------------------------------------------------------------- openat */ +int +openat (int fildes, const char *path, int oflag, ...) +{ + int *guard; + int fd; + void *packet; + IOTrace_packet *iopkt; + mode_t mode; + va_list ap; + size_t sz; + unsigned pktSize; + + va_start (ap, oflag); + mode = va_arg (ap, mode_t); + va_end (ap); + if (NULL_PTR (openat)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || path == NULL) + return CALL_REAL (openat)(fildes, path, oflag, mode); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (openat)(fildes, path, oflag, mode); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (path); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (fd != -1) + iopkt->iotype = OPEN_TRACE; + else + iopkt->iotype = OPEN_TRACE_ERROR; + iopkt->fd = fd; + iopkt->fstype = collector_fstype (path); + collector_strncpy (&(iopkt->fname), path, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: openat cannot allocate memory\n"); + return -1; + } + POP_REENTRANCE (guard); + return fd; +} + +/*------------------------------------------------------------- creat */ +int +creat (const char *path, mode_t mode) +{ + int *guard; + int fd; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (creat)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || path == NULL) + return CALL_REAL (creat)(path, mode); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (creat)(path, mode); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (path); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (fd != -1) + iopkt->iotype = OPEN_TRACE; + else + iopkt->iotype = OPEN_TRACE_ERROR; + iopkt->fd = fd; + iopkt->fstype = collector_fstype (path); + collector_strncpy (&(iopkt->fname), path, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: creat cannot allocate memory\n"); + return -1; + } + POP_REENTRANCE (guard); + return fd; +} + +/*------------------------------------------------------------- creat64 */ +#if WSIZE(32) +int +creat64 (const char *path, mode_t mode) +{ + int *guard; + int fd; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + + if (NULL_PTR (creat64)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || path == NULL) + return CALL_REAL (creat64)(path, mode); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (creat64)(path, mode); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (path); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (fd != -1) + iopkt->iotype = OPEN_TRACE; + else + iopkt->iotype = OPEN_TRACE_ERROR; + iopkt->fd = fd; + iopkt->fstype = collector_fstype (path); + collector_strncpy (&(iopkt->fname), path, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: creat64 cannot allocate memory\n"); + return -1; + } + POP_REENTRANCE (guard); + return fd; +} +#endif + +/*------------------------------------------------------------- mkstemp */ +int +mkstemp (char *template) +{ + int *guard; + int fd; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (mkstemp)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || template == NULL) + return CALL_REAL (mkstemp)(template); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (mkstemp)(template); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (template); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (fd != -1) + iopkt->iotype = OPEN_TRACE; + else + iopkt->iotype = OPEN_TRACE_ERROR; + iopkt->fd = fd; + iopkt->fstype = collector_fstype (template); + collector_strncpy (&(iopkt->fname), template, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: mkstemp cannot allocate memory\n"); + return -1; + } + POP_REENTRANCE (guard); + return fd; +} + +/*------------------------------------------------------------- mkstemps */ +int +mkstemps (char *template, int slen) +{ + int *guard; + int fd; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (mkstemps)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || template == NULL) + return CALL_REAL (mkstemps)(template, slen); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (mkstemps)(template, slen); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (template); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (fd != -1) + iopkt->iotype = OPEN_TRACE; + else + iopkt->iotype = OPEN_TRACE_ERROR; + iopkt->fd = fd; + iopkt->fstype = collector_fstype (template); + collector_strncpy (&(iopkt->fname), template, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: mkstemps cannot allocate memory\n"); + return -1; + } + POP_REENTRANCE (guard); + return fd; +} + +/*------------------------------------------------------------- close */ +int +close (int fildes) +{ + int *guard; + int stat; + IOTrace_packet iopkt; + if (NULL_PTR (close)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (close)(fildes); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + stat = CALL_REAL (close)(fildes); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return stat; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (stat == 0) + iopkt.iotype = CLOSE_TRACE; + else + iopkt.iotype = CLOSE_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return stat; +} + +/*------------------------------------------------------------- fopen */ +// map interposed symbol versions +#if ARCH(Intel) && WSIZE(32) + +static FILE* +__collector_fopen_symver (FILE*(real_fopen) (), const char *filename, const char *mode); + +FILE* +__collector_fopen_2_1 (const char *filename, const char *mode) +{ + if (NULL_PTR (fopen)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fopen_2_1@%p\n", CALL_REAL (fopen_2_1)); + return __collector_fopen_symver (CALL_REAL (fopen_2_1), filename, mode); +} + +FILE* +__collector_fopen_2_0 (const char *filename, const char *mode) +{ + if (NULL_PTR (fopen)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fopen_2_0@%p\n", CALL_REAL (fopen_2_0)); + return __collector_fopen_symver (CALL_REAL (fopen_2_0), filename, mode); +} + +__asm__(".symver __collector_fopen_2_1,fopen@@GLIBC_2.1"); +__asm__(".symver __collector_fopen_2_0,fopen@GLIBC_2.0"); + +#endif + +#if ARCH(Intel) && WSIZE(32) + +static FILE* +__collector_fopen_symver (FILE*(real_fopen) (), const char *filename, const char *mode) +{ +#else + +FILE* +fopen (const char *filename, const char *mode) +{ +#endif + int *guard; + FILE *fp = NULL; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (fopen)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || filename == NULL) + { +#if ARCH(Intel) && WSIZE(32) + return (real_fopen) (filename, mode); +#else + return CALL_REAL (fopen)(filename, mode); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + +#if ARCH(Intel) && WSIZE(32) + fp = (real_fopen) (filename, mode); +#else + fp = CALL_REAL (fopen)(filename, mode); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fp; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (filename); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (fp != NULL) + { + iopkt->iotype = OPEN_TRACE; + iopkt->fd = fileno (fp); + } + else + { + iopkt->iotype = OPEN_TRACE_ERROR; + iopkt->fd = -1; + } + iopkt->fstype = collector_fstype (filename); + collector_strncpy (&(iopkt->fname), filename, sz); + +#if ARCH(Intel) && WSIZE(32) + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK_ARG, &iopkt); +#else + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); +#endif + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: fopen cannot allocate memory\n"); + return NULL; + } + POP_REENTRANCE (guard); + return fp; +} + +/*------------------------------------------------------------- fclose */ +// map interposed symbol versions +#if ARCH(Intel) && WSIZE(32) + +static int +__collector_fclose_symver (int(real_fclose) (), FILE *stream); + +int +__collector_fclose_2_1 (FILE *stream) +{ + if (NULL_PTR (fclose)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fclose_2_1@%p\n", CALL_REAL (fclose_2_1)); + return __collector_fclose_symver (CALL_REAL (fclose_2_1), stream); +} + +int +__collector_fclose_2_0 (FILE *stream) +{ + if (NULL_PTR (fclose)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fclose_2_0@%p\n", CALL_REAL (fclose_2_0)); + return __collector_fclose_symver (CALL_REAL (fclose_2_0), stream); +} + +__asm__(".symver __collector_fclose_2_1,fclose@@GLIBC_2.1"); +__asm__(".symver __collector_fclose_2_0,fclose@GLIBC_2.0"); + +#endif + +#if ARCH(Intel) && WSIZE(32) + +static int +__collector_fclose_symver (int(real_fclose) (), FILE *stream) +{ +#else + +int +fclose (FILE *stream) +{ +#endif + int *guard; + int stat; + IOTrace_packet iopkt; + if (NULL_PTR (fclose)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + { +#if ARCH(Intel) && WSIZE(32) + return (real_fclose) (stream); +#else + return CALL_REAL (fclose)(stream); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); +#if ARCH(Intel) && WSIZE(32) + stat = (real_fclose) (stream); +#else + stat = CALL_REAL (fclose)(stream); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return stat; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (stat == 0) + iopkt.iotype = CLOSE_TRACE; + else + iopkt.iotype = CLOSE_TRACE_ERROR; + iopkt.fd = fileno (stream); + +#if ARCH(Intel) && WSIZE(32) + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK_ARG, &iopkt); +#else + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); +#endif + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return stat; +} + +/*------------------------------------------------------------- fflush */ +int +fflush (FILE *stream) +{ + int *guard; + int stat; + IOTrace_packet iopkt; + if (NULL_PTR (fflush)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (fflush)(stream); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + stat = CALL_REAL (fflush)(stream); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return stat; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (stat == 0) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + if (stream != NULL) + iopkt.fd = fileno (stream); + else + iopkt.fd = -1; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return stat; +} + +/*------------------------------------------------------------- fdopen */ +// map interposed symbol versions +#if ARCH(Intel) && WSIZE(32) + +static FILE* +__collector_fdopen_symver (FILE*(real_fdopen) (), int fildes, const char *mode); + +FILE* +__collector_fdopen_2_1 (int fildes, const char *mode) +{ + if (NULL_PTR (fdopen)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fdopen_2_1@%p\n", CALL_REAL (fdopen_2_1)); + return __collector_fdopen_symver (CALL_REAL (fdopen_2_1), fildes, mode); +} + +FILE* +__collector_fdopen_2_0 (int fildes, const char *mode) +{ + if (NULL_PTR (fdopen)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fdopen_2_0@%p\n", CALL_REAL (fdopen_2_0)); + return __collector_fdopen_symver (CALL_REAL (fdopen_2_0), fildes, mode); +} + +__asm__(".symver __collector_fdopen_2_1,fdopen@@GLIBC_2.1"); +__asm__(".symver __collector_fdopen_2_0,fdopen@GLIBC_2.0"); + +#endif + +#if ARCH(Intel) && WSIZE(32) +static FILE* +__collector_fdopen_symver (FILE*(real_fdopen) (), int fildes, const char *mode) +{ +#else +FILE* +fdopen (int fildes, const char *mode) +{ +#endif + int *guard; + FILE *fp = NULL; + IOTrace_packet iopkt; + if (NULL_PTR (fdopen)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + { +#if ARCH(Intel) && WSIZE(32) + return (real_fdopen) (fildes, mode); +#else + return CALL_REAL (fdopen)(fildes, mode); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); +#if ARCH(Intel) && WSIZE(32) + fp = (real_fdopen) (fildes, mode); +#else + fp = CALL_REAL (fdopen)(fildes, mode); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fp; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (fp != NULL) + iopkt.iotype = OPEN_TRACE; + else + iopkt.iotype = OPEN_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.fstype = UNKNOWNFS_TYPE; +#if ARCH(Intel) && WSIZE(32) + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK_ARG, &iopkt); +#else + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); +#endif + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return fp; +} + +/*------------------------------------------------------------- dup */ +int +dup (int fildes) +{ + int *guard; + int fd; + IOTrace_packet iopkt; + if (NULL_PTR (dup)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (dup)(fildes); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (dup)(fildes); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (fd != -1) + iopkt.iotype = OPEN_TRACE; + else + iopkt.iotype = OPEN_TRACE_ERROR; + + iopkt.fd = fd; + iopkt.ofd = fildes; + iopkt.fstype = UNKNOWNFS_TYPE; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return fd; +} + +/*------------------------------------------------------------- dup2 */ +int +dup2 (int fildes, int fildes2) +{ + int *guard; + int fd; + IOTrace_packet iopkt; + if (NULL_PTR (dup2)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (dup2)(fildes, fildes2); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (dup2)(fildes, fildes2); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (fd != -1) + iopkt.iotype = OPEN_TRACE; + else + iopkt.iotype = OPEN_TRACE_ERROR; + iopkt.fd = fd; + iopkt.ofd = fildes; + iopkt.fstype = UNKNOWNFS_TYPE; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return fd; +} + +/*------------------------------------------------------------- pipe */ +int +pipe (int fildes[2]) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (pipe)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (pipe)(fildes); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (pipe)(fildes); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret != -1) + iopkt.iotype = OPEN_TRACE; + else + iopkt.iotype = OPEN_TRACE_ERROR; + iopkt.fd = fildes[0]; + iopkt.fstype = UNKNOWNFS_TYPE; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret != -1) + iopkt.iotype = OPEN_TRACE; + else + iopkt.iotype = OPEN_TRACE_ERROR; + iopkt.fd = fildes[1]; + iopkt.fstype = UNKNOWNFS_TYPE; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- socket */ +int +socket (int domain, int type, int protocol) +{ + int *guard; + int fd; + IOTrace_packet iopkt; + if (NULL_PTR (socket)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (socket)(domain, type, protocol); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + fd = CALL_REAL (socket)(domain, type, protocol); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return fd; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (fd != -1) + iopkt.iotype = OPEN_TRACE; + else + iopkt.iotype = OPEN_TRACE_ERROR; + iopkt.fd = fd; + iopkt.fstype = UNKNOWNFS_TYPE; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return fd; +} + +/*------------------------------------------------------------- read */ +ssize_t +read (int fildes, void *buf, size_t nbyte) +{ + int *guard; + ssize_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (read)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (read)(fildes, buf, nbyte); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (read)(fildes, buf, nbyte); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret >= 0) + iopkt.iotype = READ_TRACE; + else + iopkt.iotype = READ_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.nbyte = ret; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- write */ +ssize_t +write (int fildes, const void *buf, size_t nbyte) +{ + int *guard; + ssize_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (write)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (write)(fildes, buf, nbyte); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (write)(fildes, buf, nbyte); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret >= 0) + iopkt.iotype = WRITE_TRACE; + else + iopkt.iotype = WRITE_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.nbyte = ret; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- readv */ +ssize_t +readv (int fildes, const struct iovec *iov, int iovcnt) +{ + int *guard; + ssize_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (readv)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (readv)(fildes, iov, iovcnt); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (readv)(fildes, iov, iovcnt); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret >= 0) + iopkt.iotype = READ_TRACE; + else + iopkt.iotype = READ_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.nbyte = ret; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- writev */ +ssize_t +writev (int fildes, const struct iovec *iov, int iovcnt) +{ + int *guard; + ssize_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (writev)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (writev)(fildes, iov, iovcnt); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (writev)(fildes, iov, iovcnt); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret >= 0) + iopkt.iotype = WRITE_TRACE; + else + iopkt.iotype = WRITE_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.nbyte = ret; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- fread */ +size_t +fread (void *ptr, size_t size, size_t nitems, FILE *stream) +{ + int *guard; + size_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (fread)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + return CALL_REAL (fread)(ptr, size, nitems, stream); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (fread)(ptr, size, nitems, stream); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ferror (stream) == 0) + { + iopkt.iotype = READ_TRACE; + iopkt.nbyte = ret * size; + } + else + { + iopkt.iotype = READ_TRACE_ERROR; + iopkt.nbyte = 0; + } + iopkt.fd = fileno (stream); + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- fwrite */ +size_t +fwrite (const void *ptr, size_t size, size_t nitems, FILE *stream) +{ + int *guard; + size_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (fwrite)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + return CALL_REAL (fwrite)(ptr, size, nitems, stream); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (fwrite)(ptr, size, nitems, stream); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ferror (stream) == 0) + { + iopkt.iotype = WRITE_TRACE; + iopkt.nbyte = ret * size; + } + else + { + iopkt.iotype = WRITE_TRACE_ERROR; + iopkt.nbyte = 0; + } + iopkt.fd = fileno (stream); + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- pread */ +#if ARCH(Intel) && WSIZE(32) +// map interposed symbol versions +static int +__collector_pread_symver (int(real_pread) (), int fildes, void *buf, size_t nbyte, off_t offset); + +int +__collector_pread_2_2 (int fildes, void *buf, size_t nbyte, off_t offset) +{ + TprintfT (DBG_LTT, "iotrace: __collector_pread_2_2@%p(fildes=%d, buf=%p, nbyte=%lld, offset=%lld)\n", + CALL_REAL (pread_2_2), fildes, buf, (long long) nbyte, (long long) offset); + if (NULL_PTR (pread)) + init_io_intf (); + return __collector_pread_symver (CALL_REAL (pread_2_2), fildes, buf, nbyte, offset); +} + +int +__collector_pread_2_1 (int fildes, void *buf, size_t nbyte, off_t offset) +{ + TprintfT (DBG_LTT, "iotrace: __collector_pread_2_1@%p(fildes=%d, buf=%p, nbyte=%lld, offset=%lld)\n", + CALL_REAL (pread_2_1), fildes, buf, (long long) nbyte, (long long) offset); + if (NULL_PTR (pread)) + init_io_intf (); + return __collector_pread_symver (CALL_REAL (pread_2_1), fildes, buf, nbyte, offset); +} + +__asm__(".symver __collector_pread_2_2,pread@@GLIBC_2.2"); +__asm__(".symver __collector_pread_2_1,pread@GLIBC_2.1"); + +static int +__collector_pread_symver (int(real_pread) (), int fildes, void *buf, size_t nbyte, off_t offset) +{ +#else /* ^ARCH(Intel) && WSIZE(32) */ + +ssize_t +pread (int fildes, void *buf, size_t nbyte, off_t offset) +{ +#endif + int *guard; + ssize_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (pread)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + { +#if ARCH(Intel) && WSIZE(32) + return (real_pread) (fildes, buf, nbyte, offset); +#else + return CALL_REAL (pread)(fildes, buf, nbyte, offset); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); +#if ARCH(Intel) && WSIZE(32) + ret = (real_pread) (fildes, buf, nbyte, offset); +#else + ret = CALL_REAL (pread)(fildes, buf, nbyte, offset); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret >= 0) + iopkt.iotype = READ_TRACE; + else + iopkt.iotype = READ_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.nbyte = ret; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- pwrite */ +#if ARCH(Intel) && WSIZE(32) +// map interposed symbol versions +static int +__collector_pwrite_symver (int(real_pwrite) (), int fildes, const void *buf, size_t nbyte, off_t offset); + +int +__collector_pwrite_2_2 (int fildes, const void *buf, size_t nbyte, off_t offset) +{ + TprintfT (DBG_LTT, "iotrace: __collector_pwrite_2_2@%p(fildes=%d, buf=%p, nbyte=%lld, offset=%lld)\n", + CALL_REAL (pwrite_2_2), fildes, buf, (long long) nbyte, (long long) offset); + if (NULL_PTR (pwrite)) + init_io_intf (); + return __collector_pwrite_symver (CALL_REAL (pwrite_2_2), fildes, buf, nbyte, offset); +} + +int +__collector_pwrite_2_1 (int fildes, const void *buf, size_t nbyte, off_t offset) +{ + TprintfT (DBG_LTT, "iotrace: __collector_pwrite_2_1@%p(fildes=%d, buf=%p, nbyte=%lld, offset=%lld)\n", + CALL_REAL (pwrite_2_1), fildes, buf, (long long) nbyte, (long long) offset); + if (NULL_PTR (pwrite)) + init_io_intf (); + return __collector_pwrite_symver (CALL_REAL (pwrite_2_1), fildes, buf, nbyte, offset); +} + +__asm__(".symver __collector_pwrite_2_2,pwrite@@GLIBC_2.2"); +__asm__(".symver __collector_pwrite_2_1,pwrite@GLIBC_2.1"); + +static int +__collector_pwrite_symver (int(real_pwrite) (), int fildes, const void *buf, size_t nbyte, off_t offset) +{ +#else /* ^ARCH(Intel) && WSIZE(32) */ + +ssize_t +pwrite (int fildes, const void *buf, size_t nbyte, off_t offset) +{ +#endif /* ^ARCH(Intel) && WSIZE(32) */ + int *guard; + ssize_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (pwrite)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + { +#if ARCH(Intel) && WSIZE(32) + return (real_pwrite) (fildes, buf, nbyte, offset); +#else + return CALL_REAL (pwrite)(fildes, buf, nbyte, offset); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); +#if ARCH(Intel) && WSIZE(32) + ret = (real_pwrite) (fildes, buf, nbyte, offset); +#else + ret = CALL_REAL (pwrite)(fildes, buf, nbyte, offset); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret >= 0) + iopkt.iotype = WRITE_TRACE; + else + iopkt.iotype = WRITE_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.nbyte = ret; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- pwrite64 */ +#if ARCH(Intel) && WSIZE(32) +// map interposed symbol versions +static int +__collector_pwrite64_symver (int(real_pwrite64) (), int fildes, const void *buf, size_t nbyte, off64_t offset); + +int +__collector_pwrite64_2_2 (int fildes, const void *buf, size_t nbyte, off64_t offset) +{ + TprintfT (DBG_LTT, "iotrace: __collector_pwrite64_2_2@%p(fildes=%d, buf=%p, nbyte=%lld, offset=%lld)\n", + CALL_REAL (pwrite64_2_2), fildes, buf, (long long) nbyte, (long long) offset); + if (NULL_PTR (pwrite64)) + init_io_intf (); + return __collector_pwrite64_symver (CALL_REAL (pwrite64_2_2), fildes, buf, nbyte, offset); +} + +int +__collector_pwrite64_2_1 (int fildes, const void *buf, size_t nbyte, off64_t offset) +{ + TprintfT (DBG_LTT, "iotrace: __collector_pwrite64_2_1@%p(fildes=%d, buf=%p, nbyte=%lld, offset=%lld)\n", + CALL_REAL (pwrite64_2_1), fildes, buf, (long long) nbyte, (long long) offset); + if (NULL_PTR (pwrite64)) + init_io_intf (); + return __collector_pwrite64_symver (CALL_REAL (pwrite64_2_1), fildes, buf, nbyte, offset); +} + +__asm__(".symver __collector_pwrite64_2_2,pwrite64@@GLIBC_2.2"); +__asm__(".symver __collector_pwrite64_2_1,pwrite64@GLIBC_2.1"); + +static int +__collector_pwrite64_symver (int(real_pwrite64) (), int fildes, const void *buf, size_t nbyte, off64_t offset) +{ +#else /* ^ARCH(Intel) && WSIZE(32) */ + +ssize_t +pwrite64 (int fildes, const void *buf, size_t nbyte, off64_t offset) +{ +#endif /* ^ARCH(Intel) && WSIZE(32) */ + int *guard; + ssize_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (pwrite64)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + { +#if ARCH(Intel) && WSIZE(32) + return (real_pwrite64) (fildes, buf, nbyte, offset); +#else + return CALL_REAL (pwrite64)(fildes, buf, nbyte, offset); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); +#if ARCH(Intel) && WSIZE(32) + ret = (real_pwrite64) (fildes, buf, nbyte, offset); +#else + ret = CALL_REAL (pwrite64)(fildes, buf, nbyte, offset); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret >= 0) + iopkt.iotype = WRITE_TRACE; + else + iopkt.iotype = WRITE_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.nbyte = ret; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- fgets */ +char* +fgets (char *s, int n, FILE *stream) +{ + int *guard; + char *ptr; + IOTrace_packet iopkt; + if (NULL_PTR (fgets)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + return CALL_REAL (fgets)(s, n, stream); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ptr = CALL_REAL (fgets)(s, n, stream); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ptr; + } + int error = errno; + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ptr != NULL) + { + iopkt.iotype = READ_TRACE; + iopkt.nbyte = collector_strlen (ptr); + } + else if (ptr == NULL && error != EAGAIN && error != EBADF && error != EINTR && + error != EIO && error != EOVERFLOW && error != ENOMEM && error != ENXIO) + { + iopkt.iotype = READ_TRACE; + iopkt.nbyte = 0; + } + else + iopkt.iotype = READ_TRACE_ERROR; + iopkt.fd = fileno (stream); + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ptr; +} + +/*------------------------------------------------------------- fputs */ +int +fputs (const char *s, FILE *stream) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (fputs)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + return CALL_REAL (fputs)(s, stream); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (fputs)(s, stream); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret != EOF) + { + iopkt.iotype = WRITE_TRACE; + iopkt.nbyte = ret; + } + else + { + iopkt.iotype = WRITE_TRACE_ERROR; + iopkt.nbyte = 0; + } + iopkt.fd = fileno (stream); + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- fputc */ +int +fputc (int c, FILE *stream) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (fputc)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + return CALL_REAL (fputc)(c, stream); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (fputc)(c, stream); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret != EOF) + { + iopkt.iotype = WRITE_TRACE; + iopkt.nbyte = ret; + } + else + { + iopkt.iotype = WRITE_TRACE_ERROR; + iopkt.nbyte = 0; + } + iopkt.fd = fileno (stream); + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- fprintf */ +int +fprintf (FILE *stream, const char *format, ...) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + va_list ap; + va_start (ap, format); + if (NULL_PTR (fprintf)) + init_io_intf (); + if (NULL_PTR (vfprintf)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + return CALL_REAL (vfprintf)(stream, format, ap); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (vfprintf)(stream, format, ap); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret >= 0) + iopkt.iotype = WRITE_TRACE; + else + iopkt.iotype = WRITE_TRACE_ERROR; + iopkt.fd = fileno (stream); + iopkt.nbyte = ret; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- vfprintf */ +int +vfprintf (FILE *stream, const char *format, va_list ap) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (vfprintf)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + return CALL_REAL (vfprintf)(stream, format, ap); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (vfprintf)(stream, format, ap); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret >= 0) + iopkt.iotype = WRITE_TRACE; + else + iopkt.iotype = WRITE_TRACE_ERROR; + iopkt.fd = fileno (stream); + iopkt.nbyte = ret; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- lseek */ +off_t +lseek (int fildes, off_t offset, int whence) +{ + int *guard; + off_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (lseek)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (lseek)(fildes, offset, whence); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (lseek)(fildes, offset, whence); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret != -1) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- llseek */ +offset_t +llseek (int fildes, offset_t offset, int whence) +{ + int *guard; + offset_t ret; + IOTrace_packet iopkt; + if (NULL_PTR (llseek)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (llseek)(fildes, offset, whence); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (llseek)(fildes, offset, whence); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret != -1) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- chmod */ +int +chmod (const char *path, mode_t mode) +{ + int *guard; + int ret; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (chmod)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || path == NULL) + return CALL_REAL (chmod)(path, mode); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (chmod)(path, mode); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (path); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (ret != -1) + iopkt->iotype = OTHERIO_TRACE; + else + iopkt->iotype = OTHERIO_TRACE_ERROR; + collector_strncpy (&(iopkt->fname), path, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: chmod cannot allocate memory\n"); + return 0; + } + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- access */ +int +access (const char *path, int amode) +{ + int *guard; + int ret; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (access)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || path == NULL) + return CALL_REAL (access)(path, amode); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (access)(path, amode); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (path); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (ret != -1) + iopkt->iotype = OTHERIO_TRACE; + else + iopkt->iotype = OTHERIO_TRACE_ERROR; + collector_strncpy (&(iopkt->fname), path, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: access cannot allocate memory\n"); + return 0; + } + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- rename */ +int +rename (const char *old, const char *new) +{ + int *guard; + int ret; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (rename)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || new == NULL) + return CALL_REAL (rename)(old, new); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (rename)(old, new); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (new); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (ret != -1) + iopkt->iotype = OTHERIO_TRACE; + else + iopkt->iotype = OTHERIO_TRACE_ERROR; + collector_strncpy (&(iopkt->fname), new, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: rename cannot allocate memory\n"); + return 0; + } + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- mkdir */ +int +mkdir (const char *path, mode_t mode) +{ + int *guard; + int ret; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (mkdir)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || path == NULL) + return CALL_REAL (mkdir)(path, mode); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (mkdir)(path, mode); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (path); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (ret != -1) + iopkt->iotype = OTHERIO_TRACE; + else + iopkt->iotype = OTHERIO_TRACE_ERROR; + collector_strncpy (&(iopkt->fname), path, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: mkdir cannot allocate memory\n"); + return 0; + } + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- getdents */ +int +getdents (int fildes, struct dirent *buf, size_t nbyte) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (getdents)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (getdents)(fildes, buf, nbyte); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (getdents)(fildes, buf, nbyte); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret != -1) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- unlink */ +int +unlink (const char *path) +{ + int *guard; + int ret; + void *packet; + IOTrace_packet *iopkt; + size_t sz; + unsigned pktSize; + if (NULL_PTR (unlink)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || path == NULL) + return CALL_REAL (unlink)(path); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (unlink)(path); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + sz = collector_strlen (path); + pktSize = sizeof (IOTrace_packet) + sz; + pktSize = collector_align_pktsize (pktSize); + Tprintf (DBG_LT1, "iotrace allocating %u from io_heap\n", pktSize); + packet = collector_interface->allocCSize (io_heap, pktSize, 1); + if (packet != NULL) + { + iopkt = (IOTrace_packet *) packet; + collector_memset (iopkt, 0, pktSize); + iopkt->comm.tsize = pktSize; + iopkt->comm.tstamp = grnt; + iopkt->requested = reqt; + if (ret != -1) + iopkt->iotype = OTHERIO_TRACE; + else + iopkt->iotype = OTHERIO_TRACE_ERROR; + collector_strncpy (&(iopkt->fname), path, sz); + iopkt->comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt->comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) iopkt); + collector_interface->freeCSize (io_heap, packet, pktSize); + } + else + { + Tprintf (0, "iotrace: ERROR: unlink cannot allocate memory\n"); + return 0; + } + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- fseek */ +int +fseek (FILE *stream, long offset, int whence) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (fseek)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + return CALL_REAL (fseek)(stream, offset, whence); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (fseek)(stream, offset, whence); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret != -1) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fileno (stream); + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- rewind */ +void +rewind (FILE *stream) +{ + int *guard; + IOTrace_packet iopkt; + if (NULL_PTR (rewind)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + { + CALL_REAL (rewind)(stream); + return; + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + CALL_REAL (rewind)(stream); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + iopkt.iotype = OTHERIO_TRACE; + iopkt.fd = fileno (stream); + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); +} + +/*------------------------------------------------------------- ftell */ +long +ftell (FILE *stream) +{ + int *guard; + long ret; + IOTrace_packet iopkt; + if (NULL_PTR (ftell)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + return CALL_REAL (ftell)(stream); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (ftell)(stream); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret != -1) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fileno (stream); + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- fgetpos */ +// map interposed symbol versions +#if ARCH(Intel) && WSIZE(32) +static int +__collector_fgetpos_symver (int(real_fgetpos) (), FILE *stream, fpos_t *pos); + +int +__collector_fgetpos_2_2 (FILE *stream, fpos_t *pos) +{ + if (NULL_PTR (fgetpos)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fgetpos_2_2@%p\n", CALL_REAL (fgetpos_2_2)); + return __collector_fgetpos_symver (CALL_REAL (fgetpos_2_2), stream, pos); +} + +int +__collector_fgetpos_2_0 (FILE *stream, fpos_t *pos) +{ + if (NULL_PTR (fgetpos)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fgetpos_2_0@%p\n", CALL_REAL (fgetpos_2_0)); + return __collector_fgetpos_symver (CALL_REAL (fgetpos_2_0), stream, pos); +} + +__asm__(".symver __collector_fgetpos_2_2,fgetpos@@GLIBC_2.2"); +__asm__(".symver __collector_fgetpos_2_0,fgetpos@GLIBC_2.0"); +#endif + +#if ARCH(Intel) && WSIZE(32) + +static int +__collector_fgetpos_symver (int(real_fgetpos) (), FILE *stream, fpos_t *pos) +{ +#else +int +fgetpos (FILE *stream, fpos_t *pos) +{ +#endif + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (fgetpos)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + { +#if ARCH(Intel) && WSIZE(32) + return (real_fgetpos) (stream, pos); +#else + return CALL_REAL (fgetpos)(stream, pos); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); +#if ARCH(Intel) && WSIZE(32) + ret = (real_fgetpos) (stream, pos); +#else + ret = CALL_REAL (fgetpos)(stream, pos); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret == 0) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fileno (stream); + +#if ARCH(Intel) && WSIZE(32) + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK_ARG, &iopkt); +#else + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); +#endif + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +#if WSIZE(32) +/*------------------------------------------------------------- fgetpos64 */ +#if ARCH(Intel) +// map interposed symbol versions + +static int +__collector_fgetpos64_symver (int(real_fgetpos64) (), FILE *stream, fpos64_t *pos); + +int +__collector_fgetpos64_2_2 (FILE *stream, fpos64_t *pos) +{ + TprintfT (DBG_LTT, "iotrace: __collector_fgetpos64_2_2@%p(stream=%p, pos=%p)\n", + CALL_REAL (fgetpos64_2_2), stream, pos); + if (NULL_PTR (fgetpos64)) + init_io_intf (); + return __collector_fgetpos64_symver (CALL_REAL (fgetpos64_2_2), stream, pos); +} + +int +__collector_fgetpos64_2_1 (FILE *stream, fpos64_t *pos) +{ + TprintfT (DBG_LTT, "iotrace: __collector_fgetpos64_2_1@%p(stream=%p, pos=%p)\n", + CALL_REAL (fgetpos64_2_1), stream, pos); + if (NULL_PTR (fgetpos64)) + init_io_intf (); + return __collector_fgetpos64_symver (CALL_REAL (fgetpos64_2_1), stream, pos); +} + +__asm__(".symver __collector_fgetpos64_2_2,fgetpos64@@GLIBC_2.2"); +__asm__(".symver __collector_fgetpos64_2_1,fgetpos64@GLIBC_2.1"); + +static int +__collector_fgetpos64_symver (int(real_fgetpos64) (), FILE *stream, fpos64_t *pos) +{ +#else +int +fgetpos64 (FILE *stream, fpos64_t *pos) +{ +#endif + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (fgetpos64)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + { +#if ARCH(Intel) + return (real_fgetpos64) (stream, pos); +#else + return CALL_REAL (fgetpos64)(stream, pos); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); +#if ARCH(Intel) + ret = (real_fgetpos64) (stream, pos); +#else + ret = CALL_REAL (fgetpos64)(stream, pos); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret == 0) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fileno (stream); +#if ARCH(Intel) + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK_ARG, &iopkt); +#else + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); +#endif + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} +#endif + +/*------------------------------------------------------------- fsetpos */ +// map interposed symbol versions +#if ARCH(Intel) && WSIZE(32) +static int +__collector_fsetpos_symver (int(real_fsetpos) (), FILE *stream, const fpos_t *pos); + +int +__collector_fsetpos_2_2 (FILE *stream, const fpos_t *pos) +{ + if (NULL_PTR (fsetpos)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fsetpos_2_2@%p\n", CALL_REAL (fsetpos_2_2)); + return __collector_fsetpos_symver (CALL_REAL (fsetpos_2_2), stream, pos); +} + +int +__collector_fsetpos_2_0 (FILE *stream, const fpos_t *pos) +{ + if (NULL_PTR (fsetpos)) + init_io_intf (); + TprintfT (DBG_LTT, "iotrace: __collector_fsetpos_2_0@%p\n", CALL_REAL (fsetpos_2_0)); + return __collector_fsetpos_symver (CALL_REAL (fsetpos_2_0), stream, pos); +} + +__asm__(".symver __collector_fsetpos_2_2,fsetpos@@GLIBC_2.2"); +__asm__(".symver __collector_fsetpos_2_0,fsetpos@GLIBC_2.0"); +#endif + +#if ARCH(Intel) && WSIZE(32) + +static int +__collector_fsetpos_symver (int(real_fsetpos) (), FILE *stream, const fpos_t *pos) +{ +#else +int +fsetpos (FILE *stream, const fpos_t *pos) +{ +#endif + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (fsetpos)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + { +#if ARCH(Intel) && WSIZE(32) + return (real_fsetpos) (stream, pos); +#else + return CALL_REAL (fsetpos)(stream, pos); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); +#if ARCH(Intel) && WSIZE(32) + ret = (real_fsetpos) (stream, pos); +#else + ret = CALL_REAL (fsetpos)(stream, pos); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret == 0) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fileno (stream); +#if ARCH(Intel) && WSIZE(32) + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK_ARG, &iopkt); +#else + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); +#endif + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +#if WSIZE(32) +/*------------------------------------------------------------- fsetpos64 */ +#if ARCH(Intel) +// map interposed symbol versions +static int +__collector_fsetpos64_symver (int(real_fsetpos64) (), FILE *stream, const fpos64_t *pos); + +int +__collector_fsetpos64_2_2 (FILE *stream, const fpos64_t *pos) +{ + TprintfT (DBG_LTT, "iotrace: __collector_fsetpos64_2_2@%p(stream=%p, pos=%p)\n", + CALL_REAL (fsetpos64_2_2), stream, pos); + if (NULL_PTR (fsetpos64)) + init_io_intf (); + return __collector_fsetpos64_symver (CALL_REAL (fsetpos64_2_2), stream, pos); +} + +int +__collector_fsetpos64_2_1 (FILE *stream, const fpos64_t *pos) +{ + TprintfT (DBG_LTT, "iotrace: __collector_fsetpos64_2_1@%p(stream=%p, pos=%p)\n", + CALL_REAL (fsetpos64_2_1), stream, pos); + if (NULL_PTR (fsetpos64)) + init_io_intf (); + return __collector_fsetpos64_symver (CALL_REAL (fsetpos64_2_1), stream, pos); +} + +__asm__(".symver __collector_fsetpos64_2_2,fsetpos64@@GLIBC_2.2"); +__asm__(".symver __collector_fsetpos64_2_1,fsetpos64@GLIBC_2.1"); + +static int +__collector_fsetpos64_symver (int(real_fsetpos64) (), FILE *stream, const fpos64_t *pos) +{ +#else +int +fsetpos64 (FILE *stream, const fpos64_t *pos) +{ +#endif + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (fsetpos64)) + init_io_intf (); + if (CHCK_REENTRANCE (guard) || stream == NULL) + { +#if ARCH(Intel) && WSIZE(32) + return (real_fsetpos64) (stream, pos); +#else + return CALL_REAL (fsetpos64)(stream, pos); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); +#if ARCH(Intel) && WSIZE(32) + ret = (real_fsetpos64) (stream, pos); +#else + ret = CALL_REAL (fsetpos64)(stream, pos); +#endif + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret == 0) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fileno (stream); +#if ARCH(Intel) + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK_ARG, &iopkt); +#else + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); +#endif + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} +#endif + +/*------------------------------------------------------------- fsync */ +int +fsync (int fildes) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (fsync)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (fsync)(fildes); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (fsync)(fildes); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret == 0) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- readdir */ +struct dirent* +readdir (DIR *dirp) +{ + int *guard; + struct dirent *ptr; + IOTrace_packet iopkt; + if (NULL_PTR (readdir)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (readdir)(dirp); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ptr = CALL_REAL (readdir)(dirp); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ptr; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof ( IOTrace_packet)); + iopkt.comm.tsize = sizeof ( IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ptr != NULL) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ptr; +} + +/*------------------------------------------------------------- flock */ +int +flock (int fd, int operation) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (flock)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (flock)(fd, operation); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (flock)(fd, operation); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret == 0) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fd; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- lockf */ +int +lockf (int fildes, int function, off_t size) +{ + int *guard; + int ret; + IOTrace_packet iopkt; + if (NULL_PTR (lockf)) + init_io_intf (); + if (CHCK_REENTRANCE (guard)) + return CALL_REAL (lockf)(fildes, function, size); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + ret = CALL_REAL (lockf)(fildes, function, size); + if (RECHCK_REENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + collector_memset (&iopkt, 0, sizeof (IOTrace_packet)); + iopkt.comm.tsize = sizeof (IOTrace_packet); + iopkt.comm.tstamp = grnt; + iopkt.requested = reqt; + if (ret == 0) + iopkt.iotype = OTHERIO_TRACE; + else + iopkt.iotype = OTHERIO_TRACE_ERROR; + iopkt.fd = fildes; + iopkt.comm.frinfo = collector_interface->getFrameInfo (io_hndl, iopkt.comm.tstamp, FRINFO_FROM_STACK, &iopkt); + collector_interface->writeDataRecord (io_hndl, (Common_packet*) & iopkt); + POP_REENTRANCE (guard); + return ret; +} diff --git a/gprofng/libcollector/jprofile.c b/gprofng/libcollector/jprofile.c new file mode 100644 index 0000000..9daaa5a --- /dev/null +++ b/gprofng/libcollector/jprofile.c @@ -0,0 +1,1315 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" + +#if defined(GPROFNG_JAVA_PROFILING) +#include <alloca.h> +#include <dlfcn.h> /* dlsym() */ +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <sys/param.h> /* MAXPATHLEN */ + +#include <jni.h> +#include <jvmti.h> + +#include "gp-defs.h" +#include "collector.h" +#include "gp-experiment.h" +#include "tsd.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +/* ARCH_STRLEN is defined in dbe, copied here */ +#define ARCH_STRLEN(s) ((CALL_UTIL(strlen)(s) + 4 ) & ~0x3) + +/* call frame */ +typedef struct +{ + jint lineno; /* line number in the source file */ + jmethodID method_id; /* method executed in this frame */ +} JVMPI_CallFrame; + +/* call trace */ +typedef struct +{ + JNIEnv *env_id; /* Env where trace was recorded */ + jint num_frames; /* number of frames in this trace */ + JVMPI_CallFrame *frames; /* frames */ +} JVMPI_CallTrace; + +extern void __collector_jprofile_enable_synctrace (void); +int __collector_jprofile_start_attach (void); +static int init_interface (CollectorInterface*); +static int open_experiment (const char *); +static int close_experiment (void); +static int detach_experiment (void); +static void jprof_find_asyncgetcalltrace (void); +static char *apistr = NULL; + +static ModuleInterface module_interface = { + "*"SP_JCLASSES_FILE, /* description, exempt from limit */ + init_interface, /* initInterface */ + open_experiment, /* openExperiment */ + NULL, /* startDataCollection */ + NULL, /* stopDataCollection */ + close_experiment, /* closeExperiment */ + detach_experiment /* detachExperiment (fork child) */ +}; + +static CollectorInterface *collector_interface = NULL; +static CollectorModule jprof_hndl = COLLECTOR_MODULE_ERR; +static int __collector_java_attach = 0; +static JavaVM *jvm; +static jmethodID getResource = NULL; +static jmethodID toExternalForm = NULL; + +/* Java profiling thread specific data */ +typedef struct TSD_Entry +{ + JNIEnv *env; + hrtime_t tstamp; +} TSD_Entry; + +static unsigned tsd_key = COLLECTOR_TSD_INVALID_KEY; +static collector_mutex_t jclasses_lock = COLLECTOR_MUTEX_INITIALIZER; +static int java_gc_on = 0; +static int java_mem_mode = 0; +static int java_sync_mode = 0; +static int is_hotspot_vm = 0; +static void get_jvm_settings (); +static void rwrite (int fd, const void *buf, size_t nbyte); +static void addToDynamicArchive (const char* name, const unsigned char* class_data, int class_data_len); +static void (*AsyncGetCallTrace)(JVMPI_CallTrace*, jint, ucontext_t*) = NULL; +static void (*collector_heap_record)(int, int, void*) = NULL; +static void (*collector_jsync_begin)() = NULL; +static void (*collector_jsync_end)(hrtime_t, void *) = NULL; + +#define gethrtime collector_interface->getHiResTime + +/* + * JVMTI declarations + */ + +static jvmtiEnv *jvmti; +static void jvmti_VMInit (jvmtiEnv*, JNIEnv*, jthread); +static void jvmti_VMDeath (jvmtiEnv*, JNIEnv*); +static void jvmti_ThreadStart (jvmtiEnv*, JNIEnv*, jthread); +static void jvmti_ThreadEnd (jvmtiEnv*, JNIEnv*, jthread); +static void jvmti_CompiledMethodLoad (jvmtiEnv*, jmethodID, jint, const void*, + jint, const jvmtiAddrLocationMap*, const void*); +static void jvmti_CompiledMethodUnload (jvmtiEnv*, jmethodID, const void*); +static void jvmti_DynamicCodeGenerated (jvmtiEnv*, const char*, const void*, jint); +static void jvmti_ClassPrepare (jvmtiEnv*, JNIEnv*, jthread, jclass); +static void jvmti_ClassLoad (jvmtiEnv*, JNIEnv*, jthread, jclass); +//static void jvmti_ClassUnload( jvmtiEnv*, JNIEnv*, jthread, jclass ); +static void jvmti_MonitorEnter (jvmtiEnv *, JNIEnv*, jthread, jobject); +static void jvmti_MonitorEntered (jvmtiEnv *, JNIEnv*, jthread, jobject); +#if 0 +static void jvmti_MonitorWait (jvmtiEnv *, JNIEnv*, jthread, jobject, jlong); +static void jvmti_MonitorWaited (jvmtiEnv *, JNIEnv*, jthread, jobject, jboolean); +#endif +static void jvmti_ClassFileLoadHook (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jclass class_being_redefined, + jobject loader, const char* name, jobject protection_domain, + jint class_data_len, const unsigned char* class_data, + jint* new_class_data_len, unsigned char** new_class_data); +static void jvmti_GarbageCollectionStart (jvmtiEnv *); +static void +jvmti_GarbageCollectionFinish (jvmtiEnv *); +jvmtiEventCallbacks callbacks = { + jvmti_VMInit, // 50 jvmtiEventVMInit; + jvmti_VMDeath, // 51 jvmtiEventVMDeath; + jvmti_ThreadStart, // 52 jvmtiEventThreadStart; + jvmti_ThreadEnd, // 53 jvmtiEventThreadEnd; + jvmti_ClassFileLoadHook, // 54 jvmtiEventClassFileLoadHook; + jvmti_ClassLoad, // 55 jvmtiEventClassLoad; + jvmti_ClassPrepare, // 56 jvmtiEventClassPrepare; + NULL, // 57 reserved57; + NULL, // 58 jvmtiEventException; + NULL, // 59 jvmtiEventExceptionCatch; + NULL, // 60 jvmtiEventSingleStep; + NULL, // 61 jvmtiEventFramePop; + NULL, // 62 jvmtiEventBreakpoint; + NULL, // 63 jvmtiEventFieldAccess; + NULL, // 64 jvmtiEventFieldModification; + NULL, // 65 jvmtiEventMethodEntry; + NULL, // 66 jvmtiEventMethodExit; + NULL, // 67 jvmtiEventNativeMethodBind; + jvmti_CompiledMethodLoad, // 68 jvmtiEventCompiledMethodLoad; + jvmti_CompiledMethodUnload, // 69 jvmtiEventCompiledMethodUnload; + jvmti_DynamicCodeGenerated, // 70 jvmtiEventDynamicCodeGenerated; + NULL, // 71 jvmtiEventDataDumpRequest; + NULL, // 72 jvmtiEventDataResetRequest; + NULL, /*jvmti_MonitorWait,*/ // 73 jvmtiEventMonitorWait; + NULL, /*jvmti_MonitorWaited,*/ // 74 jvmtiEventMonitorWaited; + jvmti_MonitorEnter, // 75 jvmtiEventMonitorContendedEnter; + jvmti_MonitorEntered, // 76 jvmtiEventMonitorContendedEntered; + NULL, // 77 jvmtiEventMonitorContendedExit; + NULL, // 78 jvmtiEventReserved; + NULL, // 79 jvmtiEventReserved; + NULL, // 80 jvmtiEventReserved; + jvmti_GarbageCollectionStart, // 81 jvmtiEventGarbageCollectionStart; + jvmti_GarbageCollectionFinish, // 82 jvmtiEventGarbageCollectionFinish; + NULL, // 83 jvmtiEventObjectFree; + NULL // 84 jvmtiEventVMObjectAlloc; +}; + +typedef jint (JNICALL JNI_GetCreatedJavaVMs_t)(JavaVM **, jsize, jsize *); + +int +init_interface (CollectorInterface *_collector_interface) +{ + collector_interface = _collector_interface; + return COL_ERROR_NONE; +} + +static int +open_experiment (const char *exp) +{ + if (collector_interface == NULL) + return COL_ERROR_JAVAINIT; + TprintfT (0, "jprofile: open_experiment %s\n", exp); + const char *params = collector_interface->getParams (); + const char *args = params; + while (args) + { + if (__collector_strStartWith (args, "j:") == 0) + { + args += 2; + break; + } + args = CALL_UTIL (strchr)(args, ';'); + if (args) + args++; + } + if (args == NULL) /* Java profiling not specified */ + return COL_ERROR_JAVAINIT; + tsd_key = collector_interface->createKey (sizeof ( TSD_Entry), NULL, NULL); + if (tsd_key == (unsigned) - 1) + { + TprintfT (0, "jprofile: TSD key create failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_JAVAINIT); + return COL_ERROR_JAVAINIT; + } + else + Tprintf (DBG_LT2, "jprofile: TSD key create succeeded %d.\n", tsd_key); + + args = params; + while (args) + { + if (__collector_strStartWith (args, "H:") == 0) + { + java_mem_mode = 1; + collector_heap_record = (void(*)(int, int, void*))dlsym (RTLD_DEFAULT, "__collector_heap_record"); + } +#if 0 + else if (__collector_strStartWith (args, "s:") == 0) + { + java_sync_mode = 1; + collector_jsync_begin = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_begin"); + collector_jsync_end = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_end"); + } +#endif + args = CALL_UTIL (strchr)(args, ';'); + if (args) + args++; + } + + /* synchronization tracing is enabled by the synctrace module, later in initialization */ + __collector_java_mode = 1; + java_gc_on = 1; + return COL_ERROR_NONE; +} + +/* routine called from the syntrace module to enable Java-API synctrace */ +void +__collector_jprofile_enable_synctrace () +{ + if (__collector_java_mode == 0) + { + TprintfT (DBG_LT1, "jprofile: not turning on Java synctrace; Java mode not enabled\n"); + return; + } + java_sync_mode = 1; + collector_jsync_begin = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_begin"); + collector_jsync_end = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_end"); + TprintfT (DBG_LT1, "jprofile: turning on Java synctrace, and requesting events\n"); +} + +int +__collector_jprofile_start_attach (void) +{ + if (!__collector_java_mode || __collector_java_asyncgetcalltrace_loaded) + return 0; + void *g_sHandle = RTLD_DEFAULT; + /* Now get the function addresses */ + JNI_GetCreatedJavaVMs_t *pfnGetCreatedJavaVMs; + pfnGetCreatedJavaVMs = (JNI_GetCreatedJavaVMs_t *) dlsym (g_sHandle, "JNI_GetCreatedJavaVMs"); + if (pfnGetCreatedJavaVMs != NULL) + { + TprintfT (0, "jprofile attach: pfnGetCreatedJavaVMs is detected.\n"); + JavaVM * vmBuf[1]; // XXXX only detect on jvm + jsize nVMs = 0; + (*pfnGetCreatedJavaVMs)(vmBuf, 1, &nVMs); + if (vmBuf[0] != NULL && nVMs > 0) + { + jvm = vmBuf[0]; + JNIEnv* jni_env = NULL; + (*jvm)->AttachCurrentThread (jvm, (void **) &jni_env, NULL); + Agent_OnLoad (jvm, NULL, NULL); + if ((*jvm)->GetEnv (jvm, (void **) &jni_env, JNI_VERSION_1_2) >= 0 && jni_env && jvmti) + { + jthread thread; + (*jvmti)->GetCurrentThread (jvmti, &thread); +#ifdef DEBUG + collector_thread_t tid; + tid = __collector_thr_self (); + TprintfT (0, "jprofile attach: AttachCurrentThread: thread: %lu jni_env=%p jthread=%p\n", + (unsigned long) tid, jni_env, thread); +#endif /* DEBUG */ + jvmti_VMInit (jvmti, jni_env, thread); + (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_COMPILED_METHOD_LOAD); + (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_DYNAMIC_CODE_GENERATED); + __collector_java_attach = 1; + (*jvm)->DetachCurrentThread (jvm); + } + } + } + return 0; +} + +static int +close_experiment (void) +{ + /* fixme XXXXX add content here */ + /* see detach_experiment() */ + __collector_java_mode = 0; + __collector_java_asyncgetcalltrace_loaded = 0; + __collector_java_attach = 0; + java_gc_on = 0; + java_mem_mode = 0; + java_sync_mode = 0; + is_hotspot_vm = 0; + __collector_mutex_init (&jclasses_lock); + tsd_key = COLLECTOR_TSD_INVALID_KEY; + TprintfT (0, "jprofile: experiment closed.\n"); + return 0; +} + +static int +detach_experiment (void) +/* fork child. Clean up state but don't write to experiment */ +{ + __collector_java_mode = 0; + java_gc_on = 0; + jvm = NULL; + java_mem_mode = 0; + java_sync_mode = 0; + is_hotspot_vm = 0; + jvmti = NULL; + apistr = NULL; + __collector_mutex_init (&jclasses_lock); + tsd_key = COLLECTOR_TSD_INVALID_KEY; + TprintfT (0, "jprofile: detached from experiment.\n"); + return 0; +} + +JNIEXPORT jint JNICALL +JVM_OnLoad (JavaVM *vm, char *options, void *reserved) +{ + jvmtiError err; + int use_jvmti = 0; + if (!__collector_java_mode) + { + TprintfT (DBG_LT1, "jprofile: JVM_OnLoad invoked with java mode disabled\n"); + return JNI_OK; + } + else + TprintfT (DBG_LT1, "jprofile: JVM_OnLoad invoked\n"); + jvm = vm; + jvmti = NULL; + if ((*jvm)->GetEnv (jvm, (void **) &jvmti, JVMTI_VERSION_1_0) >= 0 && jvmti) + { + TprintfT (DBG_LT1, "jprofile: JVMTI found\n"); + use_jvmti = 1; + } + if (!use_jvmti) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\"/>\n", + SP_JCMD_CERROR, COL_ERROR_JVMNOTSUPP); + return JNI_ERR; + } + else + { + Tprintf (DBG_LT0, "\tjprofile: Initializing for JVMTI\n"); + apistr = "JVMTI 1.0"; + + // setup JVMTI + jvmtiCapabilities cpblts; + err = (*jvmti)->GetPotentialCapabilities (jvmti, &cpblts); + if (err == JVMTI_ERROR_NONE) + { + jvmtiCapabilities cpblts_set; + CALL_UTIL (memset)(&cpblts_set, 0, sizeof (cpblts_set)); + + /* Add only those capabilities that are among potential ones */ + cpblts_set.can_get_source_file_name = cpblts.can_get_source_file_name; + Tprintf (DBG_LT1, "\tjprofile: adding can_get_source_file_name capability: %u\n", cpblts.can_get_source_file_name); + + cpblts_set.can_generate_compiled_method_load_events = cpblts.can_generate_compiled_method_load_events; + Tprintf (DBG_LT1, "\tjprofile: adding can_generate_compiled_method_load_events capability: %u\n", cpblts.can_generate_compiled_method_load_events); + + if (java_sync_mode) + { + cpblts_set.can_generate_monitor_events = cpblts.can_generate_monitor_events; + Tprintf (DBG_LT1, "\tjprofile: adding can_generate_monitor_events capability: %u\n", cpblts.can_generate_monitor_events); + } + if (java_gc_on) + { + cpblts_set.can_generate_garbage_collection_events = cpblts.can_generate_garbage_collection_events; + Tprintf (DBG_LT1, "\tjprofile: adding can_generate_garbage_collection_events capability: %u\n", cpblts.can_generate_garbage_collection_events); + } + err = (*jvmti)->AddCapabilities (jvmti, &cpblts_set); + Tprintf (DBG_LT1, "\tjprofile: AddCapabilities() returns: %d\n", err); + } + err = (*jvmti)->SetEventCallbacks (jvmti, &callbacks, sizeof ( callbacks)); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_END, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); + if (java_gc_on) + { + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_START, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL); + } + if (java_mem_mode) + { + // err = (*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, <no event for heap tracing> , NULL ); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\"/>\n", + SP_JCMD_CWARN, COL_WARN_NO_JAVA_HEAP); + java_mem_mode = 0; + } + if (java_sync_mode) + { + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL); + //err = (*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_WAIT, NULL ); + //err = (*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_WAITED, NULL ); + } + Tprintf (DBG_LT0, "\tjprofile: JVMTI initialized\n"); + } + + /* JVM still uses collector API on Solaris to notify us about dynamically generated code. + * If we ask it to generate events we'll end up with duplicate entries in the + * map file. + */ + if (use_jvmti) + { + err = (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_DYNAMIC_CODE_GENERATED); + err = (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_COMPILED_METHOD_LOAD); + } + Tprintf (DBG_LT1, "\tjprofile: JVM_OnLoad ok\n"); + return JNI_OK; +} + +/* This is currently just a placeholder */ +JNIEXPORT jint JNICALL +Agent_OnLoad (JavaVM *vm, char *options, void *reserved) +{ + return JVM_OnLoad (vm, options, reserved); +} + +static void +rwrite (int fd, const void *buf, size_t nbyte) +{ + size_t left = nbyte; + size_t res; + char *ptr = (char*) buf; + while (left > 0) + { + res = CALL_UTIL (write)(fd, ptr, left); + if (res == -1) + { + /* XXX: we can't write this record, we probably + * can't write anything else. Ignore. + */ + return; + } + left -= res; + ptr += res; + } +} + +void +get_jvm_settings () +{ + jint res; + JNIEnv *jni; + jclass jcls; + jmethodID jmid; + jstring jstrin; + jstring jstrout; + const char *str; + res = (*jvm)->GetEnv (jvm, (void **) &jni, JNI_VERSION_1_2); + if (res < 0) + return; + + /* I'm not checking if results are valid as JVM is extremely + * sensitive to exceptions that might occur during these JNI calls + * and will die with a fatal error later anyway. + */ + jcls = (*jni)->FindClass (jni, "java/lang/System"); + jmid = (*jni)->GetStaticMethodID (jni, jcls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); + jstrin = (*jni)->NewStringUTF (jni, "java.class.path"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str) + { + collector_interface->writeLog ("<setting %s=\"%s\"/>\n", SP_JCMD_SRCHPATH, str); + (*jni)->ReleaseStringUTFChars (jni, jstrout, str); + } + jstrin = (*jni)->NewStringUTF (jni, "sun.boot.class.path"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str) + { + collector_interface->writeLog ("<setting %s=\"%s\"/>\n", SP_JCMD_SRCHPATH, str); + (*jni)->ReleaseStringUTFChars (jni, jstrout, str); + } + jstrin = (*jni)->NewStringUTF (jni, "java.home"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str) + { + collector_interface->writeLog ("<setting %s=\"%s/../src.zip\"/>\n", SP_JCMD_SRCHPATH, str); + (*jni)->ReleaseStringUTFChars (jni, jstrout, str); + } + jstrin = (*jni)->NewStringUTF (jni, "java.vm.version"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str) + { + (void) collector_interface->writeLog ("<profile name=\"jprofile\" %s=\"%s\" %s=\"%s\"/>\n", + SP_JCMD_JVERSION, str, "api", apistr != NULL ? apistr : "N/A"); + if (__collector_strStartWith (str, "1.4.2_02") < 0) + { + (void) collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\"/>\n", + SP_JCMD_CWARN, COL_WARN_OLDJAVA); + } + (*jni)->ReleaseStringUTFChars (jni, jstrout, str); + } + is_hotspot_vm = 0; + jstrin = (*jni)->NewStringUTF (jni, "sun.management.compiler"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str && __collector_strncmp (str, "HotSpot", 7) == 0) + is_hotspot_vm = 1; + + /* Emulate System.setProperty( "collector.init", "true") */ + jmid = (*jni)->GetStaticMethodID (jni, jcls, "setProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + jstrin = (*jni)->NewStringUTF (jni, "collector.init"); + jstrout = (*jni)->NewStringUTF (jni, "true"); + (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin, jstrout); +} + +/* + * JVMTI code + */ + +static void +jvmti_VMInit (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) +{ + jint class_count = 0; + jclass *classes = NULL; + int i; + TprintfT (DBG_LT1, "jprofile: jvmti_VMInit called\n"); + get_jvm_settings (); + + /* determine loaded classes */ + (*jvmti_env)->GetLoadedClasses (jvmti_env, &class_count, &classes); + TprintfT (DBG_LT1, "jprofile: jvmti_VMInit initializing %d classes\n", class_count); + for (i = 0; i < class_count; i++) + { + // PushLocalFrame + jvmti_ClassPrepare (jvmti_env, jni_env, NULL, classes[i]); + // PopLocalFrame + // DeleteLocalRef( classes[i] ); + } + (*jvmti_env)->Deallocate (jvmti_env, (unsigned char*) classes); + getResource = (*jni_env)->GetMethodID (jni_env, (*jni_env)->FindClass (jni_env, "java/lang/ClassLoader"), "getResource", "(Ljava/lang/String;)Ljava/net/URL;"); + toExternalForm = (*jni_env)->GetMethodID (jni_env, (*jni_env)->FindClass (jni_env, "java/net/URL"), "toExternalForm", "()Ljava/lang/String;"); + + /* find the stack unwind routine */ + jprof_find_asyncgetcalltrace (); +} + +static void +jvmti_VMDeath (jvmtiEnv *jvmti_env, JNIEnv* jni_env) +{ + __collector_java_mode = 0; + TprintfT (DBG_LT1, "jprofile: jvmti_VMDeath event received\n"); +} + +static void +jvmti_ThreadStart (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) +{ + jvmtiError err; + jvmtiThreadInfo t_info; + char *thread_name, *group_name, *parent_name; + hrtime_t hrt; + collector_thread_t tid; + thread_name = group_name = parent_name = NULL; + hrt = gethrtime (); + tid = __collector_thr_self (); + TprintfT (DBG_LT1, "jprofile: jvmti_ThreadStart: thread: %lu jni_env=%p jthread=%p\n", + (unsigned long) tid, jni_env, thread); + err = (*jvmti_env)->GetThreadInfo (jvmti_env, thread, &t_info); + if (err == JVMTI_ERROR_NONE) + { + jvmtiThreadGroupInfo g_info; + thread_name = t_info.name; + if (t_info.thread_group) + { + err = (*jvmti_env)->GetThreadGroupInfo (jvmti_env, t_info.thread_group, &g_info); + if (err == JVMTI_ERROR_NONE) + { + group_name = g_info.name; + if (g_info.parent) + { + jvmtiThreadGroupInfo p_info; + err = (*jvmti_env)->GetThreadGroupInfo (jvmti_env, g_info.parent, &p_info); + if (err == JVMTI_ERROR_NONE) + { + parent_name = p_info.name; + // DeleteLocalRef( p_info.parent ); + } + // DeleteLocalRef( g_info.parent ); + } + } + } + // DeleteLocalRef( t_info.thread_group ); + // DeleteLocalRef( t_info.context_class_loader ); + } + if (thread_name == NULL) + thread_name = ""; + if (group_name == NULL) + group_name = ""; + if (parent_name == NULL) + parent_name = ""; + collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\" name=\"%s\" grpname=\"%s\" prntname=\"%s\" tid=\"%lu\" jthr=\"0x%lx\" jenv=\"0x%lx\"/>\n", + SP_JCMD_JTHRSTART, + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), + thread_name, + group_name, + parent_name, + (unsigned long) tid, + thread, + jni_env + ); + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd) + tsd->env = jni_env; +} + +static void +jvmti_ThreadEnd (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) +{ + hrtime_t hrt = gethrtime (); + collector_thread_t tid = __collector_thr_self (); + TprintfT (DBG_LT1, "jprofile: jvmti_ThreadEnd: thread: %lu jni_env=%p jthread=%p\n", + (unsigned long) tid, jni_env, thread); + + collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\" tid=\"%lu\" jthr=\"0x%lx\" jenv=\"0x%lx\"/>\n", + SP_JCMD_JTHREND, + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), + (unsigned long) tid, + thread, + jni_env + ); + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd) + tsd->env = NULL; +} + +/* The following definitions are borrowed from file jvmticmlr.h, part of jdk7 */ +typedef enum +{ + JVMTI_CMLR_DUMMY = 1, + JVMTI_CMLR_INLINE_INFO = 2 +} jvmtiCMLRKind; + +/* + * Record that represents arbitrary information passed through JVMTI + * CompiledMethodLoadEvent void pointer. + */ +typedef struct _jvmtiCompiledMethodLoadRecordHeader +{ + jvmtiCMLRKind kind; /* id for the kind of info passed in the record */ + jint majorinfoversion; /* major and minor info version values. Init'ed */ + jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */ + struct _jvmtiCompiledMethodLoadRecordHeader* next; +} jvmtiCompiledMethodLoadRecordHeader; + +/* + * Record that gives information about the methods on the compile-time + * stack at a specific pc address of a compiled method. Each element in + * the methods array maps to same element in the bcis array. + */ +typedef struct _PCStackInfo +{ + void* pc; /* the pc address for this compiled method */ + jint numstackframes; /* number of methods on the stack */ + jmethodID* methods; /* array of numstackframes method ids */ + jint* bcis; /* array of numstackframes bytecode indices */ +} PCStackInfo; + +/* + * Record that contains inlining information for each pc address of + * an nmethod. + */ +typedef struct _jvmtiCompiledMethodLoadInlineRecord +{ + jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */ + jint numpcs; /* number of pc descriptors in this nmethod */ + PCStackInfo* pcinfo; /* array of numpcs pc descriptors */ +} jvmtiCompiledMethodLoadInlineRecord; + +static void +jvmti_CompiledMethodLoad (jvmtiEnv *jvmti_env, jmethodID method, + jint code_size, const void *code_addr, jint map_length, + const jvmtiAddrLocationMap *map, + const void *compile_info) +{ + TprintfT (DBG_LT2, "jprofile: jvmti_CompiledMethodLoad: mid=0x%lx addr=%p sz=0x%lu map=%p info=%p\n", + (unsigned long) method, code_addr, (long) code_size, map, compile_info); + char name[32]; + CALL_UTIL (snprintf)(name, sizeof (name), "0x%lx", (unsigned long) method); + + /* Parse compile_info to get pc -> bci mapping. + * Don't interpret compile_info from JVMs other than HotSpot. + */ + int lntsize = 0; + DT_lineno *lntable = NULL; + if (compile_info != NULL && is_hotspot_vm) + { + Tprintf (DBG_LT2, "Mapping from compile_info:\n"); + jvmtiCompiledMethodLoadRecordHeader *currec = + (jvmtiCompiledMethodLoadRecordHeader*) compile_info; + while (currec != NULL) + { + if (currec->kind == JVMTI_CMLR_INLINE_INFO) + { + jvmtiCompiledMethodLoadInlineRecord *inrec = + (jvmtiCompiledMethodLoadInlineRecord*) currec; + if (inrec->numpcs <= 0) + break; + lntsize = inrec->numpcs; + lntable = (DT_lineno*) alloca (lntsize * sizeof (DT_lineno)); + PCStackInfo *pcrec = inrec->pcinfo; + DT_lineno *lnorec = lntable; + for (int i = 0; i < lntsize; ++i) + { + for (int j = pcrec->numstackframes - 1; j >= 0; --j) + if (pcrec->methods[j] == method) + { + lnorec->offset = (char*) pcrec->pc - (char*) code_addr; + lnorec->lineno = pcrec->bcis[j]; + Tprintf (DBG_LT2, " pc: 0x%lx bci: 0x%lx\n", + (long) lnorec->offset, (long) lnorec->lineno); + ++lnorec; + break; + } + ++pcrec; + } + break; + } + currec = currec->next; + } + } + else if (map != NULL) + { + Tprintf (DBG_LT2, "Mapping from jvmtiAddrLocationMap:\n"); + lntsize = map_length; + lntable = (DT_lineno*) alloca (lntsize * sizeof (DT_lineno)); + DT_lineno *lnorec = lntable; + for (int i = 0; i < map_length; ++i) + { + lnorec->offset = (char*) map[i].start_address - (char*) code_addr; + lnorec->lineno = (unsigned int) map[i].location; + Tprintf (DBG_LT2, " pc: 0x%lx bci: 0x%lx\n", + (long) lnorec->offset, (long) lnorec->lineno); + ++lnorec; + } + } + __collector_int_func_load (DFUNC_JAVA, name, NULL, (void*) code_addr, + code_size, lntsize, lntable); +} + +static void +jvmti_CompiledMethodUnload (jvmtiEnv *jvmti_env, jmethodID method, const void* code_addr) +{ + __collector_int_func_unload (DFUNC_API, (void*) code_addr); +} + +static void +jvmti_DynamicCodeGenerated (jvmtiEnv *jvmti_env, const char*name, const void *code_addr, jint code_size) +{ + __collector_int_func_load (DFUNC_API, (char*) name, NULL, (void*) code_addr, + code_size, 0, NULL); +} + +static void +addToDynamicArchive (const char* name, const unsigned char* class_data, int class_data_len) +{ + char path[MAXPATHLEN + 1]; + mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + mode_t dmode = fmode | S_IXUSR | S_IXGRP | S_IXOTH; + if (name == NULL) + name = ""; + const char *expdir = collector_interface->getExpDir (); + if (CALL_UTIL (strlen)(expdir) + + CALL_UTIL (strlen)(SP_DYNAMIC_CLASSES) + + CALL_UTIL (strlen)(name) + 8 > sizeof (path)) + return; + CALL_UTIL (snprintf)(path, sizeof (path), "%s/%s/%s.class", expdir, SP_DYNAMIC_CLASSES, name); + + /* Create all path components step by step starting with SP_DYNAMIC_CLASSES */ + char *str = path + CALL_UTIL (strlen)(expdir) + 1 + CALL_UTIL (strlen)(SP_DYNAMIC_CLASSES); + while (str) + { + *str = '\0'; + if (CALL_UTIL (mkdir)(path, dmode) != 0) + { + /* Checking for EEXIST is not enough, access() is more reliable */ + if (CALL_UTIL (access)(path, F_OK) != 0) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_MKDIR, errno, path); + return; + } + } + *str++ = '/'; + str = CALL_UTIL (strchr)(str, '/'); + } + + int fd = CALL_UTIL (open)(path, O_WRONLY | O_CREAT | O_TRUNC, fmode); + if (fd < 0) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_OVWOPEN, errno, path); + return; + } + rwrite (fd, class_data, class_data_len); + CALL_UTIL (close)(fd); +} + +static void +jvmti_ClassFileLoadHook (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jclass class_being_redefined, + jobject loader, const char* name, jobject protection_domain, jint class_data_len, + const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) +{ + jclass loaderlass; + int err; + jvmtiPhase phase_ptr; + char *cname = NULL; + (*jvmti_env)->GetPhase (jvmti_env, &phase_ptr); + + /* skip non live phases */ + if (phase_ptr != JVMTI_PHASE_LIVE) + return; + + /* skip system class loaders */ + if (!loader) + return; + loaderlass = (*jni_env)->GetObjectClass (jni_env, loader); + err = (*jvmti_env)->GetClassSignature (jvmti_env, loaderlass, &cname, NULL); + if (err != JVMTI_ERROR_NONE || !cname || *cname == (char) 0) + return; + + /* skip classes loaded with AppClassLoader (java.class.path) */ + if (__collector_strcmp (cname, "Lsun/misc/Launcher$AppClassLoader;") == 0) + return; + addToDynamicArchive (name, class_data, (int) class_data_len); +} + +#define NO_CLASS_NAME "<noname>" +#define NO_SOURCE_FILE "<Unknown>" + +static void +record_jclass (uint64_t class_id, hrtime_t hrt, const char *cname, const char *sname) +{ + size_t clen = ARCH_STRLEN (cname); + size_t slen = ARCH_STRLEN (sname); + size_t sz = sizeof (ARCH_jclass) + clen + slen; + ARCH_jclass *jcls = (ARCH_jclass*) alloca (sz); + jcls->comm.tsize = sz; + jcls->comm.type = ARCH_JCLASS; + jcls->class_id = class_id; + jcls->tstamp = hrt; + char *str = (char*) (jcls + 1); + size_t i = CALL_UTIL (strlcpy)(str, cname, clen); + str += i; + while (i++ < clen) + *str++ = (char) 0; /* pad with 0's */ + i = CALL_UTIL (strlcpy)(str, sname, slen); + str += i; + while (i++ < slen) + *str++ = (char) 0; /* pad with 0's */ + collector_interface->writeDataPacket (jprof_hndl, (CM_Packet*) jcls); +} + +static void +record_jmethod (uint64_t class_id, uint64_t method_id, + const char *mname, const char *msign) +{ + size_t mnlen = mname ? ARCH_STRLEN (mname) : 0; + size_t mslen = msign ? ARCH_STRLEN (msign) : 0; + size_t sz = sizeof (ARCH_jmethod) + mnlen + mslen; + ARCH_jmethod *jmth = (ARCH_jmethod*) alloca (sz); + if (jmth == NULL) + { + TprintfT (DBG_LT1, "jprofile: record_jmethod ERROR: failed to alloca(%ld)\n", (long) sz); + return; + } + jmth->comm.tsize = sz; + jmth->comm.type = ARCH_JMETHOD; + jmth->class_id = class_id; + jmth->method_id = method_id; + char *str = (char*) (jmth + 1); + if (mname) + { + size_t i = CALL_UTIL (strlcpy)(str, mname, mnlen); + str += i; + while (i++ < mnlen) + *str++ = (char) 0; /* pad with 0's */ + } + if (msign) + { + size_t i = CALL_UTIL (strlcpy)(str, msign, mslen); + str += i; + while (i++ < mslen) + *str++ = (char) 0; /* pad with 0's */ + } + collector_interface->writeDataPacket (jprof_hndl, (CM_Packet*) jmth); +} + +static void +jvmti_ClassPrepare (jvmtiEnv *jvmti_env, JNIEnv* jni_env, + jthread thread, jclass klass) +{ + hrtime_t hrt; + jint mnum; + jmethodID *mptr; + char *cname, *sname; + char *str1 = NULL; + int err = (*jvmti_env)->GetClassSignature (jvmti_env, klass, &str1, NULL); + if (err != JVMTI_ERROR_NONE || str1 == NULL || *str1 == (char) 0) + cname = NO_CLASS_NAME; + else + cname = str1; + if (*cname != 'L') + { + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassPrepare: GetClassSignature failed. err=%d cname=%s\n", err, cname); + return; + } + char *str2 = NULL; + err = (*jvmti_env)->GetSourceFileName (jvmti_env, klass, &str2); + if (err != JVMTI_ERROR_NONE || str2 == NULL || *str2 == (char) 0) + sname = NO_SOURCE_FILE; + else + sname = str2; + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassPrepare: cname=%s sname=%s\n", STR (cname), STR (sname)); + + /* Lock the whole file */ + __collector_mutex_lock (&jclasses_lock); + hrt = gethrtime (); + record_jclass ((unsigned long) klass, hrt, cname, sname); + (*jvmti_env)->Deallocate (jvmti_env, (unsigned char *) str1); + (*jvmti_env)->Deallocate (jvmti_env, (unsigned char *) str2); + err = (*jvmti_env)->GetClassMethods (jvmti_env, klass, &mnum, &mptr); + if (err == JVMTI_ERROR_NONE) + { + for (int i = 0; i < mnum; i++) + { + char *mname, *msign; + err = (*jvmti_env)->GetMethodName (jvmti_env, mptr[i], &mname, &msign, NULL); + if (err != JVMTI_ERROR_NONE) + continue; + record_jmethod ((unsigned long) klass, (unsigned long) mptr[i], mname, msign); + // DeleteLocalRef( mptr[i] ); + } + (*jvmti_env)->Deallocate (jvmti_env, (unsigned char*) mptr); + } + /* Unlock the file */ + __collector_mutex_unlock (&jclasses_lock); +} + +/* + * The CLASS_LOAD event is enabled to enable AsyncGetCallTrace + */ +static void +jvmti_ClassLoad (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jclass klass) +{ + char *cname; + char *str1 = NULL; + int err = (*jvmti_env)->GetClassSignature (jvmti_env, klass, &str1, NULL); + if (err != JVMTI_ERROR_NONE || str1 == NULL || *str1 == (char) 0) + cname = NO_CLASS_NAME; + else + cname = str1; + jstring str = NULL; + const char* resourceName; + jobject classLoader = NULL; + err = (*jvmti)->GetClassLoader (jvmti, klass, &classLoader); + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jprofile: jvmti_ClassLoad err=%d cname=%s\n", err, STR (cname)); + if (err == 0) + { + if (classLoader == NULL) + { + // bootstrap class loader + resourceName = ""; + } + else + { + char* name = (char *) alloca ((CALL_UTIL (strlen)(str1) + 32) * sizeof (char)); + CALL_UTIL (strlcpy)(name, str1 + 1, CALL_UTIL (strlen)(str1)); + name[CALL_UTIL (strlen)(name) - 1] = '\0'; // remove the last ';' + char* p; + for (p = name; *p != '\0'; p++) + if (*p == '.') + *p = '/'; + CALL_UTIL (strlcat)(name, ".class", CALL_UTIL (strlen)(name) + CALL_UTIL (strlen)(".class") + 1); + if (getResource == NULL || toExternalForm == NULL) + { + resourceName = ""; + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassLoad: class %s failed to get path with method missing\n", STR (cname)); + } + else + { + jobject url = (*jni_env)->CallObjectMethod (jni_env, classLoader, getResource, (*jni_env)->NewStringUTF (jni_env, name)); + if (url == NULL) + { + resourceName = ""; + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassLoad: class %s failed to get path\n", STR (cname)); + } + else + { + str = (jstring) (*jni_env)->CallObjectMethod (jni_env, url, toExternalForm); + resourceName = (*jni_env)->GetStringUTFChars (jni_env, str, NULL); + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassLoad: ARCH_JCLASS_LOCATION(Ox%x) class_id=0x%lx className='%s' fileName '%s'\n", + (int) ARCH_JCLASS_LOCATION, (unsigned long) klass, STR (cname), STR (resourceName)); + size_t clen = ARCH_STRLEN (cname); + size_t slen = ARCH_STRLEN (resourceName); + size_t sz = sizeof (ARCH_jclass) + clen + slen; + ARCH_jclass_location *jcls = (ARCH_jclass_location*) alloca (sz); + jcls->comm.tsize = sz; + jcls->comm.type = ARCH_JCLASS_LOCATION; + jcls->class_id = (unsigned long) klass; + char *str = (char*) (jcls + 1); + size_t i = CALL_UTIL (strlcpy)(str, cname, clen); + str += i; + while (i++ < clen) + { + *str++ = (char) 0; /* pad with 0's */ + } + i = CALL_UTIL (strlcpy)(str, resourceName, slen); + str += i; + while (i++ < slen) + { + *str++ = (char) 0; /* pad with 0's */ + } + /* Lock the whole file */ + __collector_mutex_lock (&jclasses_lock); + collector_interface->writeDataPacket (jprof_hndl, (CM_Packet*) jcls); + /* Unlock the file */ + __collector_mutex_unlock (&jclasses_lock); + } + } + } + } +} + +static void +jvmti_MonitorEnter (jvmtiEnv *jvmti_env, JNIEnv* jni_env, + jthread thread, jobject object) +{ + if (collector_jsync_begin) + collector_jsync_begin (); + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + return; + tsd->tstamp = gethrtime (); +} + +static void +jvmti_MonitorEntered (jvmtiEnv *jvmti_env, JNIEnv* jni_env, + jthread thread, jobject object) +{ + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + return; + if (collector_jsync_end) + collector_jsync_end (tsd->tstamp, object); +} + +static void +jvmti_GarbageCollectionStart (jvmtiEnv *jvmti_env) +{ + hrtime_t hrt = gethrtime (); + collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\"/>\n", + SP_JCMD_GCSTART, + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC) + ); + TprintfT (DBG_LT1, "jprofile: jvmti_GarbageCollectionStart.\n"); +} + +static void +jvmti_GarbageCollectionFinish (jvmtiEnv *jvmti_env) +{ + hrtime_t hrt = gethrtime (); + collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\"/>\n", + SP_JCMD_GCEND, + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC) + ); + TprintfT (DBG_LT1, "jprofile: jvmti_GarbageCollectionFinish.\n"); +} + +#if 0 +static void +jvmti_MonitorWait (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, + jobject object, jlong timed_out) +{ + if (collector_sync_begin) + collector_sync_begin (); + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + return; + tsd->tstamp = gethrtime (); +} + +static void +jvmti_MonitorWaited (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, + jobject object, jboolean timed_out) +{ + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + return; + if (collector_sync_end) + collector_sync_end (tsd->tstamp, object); +} +#endif + +static void +jprof_find_asyncgetcalltrace () +{ + void *jvmhandle; + if (__collector_VM_ReadByteInstruction == NULL) + __collector_VM_ReadByteInstruction = (int(*)()) dlsym (RTLD_DEFAULT, "Async_VM_ReadByteInstruction"); + + /* look for stack unwind function using default path */ + AsyncGetCallTrace = (void (*)(JVMPI_CallTrace*, jint, ucontext_t*)) + dlsym (RTLD_DEFAULT, "AsyncGetCallTrace"); + if (AsyncGetCallTrace != NULL) + { + __collector_java_asyncgetcalltrace_loaded = 1; + TprintfT (DBG_LT1, "jprofile: AsyncGetCallTrace found with RTLD_DEFAULT\n"); + } + else + { + /* not found there, find libjvm.so, and ask again */ + jvmhandle = dlopen ("libjvm.so", RTLD_LAZY | RTLD_NOLOAD); + if (jvmhandle != NULL) + { + AsyncGetCallTrace = (void (*)(JVMPI_CallTrace*, jint, ucontext_t*)) + dlsym (jvmhandle, "AsyncGetCallTrace"); + } + } + + if (AsyncGetCallTrace == NULL) + { + /* we could not find it -- write collector error */ + TprintfT (0, "jprofile: ERROR -- AsyncGetCallTrace not found in address space\n"); + char *err = dlerror (); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_JVMNOJSTACK, err ? err : ""); + __collector_java_mode = 0; + } + else + { + __collector_java_asyncgetcalltrace_loaded = 1; + TprintfT (DBG_LT1, "jprofile: AsyncGetCallTrace initialized in jprof_jvmpi_init_done_event\n"); + } +} + +int +__collector_ext_jstack_unwind (char *ptr, int sz, ucontext_t *uc) +{ + if (AsyncGetCallTrace == NULL) + { + TprintfT (DBG_LT0, "jprofile: __collector_ext_jstack_unwind: AsyncGetCallTrace is NULL\n"); + return 0; + } + + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + { + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: tsd is NULL\n"); + return 0; + } + if (__collector_java_attach && tsd->env == NULL && jvmti != NULL && jvm != NULL) + { + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: tsd->env is NULL under attach\n"); + JNIEnv* jni_env = NULL; + (*jvm)->GetEnv (jvm, (void **) &jni_env, JNI_VERSION_1_2); + tsd->env = jni_env; + } + if (tsd->env == NULL) + { + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: tsd->env is NULL\n"); + return 0; + } + + /* skip the Java stack whenever another signal handler is present */ + if (uc->uc_link) + { + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: uc->uc_link is non-NULL\n"); + return 0; + } + /* we don't expect Java frames in signal handlers, so + * unroll the list of saved contexts to the topmost one + */ + while (uc->uc_link) + uc = uc->uc_link; + Java_info *jinfo = (Java_info*) ptr; + jinfo->kind = JAVA_INFO; + jinfo->hsize = sizeof (Java_info); + ptr += sizeof (Java_info); + sz -= sizeof (Java_info); + + JVMPI_CallTrace jtrace; + jtrace.env_id = tsd->env; + jtrace.frames = (JVMPI_CallFrame*) ptr; + + /* nframes is how many frames we have room for */ + jint nframes = sz / sizeof (JVMPI_CallFrame); + +#if WSIZE(64) + /* bug 6909545: garbage in 64-bit JAVA_INFO */ + CALL_UTIL (memset)(jtrace.frames, 0, nframes * sizeof (JVMPI_CallFrame)); +#endif + +#if ARCH(SPARC) + // 21328946 JDK bug 8129933 causes <no java callstack recorded> on sparc-Linux + // convert from ucontext_t to sigcontext + struct sigcontext sctx; + sctx.sigc_regs.tpc = uc->uc_mcontext.mc_gregs[MC_PC]; + __collector_memcpy (sctx.sigc_regs.u_regs, &uc->uc_mcontext.mc_gregs[3], sizeof (sctx.sigc_regs.u_regs)); + uc = (ucontext_t *) (&sctx); +#endif /* SPARC */ + AsyncGetCallTrace (&jtrace, nframes, uc); + + if (jtrace.num_frames == nframes) + { + JVMPI_CallFrame *last = &jtrace.frames[nframes - 1]; + last->method_id = (jmethodID) SP_TRUNC_STACK_MARKER; + last->lineno = 0; + } + + /* nframes is how many frames we actually got */ + nframes = jtrace.num_frames; + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: AsyncGetCallTrace jtrace.numframes = %d\n", nframes); + if (nframes <= 0) + { + /* negative values are error codes */ + TprintfT (0, "jprofile: __collector_ext_jstack_unwind: AsyncGetCallTrace returned error: jtrace.numframes = %d\n", nframes); + nframes = 1; + JVMPI_CallFrame *err = (JVMPI_CallFrame*) ptr; + err->lineno = jtrace.num_frames; // bci = error code + err->method_id = 0; // artificial method id + } + jinfo->hsize += nframes * sizeof (JVMPI_CallFrame); + return jinfo->hsize; +} + +/* + * Collector Java API implementation + */ +void +Java_com_sun_forte_st_collector_CollectorAPI__1sample(JNIEnv *jEnv, jclass jCls, jstring jName) +{ + JNIEnv *jni; + jint res = (*jvm)->GetEnv (jvm, (void **) &jni, JNI_VERSION_1_2); + if (res < 0) + return; + const char *name = jName ? (*jni)->GetStringUTFChars (jni, jName, NULL) : NULL; + __collector_sample ((char*) name); +} + +void +Java_com_sun_forte_st_collector_CollectorAPI__1pause(JNIEnv *jEnv, jclass jCls) +{ + __collector_pause_m ("JAPI"); +} + +void +Java_com_sun_forte_st_collector_CollectorAPI__1resume(JNIEnv *jEnv, jclass jCls) +{ + __collector_resume (); +} + +void +Java_com_sun_forte_st_collector_CollectorAPI__1terminate(JNIEnv *jEnv, jclass jCls) +{ + __collector_terminate_expt (); +} +#endif /* GPROFNG_JAVA_PROFILING */ + +static void init_module () __attribute__ ((constructor)); +static void +init_module () +{ +#if defined(GPROFNG_JAVA_PROFILING) + __collector_dlsym_guard = 1; + RegModuleFunc reg_module = (RegModuleFunc) dlsym (RTLD_DEFAULT, "__collector_register_module"); + __collector_dlsym_guard = 0; + if (reg_module) + { + jprof_hndl = reg_module (&module_interface); + TprintfT (0, "jprofile: init_module.\n"); + } +#endif /* GPROFNG_JAVA_PROFILING */ +} + +int __collector_java_mode = 0; +int __collector_java_asyncgetcalltrace_loaded = 0; diff --git a/gprofng/libcollector/libcol-i386-dis.c b/gprofng/libcollector/libcol-i386-dis.c new file mode 100644 index 0000000..9b3882a --- /dev/null +++ b/gprofng/libcollector/libcol-i386-dis.c @@ -0,0 +1,28 @@ +/* Copyright (C) 2022 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#if defined(__i386__) || defined(__x86_64) +#include "opcodes/i386-dis.c" + +#undef _ +#undef M +#include "libiberty/safe-ctype.c" +#endif + diff --git a/gprofng/libcollector/libcol_hwcdrv.c b/gprofng/libcollector/libcol_hwcdrv.c new file mode 100644 index 0000000..f3bd932 --- /dev/null +++ b/gprofng/libcollector/libcol_hwcdrv.c @@ -0,0 +1,25 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "collector.h" /* Tprintf*() */ + +#define LIBCOLLECTOR_SRC +#include "hwcdrv.c" diff --git a/gprofng/libcollector/libcol_hwcfuncs.c b/gprofng/libcollector/libcol_hwcfuncs.c new file mode 100644 index 0000000..1f5ad68 --- /dev/null +++ b/gprofng/libcollector/libcol_hwcfuncs.c @@ -0,0 +1,27 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "collector.h" /* Tprintf*() */ + +#define LIBCOLLECTOR_SRC +#include "hwcfuncs.c" + + diff --git a/gprofng/libcollector/libcol_util.c b/gprofng/libcollector/libcol_util.c new file mode 100644 index 0000000..c709b3c --- /dev/null +++ b/gprofng/libcollector/libcol_util.c @@ -0,0 +1,1693 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <dlfcn.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/syscall.h> +#include <sys/mman.h> +#include <sys/ioctl.h> + +#include "gp-defs.h" +#include "collector.h" +#include "libcol_util.h" +#include "gp-experiment.h" +#include "Emsgnum.h" +#include "memmgr.h" // __collector_allocCSize, __collector_freeCSize +#include "tsd.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +/* + * This file is intended for collector's own implementation of + * various routines to avoid interaction with libc and other + * libraries. + */ + +/* ------- libc interface ----------------- */ +CollectorUtilFuncs __collector_util_funcs = {NULL}; +int __collector_dlsym_guard = 0; +int(*__collector_sscanfp)(const char *restrict s, const char *restrict fmt, ...); + +/* + * We have calls on Solaris to get the thread ID. + * On Linux, there is a gettid() system call. + * From user space, we have to use syscall(__NR_gettid). + * The call is probably fast (with the tid in vdso), but dbx intercepts the syscall. + * 7182047 syscall() has large overhead under dbx on linux + * One option is to use an assembly call to get the tid. + * We know how to do this on x86, but not on SPARC. + * So another option is to do the syscall once and cache the result in thread-local storage. + * This solves the SPARC case. + * On x86 we could use one or both strategies. So there are opportunities here to simplify the code. + */ +static unsigned gettid_key = COLLECTOR_TSD_INVALID_KEY; + +void +__collector_ext_gettid_tsd_create_key () +{ + gettid_key = __collector_tsd_create_key (sizeof (pid_t), NULL, NULL); +} + +pid_t +__collector_gettid () +{ + pid_t *tid_ptr = (pid_t *) __collector_tsd_get_by_key (gettid_key); + // check if we have a thread-specific tid and if it's been initialized + // (it's 0 before initialization and cannot be 0 after since pid 0 is the boot process) + if (tid_ptr && *tid_ptr > 0) + return *tid_ptr; + pid_t r; + +#if ARCH(Intel) +#if WSIZE(32) +#define syscall_instr "int $0x80" +#define syscall_clobber "memory" +#else //WSIZE(64) +#define syscall_instr "syscall" +#define syscall_clobber "rcx", "r11", "memory" +#endif + __asm__ __volatile__(syscall_instr + : "=a" (r) : "0" (__NR_gettid) + : syscall_clobber); +#else + r = syscall (__NR_gettid); +#endif + if (tid_ptr) + *tid_ptr = r; + return r; +} + +static inline int +atomic_swap (volatile int * p, int v) +{ +#if ARCH(Intel) + int r; + __asm__ __volatile__("xchg %1, %2" : "=r" (r) : "m" (*p), "0" (v)); + return r; +#else + /* Since the inline templates perfan/libcollector/src/inline.*.il all + * have implementations for __collector_cas_32(), how about we just + * use that interface for Intel as well and drop the "#if ARCH()" stuff here? + * + * As it is, we're using an atomic swap on Intel and + * compare-and-swap on SPARC. The semantics are different + * (cas requires an expected "compare" value and swaps ONLY + * if we match that value). Nevertheless, the results of the + * two operations + * Intel: atomic_swap(&lock, 1) + * SPARC: cas(&lock,0,1) + * happen to be the same for the two cases we're interested in: + * if lock==0 lock=1 return 0 + * if lock==1 lock=1 return 1 + * You CANNOT always simply substitute cas for swap. + */ + return __collector_cas_32 ((volatile uint32_t *)p, 0, v); +#endif +} + +int +__collector_mutex_lock (collector_mutex_t *lock_var) +{ + volatile unsigned int i; /* xxxx volatile may not be honored on amd64 -x04 */ + + if (!(*lock_var) && !atomic_swap (lock_var, 1)) + return 0; + + do + { + while ((collector_mutex_t) (*lock_var) == 1) + i++; + } + while (atomic_swap (lock_var, 1)); + return 0; +} + +int +__collector_mutex_trylock (collector_mutex_t *lock_var) +{ + if (!(*lock_var) && !atomic_swap (lock_var, 1)) + return 0; + return EBUSY; +} + +int +__collector_mutex_unlock (collector_mutex_t *lock_var) +{ + (*lock_var) = 0; + return 0; +} + +#if ARCH(SPARC) +void +__collector_inc_32 (volatile uint32_t *mem) +{ + uint32_t t1, t2; + __asm__ __volatile__(" ld %2,%0 \n" + "1: add %0,1,%1 \n" + " cas %2,%0,%1 \n" + " cmp %0,%1 \n" + " bne,a 1b \n" + " mov %1,%0 \n" + : "=&r" (t1), "=&r" (t2) + : "m" (*mem) + : "cc" + ); +} + +void +__collector_dec_32 (volatile uint32_t *mem) +{ + uint32_t t1, t2; + __asm__ __volatile__(" ld %2,%0 \n" + "1: sub %0,1,%1 \n" + " cas %2,%0,%1 \n" + " cmp %0,%1 \n" + " bne,a 1b \n" + " mov %1,%0 \n" + : "=&r" (t1), "=&r" (t2) + : "m" (*mem) + : "cc" + ); +} + +uint32_t +__collector_cas_32 (volatile uint32_t *mem, uint32_t old, uint32_t new) +{ + __asm__ __volatile__("cas [%1],%2,%0" + : "+r" (new) + : "r" (mem), "r" (old)); + return new; +} + +uint32_t +__collector_subget_32 (volatile uint32_t *mem, uint32_t val) +{ + uint32_t t1, t2; + __asm__ __volatile__(" ld %2,%0 \n" + "1: sub %0,%3,%1 \n" + " cas %2,%0,%1 \n" + " cmp %0,%1 \n" + " bne,a 1b \n" + " mov %1,%0 \n" + " sub %0,%3,%1 \n" + : "=&r" (t1), "=&r" (t2) + : "m" (*mem), "r" (val) + : "cc" + ); + return t2; +} + +#if WSIZE(32) + +void * +__collector_cas_ptr (volatile void *mem, void *old, void *new) +{ + __asm__ __volatile__("cas [%1],%2,%0" + : "+r" (new) + : "r" (mem), "r" (old)); + return new; +} + +uint64_t +__collector_cas_64p (volatile uint64_t *mem, uint64_t *old, uint64_t *new) +{ + uint64_t t; + __asm__ __volatile__(" ldx [%2],%2 \n" + " ldx [%3],%3 \n" + " casx [%1],%2,%3 \n" + " stx %3,%0 \n" + : "=m" (t) + : "r" (mem), "r" (old), "r" (new) + ); + return t; +} + +#elif WSIZE(64) + +void * +__collector_cas_ptr (volatile void *mem, void *old, void *new) +{ + __asm__ __volatile__("casx [%1],%2,%0" + : "+r" (new) + : "r" (mem), "r" (old)); + return new; +} + +uint64_t +__collector_cas_64p (volatile uint64_t *mem, uint64_t *old, uint64_t *new) +{ + uint64_t t; + __asm__ __volatile__(" ldx [%2],%2 \n" + " ldx [%3],%3 \n" + " casx [%1],%2,%3 \n" + " mov %3,%0 \n" + : "=&r" (t) + : "r" (mem), "r" (old), "r" (new) + ); + return t; +} + +#endif /* WSIZE() */ +#endif /* ARCH() */ + +void * +__collector_memcpy (void *s1, const void *s2, size_t n) +{ + char *cp1 = (char*) s1; + char *cp2 = (char*) s2; + while (n--) + *cp1++ = *cp2++; + return s1; +} + +static void * +collector_memset (void *s, int c, size_t n) +{ + unsigned char *s1 = s; + while (n--) + *s1++ = (unsigned char) c; + return s; +} + +int +__collector_strcmp (const char *s1, const char *s2) +{ + for (;;) + { + if (*s1 != *s2) + return *s1 - *s2; + if (*s1 == 0) + return 0; + s1++; + s2++; + } +} + +int +__collector_strncmp (const char *s1, const char *s2, size_t n) +{ + while (n > 0) + { + if (*s1 != *s2) + return *s1 - *s2; + if (*s1 == 0) + return 0; + s1++; + s2++; + n--; + } + return 0; +} + +char * +__collector_strstr (const char *s1, const char *s2) +{ + if (s2 == NULL || *s2 == 0) + return NULL; + size_t len = __collector_strlen (s2); + for (char c = *s2; *s1; s1++) + if (c == *s1 && __collector_strncmp (s1, s2, len) == 0) + return (char *) s1; + return NULL; +} + +char * +__collector_strchr (const char *str, int chr) +{ + if (chr == '\0') + return (char *) (str + __collector_strlen (str)); + for (; *str; str++) + if (chr == (int) *str) + return (char *) str; + return NULL; +} + +char * +__collector_strrchr (const char *str, int chr) +{ + const char *p = str + __collector_strlen (str); + for (; p - str >= 0; p--) + if (chr == *p) + return (char *) p; + return NULL; +} + +int +__collector_strStartWith (const char *s1, const char *s2) +{ + size_t slen = __collector_strlen (s2); + return __collector_strncmp (s1, s2, slen); +} + +size_t +__collector_strlen (const char *s) +{ + int len = -1; + while (s[++len] != '\0') + ; + return len; +} + +size_t +__collector_strlcpy (char *dst, const char *src, size_t dstsize) +{ + size_t srcsize = 0; + size_t n = dstsize - 1; + char c; + while ((c = *src++) != 0) + if (srcsize++ < n) + *dst++ = c; + if (dstsize > 0) + *dst = '\0'; + return srcsize; +} + +size_t +__collector_strncpy (char *dst, const char *src, size_t dstsize) +{ + size_t i; + for (i = 0; i < dstsize; i++) + { + dst[i] = src[i]; + if (src[i] == '\0') + break; + } + return i; +} + +char * +__collector_strcat (char *dst, const char *src) +{ + size_t sz = __collector_strlen (dst); + for (size_t i = 0;; i++) + { + dst[sz + i] = src[i]; + if (src[i] == '\0') + break; + } + return dst; +} + +size_t +__collector_strlcat (char *dst, const char *src, size_t dstsize) +{ + size_t sz = __collector_strlen (dst); + return sz + __collector_strlcpy (dst + sz, src, dstsize - sz); +} + +void * +__collector_malloc (size_t size) +{ + void * ptr = __collector_allocCSize (__collector_heap, size, 0); + return ptr; +} + +void * +__collector_calloc (size_t nelem, size_t elsize) +{ + size_t n = nelem * elsize; + void * ptr = __collector_malloc (n); + if (NULL == ptr) + return NULL; + collector_memset (ptr, 0, n); + return ptr; +} + +char * +__collector_strdup (const char * str) +{ + if (NULL == str) + return NULL; + size_t size = __collector_strlen (str); + char * dst = (char *) __collector_malloc (size + 1); + if (NULL == dst) + return NULL; + __collector_strncpy (dst, str, size + 1); + return dst; +} + +#define C_FMT 1 +#define C_STR 2 +static char +Printable[256] = {//characters should be escaped by xml: "'<>& + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, /* ................ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ................ */ + 3, 3, 1, 3, 3, 3, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, /* !"#$%&'()*+,-./ */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 1, 3, /* 0123456789:;<=>? */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* @ABCDEFGHIJKLMNO */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* PQRSTUVWXYZ[\]^_ */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* `abcdefghijklmno */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, /* pqrstuvwxyz{|}~. */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ................ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ................ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ................ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ................ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ................ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ................ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ................ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* ................ */ +}; +static char hex[17] = "0123456789abcdef"; +static char HEX[17] = "0123456789ABCDEF"; + +int +__collector_xml_snprintf (char *s, size_t n, const char *format, ...) +{ + va_list args; + va_start (args, format); + int res = __collector_xml_vsnprintf (s, n, format, args); + va_end (args); + return res; +} + +int +__collector_xml_vsnprintf (char *s, size_t n, const char *format, va_list args) +{ + const char *src = format; + char *dst = s; + int cnt = 0; + unsigned char c; + while ((c = *src) != 0) + { + if (c == '%') + { + char numbuf[32]; + int done = 0; + int jflag = 0; + int lflag = 0; + int zflag = 0; + int width = 0; + src++; + while (!done) + { + c = *src; + switch (c) + { + case '%': + { + if (cnt++ < n - 1) + *dst++ = '%'; + if (cnt++ < n - 1) + *dst++ = hex[c / 16]; + if (cnt++ < n - 1) + *dst++ = hex[c % 16]; + if (cnt++ < n - 1) + *dst++ = '%'; + src++; + done = 1; + break; + } + case '-': + { + if (jflag != 0) + done = 1; + else + { + jflag = 1; + src++; + } + break; + } + case 'l': + { + if (lflag != 0) + done = 1; + else + { + lflag = 1; + c = *++src; + if (c == 'l') + { + lflag++; + src++; + } + } + break; + } + case 'c': + { + unsigned char c1 = (unsigned char) va_arg (args, int); + if ((Printable[(int) c1] & C_STR) == 0) + { + if (c1 == '"') + {//" + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'q'; + if (cnt++ < n - 1) + *dst++ = 'u'; + if (cnt++ < n - 1) + *dst++ = 'o'; + if (cnt++ < n - 1) + *dst++ = 't'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else if (c1 == '\'') + {//' + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'a'; + if (cnt++ < n - 1) + *dst++ = 'p'; + if (cnt++ < n - 1) + *dst++ = 'o'; + if (cnt++ < n - 1) + *dst++ = 's'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else if (c1 == '&') + {//& + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'a'; + if (cnt++ < n - 1) + *dst++ = 'm'; + if (cnt++ < n - 1) + *dst++ = 'p'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else if (c1 == '<') + {//< + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'l'; + if (cnt++ < n - 1) + *dst++ = 't'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else if (c1 == '>') + {//> + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'g'; + if (cnt++ < n - 1) + *dst++ = 't'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else + { + if (cnt++ < n - 1) + *dst++ = '%'; + if (cnt++ < n - 1) + *dst++ = hex[c1 / 16]; + if (cnt++ < n - 1) + *dst++ = hex[c1 % 16]; + if (cnt++ < n - 1) + *dst++ = '%'; + } + } + else if (cnt++ < n - 1) + *dst++ = c1; + src++; + done = 1; + break; + } + case 's': + { + /* Strings are always left justified */ + char *str = va_arg (args, char*); + if (!str) + str = "<NULL>"; + unsigned char c1; + while ((c1 = *str++) != 0) + { + if ((Printable[(int) c1] & C_STR) == 0) + { + if (c1 == '"') + {//" + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'q'; + if (cnt++ < n - 1) + *dst++ = 'u'; + if (cnt++ < n - 1) + *dst++ = 'o'; + if (cnt++ < n - 1) + *dst++ = 't'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else if (c1 == '\'') + {//' + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'a'; + if (cnt++ < n - 1) + *dst++ = 'p'; + if (cnt++ < n - 1) + *dst++ = 'o'; + if (cnt++ < n - 1) + *dst++ = 's'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else if (c1 == '&') + {//& + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'a'; + if (cnt++ < n - 1) + *dst++ = 'm'; + if (cnt++ < n - 1) + *dst++ = 'p'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else if (c1 == '<') + {//< + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'l'; + if (cnt++ < n - 1) + *dst++ = 't'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else if (c1 == '>') + {//> + if (cnt++ < n - 1) + *dst++ = '&'; + if (cnt++ < n - 1) + *dst++ = 'g'; + if (cnt++ < n - 1) + *dst++ = 't'; + if (cnt++ < n - 1) + *dst++ = ';'; + } + else + { + if (cnt++ < n - 1) + *dst++ = '%'; + if (cnt++ < n - 1) + *dst++ = hex[c1 / 16]; + if (cnt++ < n - 1) + *dst++ = hex[c1 % 16]; + if (cnt++ < n - 1) + *dst++ = '%'; + } + } + else if (cnt++ < n - 1) + *dst++ = c1; + width--; + } + while (width > 0) + { + if (cnt++ < n - 1) + *dst++ = ' '; + width--; + } + src++; + done = 1; + break; + } + case 'i': + case 'd': + case 'o': + case 'p': + case 'u': + case 'x': + case 'X': + { + int base = 10; + int uflag = 0; + int sflag = 0; + if (c == 'o') + { + uflag = 1; + base = 8; + } + else if (c == 'u') + uflag = 1; + else if (c == 'p') + { + lflag = 1; + uflag = 1; + base = 16; + } + else if (c == 'x' || c == 'X') + { + uflag = 1; + base = 16; + } + long long argll = 0LL; + if (lflag == 0) + { + if (uflag) + argll = va_arg (args, unsigned int); + else + argll = va_arg (args, int); + } + else if (lflag == 1) + { + if (uflag) + argll = va_arg (args, unsigned long); + else + argll = va_arg (args, long); + } + else if (lflag == 2) + argll = va_arg (args, long long); + unsigned long long argllu = 0ULL; + if (uflag || argll >= 0) + argllu = argll; + else + { + sflag = 1; + argllu = -argll; + } + int idx = sizeof (numbuf); + do + { + numbuf[--idx] = (c == 'X' ? HEX[argllu % base] : hex[argllu % base]); + argllu = argllu / base; + } + while (argllu != 0) + ; + if (sflag) + { + if (jflag || zflag) + { + if (cnt++ < n - 1) + *dst++ = '-'; + } + else + numbuf[--idx] = '-'; + } + + if (jflag) + { + while (idx < sizeof (numbuf) && width > 0) + { + if (cnt++ < n - 1) + *dst++ = numbuf[idx]; + idx++; + width--; + } + zflag = 0; + } + + while (width > sizeof (numbuf) - idx) + { + if (cnt++ < n - 1) + *dst++ = zflag ? '0' : ' '; + width--; + } + while (idx != sizeof (numbuf)) + { + if (cnt++ < n - 1) + *dst++ = numbuf[idx]; + idx++; + } + src++; + done = 1; + break; + } + case '0': + zflag = 1; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + { + while (c >= '0' && c <= '9') + { + width = width * 10 + (c - '0'); + c = *++src; + } + break; + } + default: + done = 1; + break; + } + } + } + else if ((Printable[(int) c] & C_FMT) == 0) + { + if (cnt++ < n - 1) + *dst++ = '%'; + if (cnt++ < n - 1) + *dst++ = hex[c / 16]; + if (cnt++ < n - 1) + *dst++ = hex[c % 16]; + if (cnt++ < n - 1) + *dst++ = '%'; + src++; + } + else + { + if (cnt++ < n - 1) + *dst++ = c; + src++; + } + } + + if (cnt < n - 1) + s[cnt] = '\0'; + else + s[n - 1] = '\0'; + + return cnt; +} + +/* + * Functions to be called directly from libc.so + */ +#if ARCH(Intel) /* intel-Linux */ +/* + * The CPUIDinfo/__collector_cpuid() code is old, + * incorrect, and complicated. It returns the apicid + * rather than the processor number. + * + * Unfortunately, the higher-level sched_getcpu() function, + * which we use on SPARC-Linux, is not available on Oracle + * Linux 5. So we have to test for its existence. + */ + +/* a pointer to sched_getcpu(), in case we find it */ +typedef int (*sched_getcpu_ptr_t)(void); +sched_getcpu_ptr_t sched_getcpu_ptr; +static int need_warning = 0; + +/* the old, low-level code */ +static int useLeafB = 0; + +/* access to the CPUID instruction on Intel/AMD */ +typedef struct +{ + uint32_t eax, ebx, ecx, edx; +} CPUIDinfo; + +/** + * This function returns the result of the "cpuid" instruction + */ +static __attribute__ ((always_inline)) inline void +__collector_cpuid (CPUIDinfo* info) +{ + uint32_t ebx = info->ebx, ecx = info->ecx, edx = info->edx, eax = info->eax; + __asm__ ("cpuid" : "=b" (ebx), "=c" (ecx), "=d" (edx), "=a" (eax) : "a" (eax)); + info->eax = eax; + info->ebx = ebx; + info->ecx = ecx; + info->edx = edx; +} + +static void +getcpuid_init () +{ + CPUIDinfo info; + info.eax = 0; /* max input value for CPUID */ + __collector_cpuid (&info); + + if (info.eax >= 0xb) + { + info.eax = 0xb; + info.ecx = 0; + __collector_cpuid (&info); + useLeafB = info.ebx != 0; + } + + /* indicate that we need a warning */ + /* (need to wait until log mechanism has been initialized) */ + need_warning = 1; +} + +static uint32_t +getcpuid () +{ + /* if we found sched_getcpu(), use it */ + if (sched_getcpu_ptr) + return (*sched_getcpu_ptr)(); + + /* otherwise, check if we need warning */ + if (need_warning) + { + if (useLeafB) + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">x2APIC</event>\n", + SP_JCMD_CWARN, COL_WARN_LINUX_X86_APICID); + else + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">APIC</event>\n", + SP_JCMD_CWARN, COL_WARN_LINUX_X86_APICID); + need_warning = 0; + } + + /* and use the old, low-level code */ + CPUIDinfo info; + if (useLeafB) + { + info.eax = 0xb; + info.ecx = 0; + __collector_cpuid (&info); + return info.edx; /* x2APIC ID */ + } + else + { + info.eax = 0x1; + info.ecx = 0; + __collector_cpuid (&info); + return info.ebx >> 24; /* APIC ID */ + } +} + +#else /* sparc-Linux */ + +/* + * EUGENE + * How should sched_getcpu() be prototyped? Like this? + * #include <sched.h> + * Or like this? + * #define _GNU_SOURCE + * #include <utmpx.h> + * Or just prototype this function explicitly without bothering with include files. + */ +int sched_getcpu (); + +static int +getcpuid () +{ + return sched_getcpu (); +} +#endif + +/* if ever retries time-out, we will stop allowing them */ +static int exhausted_retries = 0; + +int +__collector_open (const char *path, int oflag, ...) +{ + int fd; + mode_t mode = 0; + + hrtime_t t_timeout = __collector_gethrtime () + 5 * ((hrtime_t) NANOSEC); + int nretries = 0; + long long delay = 100; /* start at some small, arbitrary value */ + + /* get optional mode argument if it's expected/required */ + if (oflag | O_CREAT) + { + va_list ap; + va_start (ap, oflag); + mode = (mode_t) va_arg (ap, mode_t); + va_end (ap); + } + + /* retry upon failure */ + while ((fd = CALL_UTIL (open_bare)(path, oflag, mode)) < 0) + { + if (exhausted_retries) + break; + + /* The particular condition we're willing to retry is if + * too many file descriptors were in use. The errno should + * be EMFILE, but apparently and mysteriously it can also be + * and often is ENOENT. + */ + if ((errno != EMFILE) && (errno != ENOENT)) + break; + if (__collector_gethrtime () > t_timeout) + { + exhausted_retries = 1; + break; + } + + /* Oddly, if I replace this spin wait with + * - a usleep() call or + * - a loop on gethrtime() calls + * for roughly the same length of time, retries aren't very effective. */ + int ispin; + double xdummy = 0.5; + for (ispin = 0; ispin < delay; ispin++) + xdummy = 0.5 * (xdummy + 1.); + if (xdummy < 0.1) + /* should never happen, but we check so the loop won't be optimized away */ + break; + delay *= 2; + if (delay > 100000000) + delay = 100000000; /* cap at some large, arbitrary value */ + nretries++; + } + return fd; +} + +int +__collector_util_init () +{ + int oldos = 0; + + /* Linux requires RTLD_LAZY, Solaris can do just RTLD_NOLOAD */ + void *libc = dlopen (SYS_LIBC_NAME, RTLD_LAZY | RTLD_NOLOAD); + if (libc == NULL) + libc = dlopen (SYS_LIBC_NAME, RTLD_NOW | RTLD_LOCAL); + if (libc == NULL) + { + /* libcollector will subsequently abort, as all the pointers in the vector are NULL */ +#if 0 + /* SP_COLLECTOR_TRACELEVEL is not yet set, so no Tprintf */ + fprintf (stderr, "__collector_util_init: dlopen(%s) failed: %s\n", SYS_LIBC_NAME, dlerror ()); + return COL_ERROR_UTIL_INIT; +#endif + abort (); + } + + void *ptr = dlsym (libc, "fprintf"); + if (ptr) + __collector_util_funcs.fprintf = (int(*)(FILE *, const char *, ...))ptr; + else + { + // We can't write any error messages without a libc reference +#if 0 + fprintf (stderr, "__collector_util_init: COLERROR_UTIL_INIT fprintf: %s\n", dlerror ()); + return COL_ERROR_UTIL_INIT; +#endif + abort (); + } + int err = 0; + + ptr = dlsym (libc, "mmap"); + if (ptr) + __collector_util_funcs.mmap = (void*(*)(void *, size_t, int, int, int, off_t))ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT mmap: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + /* mmap64 is only in 32-bits; this call goes to mmap in 64-bits */ + /* internal calls for mapping in libcollector call mmap64 */ + ptr = dlsym (libc, "mmap64"); + if (ptr) + __collector_util_funcs.mmap64 = (void*(*)(void *, size_t, int, int, int, off_t))ptr; + else + __collector_util_funcs.mmap64 = __collector_util_funcs.mmap; + + ptr = dlsym (libc, "munmap"); + if (ptr) + __collector_util_funcs.munmap = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT munmap: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "close"); + if (ptr) + __collector_util_funcs.close = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT close: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "open"); + if (ptr) + __collector_util_funcs.open = (int(*)(const char *path, int oflag, ...))ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT open: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + +#if ARCH(Intel) && WSIZE(32) + ptr = dlvsym (libc, "open64", "GLIBC_2.2"); // it is in /lib/libpthread.so.0 + if (ptr) + __collector_util_funcs.open_bare = (int(*)(const char *path, int oflag, ...))ptr; + else + { + Tprintf (DBG_LT0, "libcol_util: WARNING: dlvsym for %s@%s failed. Using dlsym() instead.", "open64", "GLIBC_2.2"); +#endif /* ARCH(Intel) && WSIZE(32) */ + ptr = dlsym (libc, "open64"); + if (ptr) + __collector_util_funcs.open_bare = (int(*)(const char *path, int oflag, ...))ptr; + else + __collector_util_funcs.open_bare = __collector_util_funcs.open; +#if ARCH(Intel) && WSIZE(32) + } +#endif /* ARCH(Intel) && WSIZE(32) */ + + ptr = dlsym (libc, "close"); + if (ptr) + __collector_util_funcs.close = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT close: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "read"); + if (ptr) + __collector_util_funcs.read = (ssize_t (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT read: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "write"); + if (ptr) + __collector_util_funcs.write = (ssize_t (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT write: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + +#if ARCH(Intel) && WSIZE(32) + ptr = dlvsym (libc, "pwrite", "GLIBC_2.2"); // it is in /lib/libpthread.so.0 + if (ptr) + __collector_util_funcs.pwrite = (ssize_t (*)())ptr; + else + { + Tprintf (DBG_LT0, "libcol_util: WARNING: dlvsym for %s@%s failed. Using dlsym() instead.", "pwrite", "GLIBC_2.2"); +#endif /* ARCH(Intel) && WSIZE(32) */ + ptr = dlsym (libc, "pwrite"); + if (ptr) + __collector_util_funcs.pwrite = (ssize_t (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT pwrite: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } +#if ARCH(Intel) && WSIZE(32) + } +#endif + +#if ARCH(Intel) && WSIZE(32) + ptr = dlvsym (libc, "pwrite64", "GLIBC_2.2"); // it is in /lib/libpthread.so.0 + if (ptr) + __collector_util_funcs.pwrite64 = (ssize_t (*)())ptr; + else + { + Tprintf (DBG_LT0, "libcol_util: WARNING: dlvsym for %s@%s failed. Using dlsym() instead.", "pwrite64", "GLIBC_2.2"); +#endif /* ARCH(Intel) && WSIZE(32) */ + ptr = dlsym (libc, "pwrite64"); + if (ptr) + __collector_util_funcs.pwrite64 = (ssize_t (*)())ptr; + else + __collector_util_funcs.pwrite64 = __collector_util_funcs.pwrite; +#if ARCH(Intel) && WSIZE(32) + } +#endif /* ARCH(Intel) && WSIZE(32) */ + + ptr = dlsym (libc, "lseek"); + if (ptr) + __collector_util_funcs.lseek = (off_t (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT lseek: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "access"); + if (ptr) + __collector_util_funcs.access = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT access: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "mkdir"); + if (ptr) + __collector_util_funcs.mkdir = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT mkdir: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "opendir"); + if (ptr) + __collector_util_funcs.opendir = (DIR * (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT opendir: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "closedir"); + if (ptr) + __collector_util_funcs.closedir = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT closedir: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "execv"); + if (ptr) + __collector_util_funcs.execv = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT execv: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "exit"); + if (ptr) + __collector_util_funcs.exit = (void(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT exit: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "vfork"); + if (ptr) + __collector_util_funcs.vfork = (pid_t (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT vfork: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "waitpid"); + if (ptr) + __collector_util_funcs.waitpid = (pid_t (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT waitpid: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + int (*__collector_getcpuid)() = (int(*)()) & getcpuid; +#if ARCH(Intel) + /* if sched_getcpu() not found, init our getcpuid() */ + sched_getcpu_ptr = (sched_getcpu_ptr_t) dlsym (libc, "sched_getcpu"); + if (sched_getcpu_ptr == NULL) + getcpuid_init (); +#endif + __collector_util_funcs.getcpuid = __collector_getcpuid; + __collector_util_funcs.memset = collector_memset; + + ptr = dlsym (libc, "malloc"); + if (ptr) + __collector_util_funcs.malloc = (void *(*)(size_t))ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT malloc: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "putenv"); + if (ptr) + __collector_util_funcs.putenv = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT putenv: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "getenv"); + if (ptr) + __collector_util_funcs.getenv = (char*(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT getenv: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "time"); + if (ptr) + __collector_util_funcs.time = (time_t (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT time: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "mktime"); + if (ptr) + __collector_util_funcs.mktime = (time_t (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT mktime: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + __collector_util_funcs.strcmp = __collector_strcmp; + __collector_util_funcs.strncmp = __collector_strncmp; + __collector_util_funcs.strncpy = __collector_strncpy; + __collector_util_funcs.strstr = __collector_strstr; + + ptr = dlsym (libc, "gmtime_r"); + if (ptr) + __collector_util_funcs.gmtime_r = (struct tm * (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT gmtime_r: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "strtol"); + if (ptr) + __collector_util_funcs.strtol = (long (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT strtol: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "strtoll"); + if (ptr) + __collector_util_funcs.strtoll = (long long (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT strtoll: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + __collector_util_funcs.strchr = __collector_strchr; + __collector_util_funcs.strrchr = __collector_strrchr; + + ptr = dlsym (libc, "setenv"); + if (ptr) + __collector_util_funcs.setenv = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT setenv: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "unsetenv"); + if (ptr) + __collector_util_funcs.unsetenv = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT unsetenv: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "atof"); + if (ptr) + __collector_util_funcs.atof = (double (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT atof: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "sysinfo"); + if (ptr) + __collector_util_funcs.sysinfo = (long (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT sysinfo: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "clearenv"); + if (ptr) + __collector_util_funcs.clearenv = (int(*)())ptr; + else + { + /* suppress warning on S10 or earlier Solaris */ + if (oldos == 0) + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT clearenv: %s\n", dlerror ()); + /* err = COL_ERROR_UTIL_INIT; */ + /* don't treat this as fatal, so that S10 could work */ + } + +#if ARCH(Intel) && WSIZE(32) + ptr = dlvsym (libc, "fopen", "GLIBC_2.1"); + if (ptr) + __collector_util_funcs.fopen = (FILE * (*)())ptr; + else + { + Tprintf (DBG_LT0, "libcol_util: WARNING: dlvsym for %s@%s failed. Using dlsym() instead.", "fopen", "GLIBC_2.1"); +#endif /* ARCH(Intel) && WSIZE(32) */ + ptr = dlsym (libc, "fopen"); + if (ptr) + __collector_util_funcs.fopen = (FILE * (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT fopen: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } +#if ARCH(Intel) && WSIZE(32) + } +#endif + + ptr = dlsym (libc, "popen"); + if (ptr) + __collector_util_funcs.popen = (FILE * (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT popen: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + +#if ARCH(Intel) && WSIZE(32) + ptr = dlvsym (libc, "fclose", "GLIBC_2.1"); + if (ptr) + __collector_util_funcs.fclose = (int(*)())ptr; + else + { + Tprintf (DBG_LT0, "libcol_util: WARNING: dlvsym for %s@%s failed. Using dlsym() instead.", "fclose", "GLIBC_2.1"); +#endif /* ARCH(Intel) && WSIZE(32) */ + ptr = dlsym (libc, "fclose"); + if (ptr) + __collector_util_funcs.fclose = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT fclose: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } +#if ARCH(Intel) && WSIZE(32) + } +#endif + + ptr = dlsym (libc, "pclose"); + if (ptr) + __collector_util_funcs.pclose = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT pclose: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "fgets"); + if (ptr) + __collector_util_funcs.fgets = (char*(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT fgets: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "sscanf"); + if (ptr) + __collector_sscanfp = (int(*)(const char *restrict s, const char *restrict fmt, ...))ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT sscanf: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "snprintf"); + if (ptr) + __collector_util_funcs.snprintf = (int(*)(char *, size_t, const char *, ...))ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT snprintf: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "vsnprintf"); + if (ptr) + __collector_util_funcs.vsnprintf = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT vsnprintf: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "atoi"); + if (ptr) + __collector_util_funcs.atoi = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT atoi: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "calloc"); + if (ptr) + __collector_util_funcs.calloc = (void*(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT calloc: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "free"); + if (ptr) + { + __collector_util_funcs.free = (void(*)())ptr; + } + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT free: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "strdup"); + if (ptr) + __collector_util_funcs.libc_strdup = (char*(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT strdup: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + __collector_util_funcs.strlen = __collector_strlen; + __collector_util_funcs.strlcat = __collector_strlcat; + __collector_util_funcs.strlcpy = __collector_strlcpy; + + ptr = dlsym (libc, "strerror"); + if (ptr) + __collector_util_funcs.strerror = (char*(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT strerror: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + ptr = dlsym (libc, "strerror_r"); + if (ptr) + __collector_util_funcs.strerror_r = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT strerror_r: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + ptr = dlsym (libc, "strspn"); + if (ptr) + __collector_util_funcs.strspn = (size_t (*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT strspn: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "strtoul"); + if (ptr) + __collector_util_funcs.strtoul = (unsigned long int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT strtoul: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "strtoull"); + if (ptr) + __collector_util_funcs.strtoull = (unsigned long long int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT strtoull: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "fcntl"); + if (ptr) + __collector_util_funcs.fcntl = (int(*)(int, int, ...))ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT fcntl: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "ioctl"); + if (ptr) + __collector_util_funcs.ioctl = (int(*)(int, int, ...))ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT ioctl: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "symlink"); + if (ptr) + __collector_util_funcs.symlink = (int(*)(const char*, const char*))ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT symlink: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "syscall"); + if (ptr) + __collector_util_funcs.syscall = (int(*)(int, ...))ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT syscall: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "sysconf"); + if (ptr) + __collector_util_funcs.sysconf = (long(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT sysconf: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "sigfillset"); + if (ptr) + __collector_util_funcs.sigfillset = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT sigfillset: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + ptr = dlsym (libc, "sigprocmask"); + if (ptr) + __collector_util_funcs.sigprocmask = (int(*)())ptr; + else + { + CALL_UTIL (fprintf)(stderr, "collector_util_init COL_ERROR_UTIL_INIT sigprocmask: %s\n", dlerror ()); + err = COL_ERROR_UTIL_INIT; + } + + return err; +} diff --git a/gprofng/libcollector/libcol_util.h b/gprofng/libcollector/libcol_util.h new file mode 100644 index 0000000..4384d47 --- /dev/null +++ b/gprofng/libcollector/libcol_util.h @@ -0,0 +1,321 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _LIBCOL_UTIL_H +#define _LIBCOL_UTIL_H + +#include <stdarg.h> +#include <pthread.h> +#include <signal.h> + +// LIBCOLLECTOR NOT I18N +#define NTXT(x) x +#define STXT(x) x + +extern int __collector_tracelevel; + +/* Initialization function */ +extern int __collector_util_init(); +extern void __collector_libkstat_funcs_init(); +extern void __collector_libscf_funcs_init(); + +/* ------- functions from libcol_util.c ----------------- */ +extern void * __collector_memcpy (void *s1, const void *s2, size_t n); +extern int (*__collector_sscanfp)(const char *restrict s, const char *restrict fmt, ...); +extern char * __collector_strcat (char *s1, const char *s2); +extern char * __collector_strchr (const char *s1, int chr); +extern size_t __collector_strlcpy (char *dst, const char *src, size_t dstsize); +extern char* __collector_strrchr (const char *str, int chr); +extern size_t __collector_strlen (const char *s); +extern size_t __collector_strlcat (char *dst, const char *src, size_t dstsize); +extern char* __collector_strchr (const char *str, int chr); +extern int __collector_strcmp (const char *s1, const char *s2); +extern int __collector_strncmp (const char *s1, const char *s2, size_t n); +extern char * __collector_strstr (const char *s1, const char *s2); +extern size_t __collector_strncpy (char *dst, const char *src, size_t dstsize); +extern size_t __collector_strncat (char *dst, const char *src, size_t dstsize); +extern void * __collector_malloc (size_t size); +extern void * __collector_calloc (size_t nelem, size_t elsize); +extern char * __collector_strdup (const char * str); +extern int __collector_strStartWith (const char *s1, const char *s2); +extern int __collector_xml_snprintf (char *s, size_t n, const char *format, ...) __attribute__ ((format (printf, 3, 4))); +extern int __collector_xml_vsnprintf (char *s, size_t n, const char *format, va_list args); + +/* ------- collector_thread ----------------- */ +pid_t __collector_gettid (); +extern void __collector_ext_gettid_tsd_create_key (); +#define collector_thread_t pthread_t // not using pid_t, since tid is defined as pthread_t in package structures, and other codes assume this type +#define statvfs_t struct statvfs +#define __collector_lwp_self() (collector_thread_t)__collector_gettid() // not using pthread_self() +#define __collector_thr_self() (collector_thread_t)__collector_gettid() // not using pthread_self() + +/* ------- collector_mutex ----------------- */ +/* + * mutex_init is defined in libthread. If we don't want to interact + * with libthread we should use memset to initialize mutexes + */ + +typedef volatile int collector_mutex_t; +#define COLLECTOR_MUTEX_INITIALIZER 0 +extern int __collector_mutex_lock (collector_mutex_t *mp); +extern int __collector_mutex_unlock (collector_mutex_t *mp); +extern int __collector_mutex_trylock (collector_mutex_t *mp); + +#define __collector_mutex_init(xx) \ + do { collector_mutex_t tmp=COLLECTOR_MUTEX_INITIALIZER; *(xx)=tmp; } while(0) + +void __collector_sample (char *name); +void __collector_terminate_expt (); +void __collector_pause (); +void __collector_pause_m (); +void __collector_resume (); + +struct DT_lineno; + +typedef enum +{ + DFUNC_API = 1, /* dynamic function declared with API */ + DFUNC_JAVA, /* dynamically compiled java method */ + DFUNC_KERNEL /* dynamic code mapped by the kernel (Linux) */ +} dfunc_mode_t; + +extern void __collector_int_func_load (dfunc_mode_t mode, char *name, + char *sourcename, void *vaddr, + int size, int lntsize, + struct DT_lineno *lntable); +extern void __collector_int_func_unload (dfunc_mode_t mode, void *vaddr); + +extern int __collector_sigaction (int sig, const struct sigaction *nact, + struct sigaction *oact); +extern void __collector_SIGDFL_handler (int sig); +extern int __collector_ext_itimer_set (int period); + +#if ARCH(Intel) +/* Atomic functions on x86/x64 */ + +/** + * This function enables the inrementing (by one) of the value stored in target + * to occur in an atomic manner. + */ +static __attribute__ ((always_inline)) inline void +__collector_inc_32 (uint32_t *ptr) +{ + __asm__ __volatile__("lock; incl %0" + : // "=m" (*ptr) // output + : "m" (*ptr)); // input +} + +/** + * This function enables the decrementing (by one) of the value stored in target + * to occur in an atomic manner. + */ +static __attribute__ ((always_inline)) inline void +__collector_dec_32 (volatile uint32_t *ptr) +{ + __asm__ __volatile__("lock; decl %0" + : // "=m" (*ptr) // output + : "m" (*ptr)); // input +} +/** + * This function subtrackts the value "off" of the value stored in target + * to occur in an atomic manner, and returns new value stored in target. + */ +static __attribute__ ((always_inline)) inline uint32_t +__collector_subget_32 (uint32_t *ptr, uint32_t off) +{ + uint32_t r; + uint32_t offset = off; + __asm__ __volatile__("movl %2, %0; negl %0; lock; xaddl %0, %1" + : "=r" (r), "=m" (*ptr) /* output */ + : "a" (off), "r" (*ptr) /* input */ + ); + return (r - offset); +} +/** + * This function returns the value of the stack pointer register + */ +static __attribute__ ((always_inline)) inline void * +__collector_getsp () +{ + void *r; +#if WSIZE(64) + __asm__ __volatile__("movq %%rsp, %0" +#else + __asm__ __volatile__("movl %%esp, %0" +#endif + : "=r" (r)); // output + return r; +} +/** + * This function returns the value of the frame pointer register + */ +static __attribute__ ((always_inline)) inline void * +__collector_getfp () +{ + void *r; +#if WSIZE(64) + __asm__ __volatile__("movq %%rbp, %0" +#else + __asm__ __volatile__("movl %%ebp, %0" +#endif + : "=r" (r)); // output + return r; +} +/** + * This function returns the value of the processor counter register + */ +static __attribute__ ((always_inline)) inline void * +__collector_getpc () +{ + void *r; + __asm__ __volatile__( +#if WSIZE(32) + " call 1f \n" + "1: popl %0 \n" +#else + " call 1f \n" + "1: popq %0 \n" +#endif + : "=r" (r)); // output + return r; +} + +/** + * This function enables a compare and swap operation to occur atomically. + * The 32-bit value stored in target is compared with "old". If these values + * are equal, the value stored in target is replaced with "new". The old + * 32-bit value stored in target is returned by the function whether or not + * the replacement occurred. + */ +static __attribute__ ((always_inline)) inline uint32_t +__collector_cas_32 (volatile uint32_t *pdata, uint32_t old, uint32_t new) +{ + uint32_t r; + __asm__ __volatile__("lock; cmpxchgl %2, %1" + : "=a" (r), "=m" (*pdata) : "r" (new), + "a" (old), "m" (*pdata)); + return r; +} +/** + * This function enables a compare and swap operation to occur atomically. + * The 64-bit value stored in target is compared with "old". If these values + * are equal, the value stored in target is replaced with "new". The old + * 64-bit value stored in target is returned by the function whether or not + * the replacement occurred. + */ +static __attribute__ ((always_inline)) inline uint64_t +__collector_cas_64p (volatile uint64_t *mem, uint64_t *old, uint64_t * new) +{ + uint64_t r; +#if WSIZE(32) + uint32_t old1 = (uint32_t) (*old & 0xFFFFFFFFL); + uint32_t old2 = (uint32_t) ((*old >> 32) & 0xFFFFFFFFL); + uint32_t new1 = (uint32_t) (*new & 0xFFFFFFFFL); + uint32_t new2 = (uint32_t) ((*new >> 32) & 0xFFFFFFFFL); + uint32_t res1 = 0; + uint32_t res2 = 0; + __asm__ __volatile__( + "movl %3, %%esi; lock; cmpxchg8b (%%esi); movl %%edx, %2; movl %%eax, %1" + : "=m" (r), "=m" (res1), "=m" (res2) /* output */ + : "m" (mem), "a" (old1), "d" (old2), "b" (new1), "c" (new2) /* input */ + : "memory", "cc", "esi" //, "edx", "ecx", "ebx", "eax" /* clobbered register */ + ); + r = (((uint64_t) res2) << 32) | ((uint64_t) res1); +#else + __asm__ __volatile__( "lock; cmpxchgq %2, %1" + : "=a" (r), "=m" (*mem) /* output */ + : "r" (*new), "a" (*old), "m" (*mem) /* input */ + : "%rcx", "rdx" /* clobbered register */ + ); +#endif + return r; +} +/** + * This function enables a compare and swap operation to occur atomically. + * The 32-/64-bit value stored in target is compared with "cmp". If these values + * are equal, the value stored in target is replaced with "new". + * The old value stored in target is returned by the function whether or not + * the replacement occurred. + */ +static __attribute__ ((always_inline)) inline void * +__collector_cas_ptr (void *mem, void *cmp, void *new) +{ + void *r; +#if WSIZE(32) + r = (void *) __collector_cas_32 ((volatile uint32_t *)mem, (uint32_t) cmp, (uint32_t)new); +#else + __asm__ __volatile__("lock; cmpxchgq %2, (%1)" + : "=a" (r), "=b" (mem) /* output */ + : "r" (new), "a" (cmp), "b" (mem) /* input */ + ); +#endif + return r; +} + +#elif ARCH(Aarch64) +static __attribute__ ((always_inline)) inline uint32_t +__collector_inc_32 (volatile uint32_t *ptr) +{ + return __sync_add_and_fetch (ptr, 1); +} + +static __attribute__ ((always_inline)) inline uint32_t +__collector_dec_32 (volatile uint32_t *ptr) +{ + return __sync_sub_and_fetch (ptr, 1); +} + +static __attribute__ ((always_inline)) inline uint32_t +__collector_subget_32 (volatile uint32_t *ptr, uint32_t off) +{ + return __sync_sub_and_fetch (ptr, off); +} + +static __attribute__ ((always_inline)) inline uint32_t +__collector_cas_32 (volatile uint32_t *ptr, uint32_t old, uint32_t new) +{ + return __sync_val_compare_and_swap (ptr, old, new); +} + +static __attribute__ ((always_inline)) inline uint64_t +__collector_cas_64p (volatile uint64_t *ptr, uint64_t *old, uint64_t * new) +{ + return __sync_val_compare_and_swap (ptr, *old, *new); +} + +static __attribute__ ((always_inline)) inline void * +__collector_cas_ptr (void *ptr, void *old, void *new) +{ + return (void *) __sync_val_compare_and_swap ((unsigned long *) ptr, (unsigned long) old, (unsigned long) new); +} + +#else +extern void __collector_flushw (); /* defined for SPARC only */ +extern void* __collector_getpc (); +extern void* __collector_getsp (); +extern void* __collector_getfp (); +extern void __collector_inc_32 (volatile uint32_t *); +extern void __collector_dec_32 (volatile uint32_t *); +extern void* __collector_cas_ptr (volatile void *, void *, void *); +extern uint32_t __collector_cas_32 (volatile uint32_t *, uint32_t, uint32_t); +extern uint32_t __collector_subget_32 (volatile uint32_t *, uint32_t); +extern uint64_t __collector_cas_64p (volatile uint64_t *, uint64_t *, uint64_t *); +#endif /* ARCH() */ +#endif /* _LIBCOL_UTIL_H */ diff --git a/gprofng/libcollector/linetrace.c b/gprofng/libcollector/linetrace.c new file mode 100644 index 0000000..970d68c --- /dev/null +++ b/gprofng/libcollector/linetrace.c @@ -0,0 +1,2005 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Lineage events for process fork, exec, etc. + */ + +#include "config.h" +#include <string.h> +#include <elf.h> +#include <regex.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#include "descendants.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LTT 0 // for interposition on GLIBC functions +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +#define LT_MAXNAMELEN 1024 +#define LT_MAXPATHLEN 1024 + +int __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; +int dbg_current_mode = FOLLOW_NONE; /* for debug only */ +unsigned line_key = COLLECTOR_TSD_INVALID_KEY; +line_mode_t line_mode = LM_DORMANT; +int user_follow_mode = FOLLOW_ON; +int java_mode = 0; + +static char *user_follow_spec; +static char new_lineage[LT_MAXNAMELEN]; +static char curr_lineage[LT_MAXNAMELEN]; +static char linetrace_exp_dir_name[LT_MAXPATHLEN + 1]; // experiment directory + +/* lineage tracking for descendants of this process */ + +static int fork_linenum = 0; +static int line_initted = 0; +static collector_mutex_t fork_lineage_lock = COLLECTOR_MUTEX_INITIALIZER; +static collector_mutex_t clone_lineage_lock = COLLECTOR_MUTEX_INITIALIZER; + +/* interposition */ +#define CALL_REAL(x) (*(int(*)())__real_##x) +#define CALL_REALC(x) (*(char*(*)())__real_##x) +#define CALL_REALF(x) (*(FILE*(*)())__real_##x) +#define NULL_PTR(x) ( __real_##x == NULL ) + +// For a given Linux, which lib functions have more than one GLIBC version? Do this: +// objdump -T `find /lib /lib64 -name "*.so*"` | grep GLIBC | grep text | grep \( +static void *__real_fork = NULL; +static void *__real_vfork = NULL; +static void *__real_execve = NULL; +static void *__real_execvp = NULL; +static void *__real_execv = NULL; +static void *__real_execle = NULL; +static void *__real_execlp = NULL; +static void *__real_execl = NULL; +static void *__real_clone = NULL; +static void *__real_grantpt = NULL; +static void *__real_ptsname = NULL; +static void *__real_popen = NULL; +static int clone_linenum = 0; +#if ARCH(Intel) +#if WSIZE(32) +static void *__real_popen_2_1 = NULL; +static void *__real_popen_2_0 = NULL; +static void *__real_posix_spawn_2_15 = NULL; +static void *__real_posix_spawnp_2_15 = NULL; +static void *__real_posix_spawn_2_2 = NULL; +static void *__real_posix_spawnp_2_2 = NULL; +#elif WSIZE(64) +static void *__real_posix_spawn_2_15 = NULL; +static void *__real_posix_spawnp_2_15 = NULL; +static void *__real_posix_spawn_2_2_5 = NULL; +static void *__real_posix_spawnp_2_2_5 = NULL; +#endif /* WSIZE() */ +#endif /* ARCH(Intel) */ +static void *__real_system = NULL; +static void *__real_posix_spawn = NULL; +static void *__real_posix_spawnp = NULL; +static void *__real_setuid = NULL; +static void *__real_seteuid = NULL; +static void *__real_setreuid = NULL; +static void *__real_setgid = NULL; +static void *__real_setegid = NULL; +static void *__real_setregid = NULL; +static void linetrace_dormant (); +static int check_follow_fork (); +static int check_follow_exec (const char *execfile); +static int check_follow_combo (const char *execfile); +static int path_collectable (const char *execfile); +static char * build_experiment_path (char *instring, size_t instring_sz, const char *lineage_str); +static int init_lineage_intf (); + +/* ------- "Previously dbx-visible" function prototypes ----------------- */ +static int linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *execfile); +static char *lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname); +static void linetrace_ext_fork_prologue (const char *variant, char * new_lineage, int *following_fork); +static void linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * new_lineage, int *following_fork); +static char **linetrace_ext_exec_prologue (const char *variant, + const char* path, char *const argv[], char *const envp[], int *following_exec); +static void linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec); +static void linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo); +static void linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo); + +#ifdef DEBUG +static int +get_combo_flag () +{ + int * guard = NULL; + int combo_flag = ((line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0); + return combo_flag; +} +#endif /* DEBUG */ + +/* must be called for potentially live experiment */ +int +__collector_ext_line_init (int *precord_this_experiment, + const char * progspec, const char * progname) +{ + *precord_this_experiment = 1; + TprintfT (DBG_LT0, "__collector_ext_line_init(%s)\n", progspec); + if (NULL_PTR (fork)) + if (init_lineage_intf ()) + { + TprintfT (DBG_LT0, "__collector_ext_line_init() ERROR: initialization failed.\n"); + return COL_ERROR_LINEINIT; + } + /* check the follow spec */ + user_follow_spec = CALL_UTIL (getenv)(SP_COLLECTOR_FOLLOW_SPEC); + if (user_follow_spec != NULL) + { + TprintfT (DBG_LT0, "collector: %s=%s\n", SP_COLLECTOR_FOLLOW_SPEC, user_follow_spec); + if (!linetrace_follow_experiment (user_follow_spec, curr_lineage, progname)) + { + *precord_this_experiment = 0; + TprintfT (DBG_LT0, "collector: -F =<regex> does not match, will NOT be followed\n"); + } + else + TprintfT (DBG_LT0, "collector: -F =<regex> matches, will be followed\n"); + user_follow_mode = FOLLOW_ALL; + } + __collector_env_save_preloads (); + TprintfT (DBG_LT0, "__collector_ext_line_init(), progname=%s, followspec=%s, followthis=%d\n", + progname, user_follow_spec ? user_follow_spec : "NULL", + *precord_this_experiment); + line_mode = LM_TRACK_LINEAGE; /* even if we don't follow, we report the interposition */ + line_initted = 1; + return COL_ERROR_NONE; +} + +/* + * int __collector_ext_line_install(args) + * Check args to determine which line events to follow. + * Create tsd key for combo flag. + */ +int +__collector_ext_line_install (char *args, const char * expname) +{ + if (!line_initted) + { + TprintfT (DBG_LT0, "__collector_ext_line_install(%s) ERROR: init hasn't be called yet\n", args); + return COL_ERROR_EXPOPEN; + } + TprintfT (DBG_LT0, "__collector_ext_line_install(%s, %s)\n", args, expname); + line_key = __collector_tsd_create_key (sizeof (int), NULL, NULL); + + /* determine experiment name */ + __collector_strlcpy (linetrace_exp_dir_name, expname, sizeof (linetrace_exp_dir_name)); + lineage_from_expname (curr_lineage, sizeof (curr_lineage), linetrace_exp_dir_name); + user_follow_mode = CALL_UTIL (atoi)(args); + TprintfT (DBG_LT0, "__collector_ext_line_install() user_follow_mode=0x%X, linetrace_exp_dir_name=%s\n", + user_follow_mode, linetrace_exp_dir_name); + + // determine java mode + char * java_follow_env = CALL_UTIL (getenv)(JAVA_TOOL_OPTIONS); + if (java_follow_env != NULL && CALL_UTIL (strstr)(java_follow_env, COLLECTOR_JVMTI_OPTION)) + java_mode = 1; + + // backup collector specific env + if (sp_env_backup == NULL) + { + sp_env_backup = __collector_env_backup (); + TprintfT (DBG_LT0, "__collector_ext_line_install creating sp_env_backup -- 0x%p\n", sp_env_backup); + } + else + TprintfT (DBG_LT0, "__collector_ext_line_install existing sp_env_backup -- 0x%p\n", sp_env_backup); + if (user_follow_mode == FOLLOW_NONE) + __collector_env_unset (NULL); + + char logmsg[256]; + logmsg[0] = '\0'; + if (user_follow_mode != FOLLOW_NONE) + CALL_UTIL (strlcat)(logmsg, "fork|exec|combo", sizeof (logmsg)); + size_t slen = __collector_strlen (logmsg); + if (slen > 0) + logmsg[slen] = '\0'; + else + CALL_UTIL (strlcat)(logmsg, "none", sizeof (logmsg)); + + /* report which line events are followed */ + (void) __collector_log_write ("<setting %s=\"%s\"/>\n", SP_JCMD_LINETRACE, logmsg); + TprintfT (DBG_LT0, "__collector_ext_line_install(%s): %s \n", expname, logmsg); + return COL_ERROR_NONE; +} + +char * +lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname) +{ + TprintfT (DBG_LT0, "lineage_from_expname(%s, %s)\n", lineage_str, expname); + char *p = NULL; + if (lstr_sz < 1 || !lineage_str || !expname) + { + TprintfT (DBG_LT0, "lineage_from_expname(): ERROR, null string\n"); + return NULL; + } + /* determine lineage from experiment name */ + p = __collector_strrchr (expname, '/'); + if ((p == NULL) || (*++p != '_')) + { + lineage_str[0] = 0; + TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\".\" (founder)\n", expname); + } + else + { + size_t tmp = __collector_strlcpy (lineage_str, p, lstr_sz); + if (tmp >= lstr_sz) + TprintfT (DBG_LT0, "lineage_from_expname(): ERROR: expt=%s lineage=\"%s\" truncated %ld characters\n", + expname, lineage_str, (long) (tmp - lstr_sz)); + lineage_str[lstr_sz - 1] = 0; + p = __collector_strchr (lineage_str, '.'); + if (p != NULL) + *p = '\0'; + TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\"%s\"\n", expname, lineage_str); + } + return lineage_str; +} + +/* + * void __collector_line_cleanup (void) + * Disable logging. Clear backup ENV. + */ +void +__collector_line_cleanup (void) +{ + if (line_mode == LM_CLOSED) + { + TprintfT (DBG_LT0, "__collector_line_cleanup(): WARNING, line is already closed\n"); + return; + } + else if (line_mode == LM_DORMANT) + TprintfT (DBG_LT0, "__collector_line_cleanup(): ERROR, line is DORMANT\n"); + else + TprintfT (DBG_LT0, "__collector_line_cleanup()\n"); + line_mode = LM_CLOSED; + user_follow_mode = FOLLOW_NONE; + dbg_current_mode = FOLLOW_NONE; /* for debug only */ + line_key = COLLECTOR_TSD_INVALID_KEY; + java_mode = 0; + if (sp_env_backup != NULL) + { + __collector_env_backup_free (); + sp_env_backup = NULL; + } + return; +} + +/* + * void __collector_ext_line_close (void) + * Disable logging. Cleans ENV vars. Clear backup ENV. + */ +void +__collector_ext_line_close (void) +{ + TprintfT (DBG_LT0, "__collector_ext_line_close()\n"); + __collector_line_cleanup (); + __collector_env_unset (NULL); + return; +} + +/* + * void linetrace_dormant(void) + * Disable logging. Preserve ENV vars. + */ +static void +linetrace_dormant (void) +{ + if (line_mode == LM_DORMANT) + { + TprintfT (DBG_LT0, "linetrace_dormant() -- already dormant\n"); + return; + } + else if (line_mode == LM_CLOSED) + { + TprintfT (DBG_LT0, "linetrace_dormant(): ERROR, line is already CLOSED\n"); + return; + } + else + TprintfT (DBG_LT0, "linetrace_dormant()\n"); + line_mode = LM_DORMANT; + return; +} + +static int +check_follow_fork () +{ + int follow = (user_follow_mode != FOLLOW_NONE); + TprintfT (DBG_LT0, "check_follow_fork()=%d\n", follow); + return follow; +} + +static int +check_follow_exec (const char *execfile) +{ + int follow = (user_follow_mode != FOLLOW_NONE); + if (follow) + { + /* revise based on collectability of execfile */ + follow = path_collectable (execfile); + } + TprintfT (DBG_LT0, "check_follow_exec(%s)=%d\n", execfile, follow); + return follow; +} + +static int +check_follow_combo (const char *execfile) +{ + int follow = (user_follow_mode != FOLLOW_NONE); + TprintfT (DBG_LT0, "check_follow_combo(%s)=%d\n", execfile, follow); + return follow; +} + +static int +check_fd_dynamic (int fd) +{ + TprintfT (DBG_LT0, "check_fd_dynamic(%d)\n", fd); + off_t off = CALL_UTIL (lseek)(fd, (off_t) 0, SEEK_END); + size_t sz = (size_t) 8192; /* one page should suffice */ + if (sz > off) + sz = off; + char *p = CALL_UTIL (mmap64)((char *) 0, sz, PROT_READ, MAP_PRIVATE, fd, (off64_t) 0); + if (p == MAP_FAILED) + { + TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: mmap failed for `%d'\n", fd); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, + COL_WARN_NOFOLLOW, "mmap-failed"); + return 0; + } + char elfclass = p[EI_CLASS]; + if ((p[EI_MAG0] != ELFMAG0) || + (p[EI_MAG1] != ELFMAG1) || + (p[EI_MAG2] != ELFMAG2) || + (p[EI_MAG3] != ELFMAG3) || + (elfclass != ELFCLASS32 && elfclass != ELFCLASS64) + ) + { + TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' is not executable ELF!\n", fd); + CALL_UTIL (munmap)(p, sz); + return 1; + } + Elf32_Ehdr *ehdr32 = (Elf32_Ehdr*) p; + Elf64_Ehdr *ehdr64 = (Elf64_Ehdr*) p; + Elf64_Off e_phoff; + Elf64_Half e_phnum; + Elf64_Half e_phentsize; + if (elfclass == ELFCLASS32) + { + e_phoff = ehdr32->e_phoff; + e_phnum = ehdr32->e_phnum; + e_phentsize = ehdr32->e_phentsize; + } + else + { + e_phoff = ehdr64->e_phoff; + e_phnum = ehdr64->e_phnum; + e_phentsize = ehdr64->e_phentsize; + } + if ((sizeof (Elf32_Ehdr) > sz) || + (sizeof (Elf64_Ehdr) > sz) || + (e_phoff + e_phentsize * (e_phnum - 1) > sz)) + { + TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' ELF file did not fit in page!\n", fd); +#if 0 + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, + COL_WARN_RISKYFOLLOW, "ELF header size"); +#endif + CALL_UTIL (munmap)(p, sz); + return 1; + } + TprintfT (DBG_LT2, "check_fd_dynamic(): elfclass=%d, e_phoff=%lu e_phnum=%lu e_phentsize=%lu\n", + (int) elfclass, (unsigned long) e_phoff, (unsigned long) e_phnum, + (unsigned long) e_phentsize); + int dynamic = 0; + Elf64_Half i; + for (i = 0; i < e_phnum; i++) + { + if (elfclass == ELFCLASS32) + { + if (PT_DYNAMIC == + ((Elf32_Phdr*) (p + e_phoff + e_phentsize * i))->p_type) + { + dynamic = 1; + break; + } + } + else + { + if (PT_DYNAMIC == + ((Elf64_Phdr*) (p + e_phoff + e_phentsize * i))->p_type) + { + dynamic = 1; + break; + } + } + } + if (!dynamic) + { + TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: Command `%d' is not a dynamic executable!\n", fd); +#if 0 + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, + COL_WARN_NOFOLLOW, "!dynamic"); +#endif + } + else + TprintfT (DBG_LT2, "check_fd_dynamic(): Command `%d' is a dynamic executable!\n", fd); + CALL_UTIL (munmap)(p, sz); + return dynamic; +} + +static int +check_dynamic (const char *execfile) +{ + TprintfT (DBG_LT2, "check_dynamic(%s)\n", execfile); + int fd = CALL_UTIL (open)(execfile, O_RDONLY); + if (fd == -1) + { + TprintfT (DBG_LT0, "check_dynamic(): ERROR/WARNING: Command `%s' could not be opened!\n", execfile); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, + COL_WARN_RISKYFOLLOW, "open"); + return 1; /* follow, though exec will presumably fail */ + } + int ret = check_fd_dynamic (fd); + CALL_UTIL (close)(fd); + return ret; +} + +static int +path_collectable (const char *execfile) +{ + TprintfT (DBG_LT0, "path_collectable(%s)\n", execfile); + /* Check that execfile exists and is a collectable executable */ + /* logging warning when collection is likely to be unsuccessful */ + /* (if check isn't accurate, generally best not to include it) */ + + if (execfile && !__collector_strchr (execfile, '/')) + { /* got an unqualified name */ + /* XXXX locate execfile on PATH to be able to check it */ + TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't check unqualified executable `%s'\n", execfile); +#if 0 + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, + COL_WARN_RISKYFOLLOW, "path"); +#endif + return 1; /* follow unqualified execfile unchecked */ + } + struct stat sbuf; + if (stat (execfile, &sbuf)) + { /* can't stat it */ + TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't stat `%s'\n", execfile); +#if 0 + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, + COL_WARN_RISKYFOLLOW, "stat"); +#endif + return 1; /* follow, though exec will probably fail */ + } + TprintfT (DBG_LT2, "path_collectable(%s) mode=0%o uid=%d gid=%d\n", + execfile, sbuf.st_mode, sbuf.st_uid, sbuf.st_gid); + if (((sbuf.st_mode & S_IXUSR) == 0) || ((sbuf.st_mode & S_IFMT) == S_IFDIR)) + { + TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is NOT an executable file!\n", execfile); +#if 0 + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, + COL_WARN_RISKYFOLLOW, "mode"); +#endif + return 1; /* follow, though exec will presumably fail */ + } + /* XXXX setxid(root) is OK iff libcollector is registered as secure */ + /* XXXX setxid(non-root) is OK iff umask is accomodating */ + if (((sbuf.st_mode & S_ISUID) != 0) || ((sbuf.st_mode & S_ISGID) != 0)) + { + TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is SetXID!\n", execfile); +#if 0 + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, + COL_WARN_RISKYFOLLOW, "setxid"); +#endif + return 1; /* follow, though collection may be unreliable */ + } + if (!check_dynamic (execfile)) + { + TprintfT (DBG_LT0, "path_collectable(%s) WARNING/ERROR: not dynamic, not collectng!\n", execfile); + return 0; /* don't follow, collection will fail unpredictably */ + } + TprintfT (DBG_LT2, "path_collectable(%s) OK!\n", execfile); + return 1; /* OK to follow */ +} + +static char * +build_experiment_path (char * instring, size_t instring_sz, const char *lineage_str) +{ + TprintfT (DBG_LT0, "build_experiment_path(,%ld, %s)\n", + (long) instring_sz, lineage_str); + const char *p = CALL_UTIL (strstr)(linetrace_exp_dir_name, DESCENDANT_EXPT_KEY); + int basedir_sz; + if (p) + basedir_sz = p - linetrace_exp_dir_name + 4; /* +3 because of DESCENDANT_EXPT_KEY */ + else + basedir_sz = __collector_strlen (linetrace_exp_dir_name) + 1; + int additional_sz = __collector_strlen (lineage_str) + 4; + if (basedir_sz + additional_sz > instring_sz) + { + TprintfT (DBG_LT0, "build_experiment_path(%s,%s): ERROR: path too long: %d > %ld\n", + linetrace_exp_dir_name, lineage_str, + basedir_sz + additional_sz, (long) instring_sz); + *instring = 0; + return NULL; + } + __collector_strlcpy (instring, linetrace_exp_dir_name, basedir_sz); + size_t slen = __collector_strlen (instring); + CALL_UTIL (snprintf)(instring + slen, instring_sz - slen, "/%s.er", lineage_str); + assert (__collector_strlen (instring) + 1 == basedir_sz + additional_sz); + return instring; +} + +static void +check_reuid_change (uid_t ruid, uid_t euid) +{ + uid_t curr_ruid = getuid (); + uid_t curr_euid = geteuid (); + mode_t curr_umask = umask (0); + umask (curr_umask); /* restore original umask */ + int W_oth = !(curr_umask & S_IWOTH); + TprintfT (DBG_LT0, "check_reuid_change(%d,%d): umask=%03o\n", ruid, euid, curr_umask); + TprintfT (DBG_LT0, "check_reuid_change(): umask W usr=%d grp=%d oth=%d\n", + (int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth); + if (ruid != -1) + { + TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_ruid, ruid); + if ((curr_euid == 0) && (ruid != 0) && !W_oth) + { + /* changing to non-root ID, with umask blocking writes by other */ + TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after ruid change (%d->%d)\n", + curr_ruid, ruid); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o ruid %d->%d</event>\n", + SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_ruid, ruid); + } + } + if (euid != -1) + { + TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_euid, euid); + if ((curr_euid == 0) && (euid != 0) && !W_oth) + { + /* changing to non-root ID, with umask blocking writes by other */ + TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after euid change (%d->%d)\n", + curr_euid, euid); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o euid %d->%d</event>\n", + SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_euid, euid); + } + } +} + +static void +check_regid_change (gid_t rgid, gid_t egid) +{ + gid_t curr_rgid = getgid (); + gid_t curr_egid = getegid (); + uid_t curr_euid = geteuid (); + mode_t curr_umask = umask (0); + umask (curr_umask); /* restore original umask */ + int W_oth = !(curr_umask & S_IWOTH); + TprintfT (DBG_LT0, "check_regid_change(%d,%d): umask=%03o euid=%d\n", + rgid, egid, curr_umask, curr_euid); + TprintfT (DBG_LT0, "umask W usr=%d grp=%d oth=%d\n", + (int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth); + if (rgid != -1) + { + TprintfT (DBG_LT0, "check_regid_change(%d->%d)\n", curr_rgid, rgid); + if ((curr_euid == 0) && (rgid != 0) && !W_oth) + { + /* changing to non-root ID, with umask blocking writes by other */ + TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after rgid change (%d->%d)\n", + curr_rgid, rgid); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o rgid %d->%d</event>\n", + SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_rgid, rgid); + } + } + if (egid != -1) + { + TprintfT (DBG_LT0, "check_regid_change(): check_egid_change(%d->%d)\n", curr_egid, egid); + if ((curr_euid == 0) && (egid != 0) && !W_oth) + { + /* changing to non-root ID, with umask blocking writes by other */ + TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after egid change (%d->%d)\n", + curr_egid, egid); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o egid %d->%d</event>\n", + SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_egid, egid); + } + } +} + +static int +init_lineage_intf () +{ + void *dlflag; + TprintfT (DBG_LT2, "init_lineage_intf()\n"); + + static int nesting_check = 0; + if (nesting_check >= 2) + { + /* segv before stack blows up */ + nesting_check /= (nesting_check - 2); + } + nesting_check++; + + __real_fork = dlsym (RTLD_NEXT, "fork"); + if (__real_fork == NULL) + { + __real_fork = dlsym (RTLD_DEFAULT, "fork"); + if (__real_fork == NULL) + return 1; + dlflag = RTLD_DEFAULT; + } + else + dlflag = RTLD_NEXT; + TprintfT (DBG_LT2, "init_lineage_intf() using RTLD_%s\n", + dlflag == RTLD_DEFAULT ? "DEFAULT" : "NEXT"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_fork\n", __real_fork); + __real_vfork = dlsym (dlflag, "vfork"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_vfork\n", __real_vfork); + __real_execve = dlsym (dlflag, "execve"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execve\n", __real_execve); + __real_execvp = dlsym (dlflag, "execvp"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execvp\n", __real_execvp); + __real_execv = dlsym (dlflag, "execv"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execv\n", __real_execv); + __real_execle = dlsym (dlflag, "execle"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execle\n", __real_execle); + __real_execlp = dlsym (dlflag, "execlp"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execlp\n", __real_execlp); + __real_execl = dlsym (dlflag, "execl"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execl\n", __real_execl); + __real_clone = dlsym (dlflag, "clone"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_clone\n", __real_clone); + __real_posix_spawn = dlsym (dlflag, "posix_spawn"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_posix_spawn\n", + __real_posix_spawn); + __real_posix_spawnp = dlsym (dlflag, "posix_spawnp"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_posix_spawnp\n", + __real_posix_spawnp); + __real_popen = dlvsym (dlflag, "popen", SYS_POPEN_VERSION); + TprintfT (DBG_LT2, "init_lineage_intf()[%s] @0x%p __real_popen\n", + SYS_POPEN_VERSION, __real_popen); +#if ARCH(Intel) + __real_posix_spawn_2_15 = dlvsym (dlflag, "posix_spawn", SYS_POSIX_SPAWN_VERSION); + __real_posix_spawnp_2_15 = dlvsym (dlflag, "posix_spawnp", SYS_POSIX_SPAWN_VERSION); +#if WSIZE(32) + __real_popen_2_1 = __real_popen; + __real_popen_2_0 = dlvsym (dlflag, "popen", "GLIBC_2.0"); + __real_posix_spawn_2_2 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2"); + __real_posix_spawnp_2_2 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2"); +#elif WSIZE(64) + __real_posix_spawn_2_2_5 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2.5"); + __real_posix_spawnp_2_2_5 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2.5"); +#endif /* WSIZE() */ +#endif /* ARCH(Intel) */ + __real_grantpt = dlsym (dlflag, "grantpt"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_grantpt\n", __real_grantpt); + __real_ptsname = dlsym (dlflag, "ptsname"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_ptsname\n", __real_ptsname); + __real_system = dlsym (dlflag, "system"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_system\n", __real_system); + __real_setuid = dlsym (dlflag, "setuid"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setuid\n", __real_setuid); + __real_seteuid = dlsym (dlflag, "seteuid"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_seteuid\n", __real_seteuid); + __real_setreuid = dlsym (dlflag, "setreuid"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setreuid\n", __real_setreuid); + __real_setgid = dlsym (dlflag, "setgid"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setgid\n", __real_setgid); + __real_setegid = dlsym (dlflag, "setegid"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setegid\n", __real_setegid); + __real_setregid = dlsym (dlflag, "setregid"); + TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setregid\n", __real_setregid); + return 0; +} + +/*------------------------------------------------------------------------ */ +/* Note: The following _prologue and _epilogue functions used to be dbx-visible. + + They are used to appropriately manage lineage-changing events, by + quiescing and re-enabling/re-setting experiment collection before and after, + and logging the lineage-change in the process/experiment undertaking it. + As shown by the interposition functions for fork, exec, etc., which follow, + the _prologue should be called immediately prior (such as a breakpoint + action defined at function entry) and the _epilogue called immediately + after (such as a breakpoint action defined at function return). + */ + +/* + Notes on MT from Solaris 10 man pthread_atfork: + + All multithreaded applications that call fork() in a POSIX + threads program and do more than simply call exec(2) in the + child of the fork need to ensure that the child is protected + from deadlock. + + Since the "fork-one" model results in duplicating only the + thread that called fork(), it is possible that at the time + of the call another thread in the parent owns a lock. This + thread is not duplicated in the child, so no thread will + unlock this lock in the child. Deadlock occurs if the sin- + gle thread in the child needs this lock. + + The problem is more serious with locks in libraries. Since + a library writer does not know if the application using the + library calls fork(), the library must protect itself from + such a deadlock scenario. If the application that links + with this library calls fork() and does not call exec() in + the child, and if it needs a library lock that may be held + by some other thread in the parent that is inside the + library at the time of the fork, the application deadlocks + inside the library. + */ + +static void +linetrace_ext_fork_prologue (const char *variant, char * n_lineage, int *following_fork) +{ + TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n", + variant, n_lineage, *following_fork); + __collector_env_print ("fork_prologue start"); + if (dbg_current_mode != FOLLOW_NONE) + TprintfT (DBG_LT0, "linetrace_ext_fork_prologue(%s) ERROR: dbg_current_mode=%d, changing to FOLLOW_FORK!\n", + variant, dbg_current_mode); + dbg_current_mode = FOLLOW_ON; + if (__collector_strncmp ((char *) variant, "clone", sizeof ("clone") - 1) == 0) + { + __collector_mutex_lock (&clone_lineage_lock); + CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_C%d", curr_lineage, ++clone_linenum); + __collector_mutex_unlock (&clone_lineage_lock); + } + else + { + __collector_mutex_lock (&fork_lineage_lock); + CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_f%d", curr_lineage, ++fork_linenum); + __collector_mutex_unlock (&fork_lineage_lock); + } + *following_fork = check_follow_fork (); + + /* write message before suspending, or it won't be written */ + hrtime_t ts = GETRELTIME (); + TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n", + variant, n_lineage, *following_fork); + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\"/>\n", + SP_JCMD_DESC_START, + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + variant, n_lineage, *following_fork); + __collector_ext_dispatcher_thread_timer_suspend (); + __collector_ext_hwc_lwp_suspend (); + __collector_env_print ("fork_prologue end"); +} + +static void +linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * n_lineage, int *following_fork) +{ + if (dbg_current_mode == FOLLOW_NONE) + TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) ERROR: dbg_current_mode=%d!\n", + variant, dbg_current_mode); + /* compute descendant experiment name */ + char new_exp_name[LT_MAXPATHLEN]; + /* save exp_name to global var */ + if (!build_experiment_path (new_exp_name, sizeof (new_exp_name), n_lineage)) + TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s): ERROR SP_COLLECTOR_EXPNAME not set\n", n_lineage); + TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s):%d() returned %d %s; child experiment name = %s\n", + variant, *following_fork, ret, (ret ? "parent" : "child"), new_exp_name); + if (ret == 0) + { + /* *************************************child */ + __collector_env_print ("fork_epilogue child at start"); + /* start a new line */ + fork_linenum = 0; + __collector_mutex_init (&fork_lineage_lock); + clone_linenum = 0; + __collector_mutex_init (&clone_lineage_lock); + __collector_env_update (NULL); + __collector_env_print ("fork_epilogue child after env_update"); + __collector_clean_state (); + __collector_env_print ("fork_epilogue child after clean_slate"); + __collector_line_cleanup (); + __collector_env_print ("fork_epilogue child after line_cleanup"); + if (*following_fork) + { + /* stop recording this experiment, but preserve env vars */ + linetrace_dormant (); + __collector_env_print ("fork_epilogue child after linetrace_dormant"); + + //static char exp_name_env[LT_MAXPATHLEN]; + char * exp_name_env = CALL_UTIL (calloc)(LT_MAXPATHLEN, 1); + CALL_UTIL (snprintf)(exp_name_env, LT_MAXPATHLEN, "%s=%s", SP_COLLECTOR_EXPNAME, new_exp_name); + CALL_UTIL (putenv)(exp_name_env); + + const char *params = CALL_UTIL (getenv)(SP_COLLECTOR_PARAMS); + int ret; + if (new_exp_name == NULL) + TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n", + SP_COLLECTOR_EXPNAME); + else if (params == NULL) + TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n", + SP_COLLECTOR_PARAMS); + else if ((ret = __collector_open_experiment (new_exp_name, params, SP_ORIGIN_FORK))) + TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: '%s' open failed, ret=%d\n", + new_exp_name, ret); + else + TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: opened(%s)\n", new_exp_name); + TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) returning to *child*\n", variant); + } + else + { + /* disable current and further linetrace experiment resumption */ + TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) child calling line_close\n", variant); + __collector_ext_line_close (); + } + __collector_env_print ("fork_epilogue child at end"); + /* *************************************end child */ + } + else + { + /* *************************************parent */ + __collector_env_print ("fork_epilogue parent at start"); + __collector_ext_dispatcher_thread_timer_resume (); + __collector_ext_hwc_lwp_resume (); + hrtime_t ts = GETRELTIME (); + char msg[256 + LT_MAXPATHLEN]; + if (ret >= 0) + CALL_UTIL (snprintf)(msg, sizeof (msg), "pid=%d", ret); + else + { + /* delete stillborn experiment? */ + char errmsg[256]; + strerror_r (errno, errmsg, sizeof (errmsg)); + CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg); + } + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n", + SP_JCMD_DESC_STARTED, + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + variant, n_lineage, *following_fork, msg); + /* environment remains set for collection */ + __collector_env_print ("fork_epilogue parent at end"); + /* *************************************end parent */ + } + dbg_current_mode = FOLLOW_NONE; + *following_fork = 0; +} + +static char** +linetrace_ext_exec_prologue_end (const char *variant, const char* cmd_string, + char *const envp[], int following_exec) +{ + char **coll_env; + TprintfT (DBG_LT0, "linetrace_ext_exec_prologue_end; variant=%s; cmd_string=%s; follow=%d\n", + variant, cmd_string, following_exec); + /* write message before suspending, or it won't be written */ + hrtime_t ts = GETRELTIME (); + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n", + SP_JCMD_EXEC_START, + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + variant, new_lineage, following_exec, cmd_string); + if (following_exec) + { + coll_env = __collector_env_allocate (envp, 0); + __collector_env_update (coll_env); + extern char **environ; /* the process' actual environment */ + if (environ == envp) /* user selected process environment */ + environ = coll_env; + } + else + coll_env = (char**) envp; + __collector_env_printall ("linetrace_ext_exec_prologue_end", coll_env); + if (!CALL_UTIL (strstr)(variant, "posix_spawn")) + { + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; + __collector_suspend_experiment ("suspend_for_exec"); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; + } + if (CALL_UTIL (strstr)(variant, "posix_spawn")) + { + __collector_ext_dispatcher_thread_timer_suspend (); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; + __collector_ext_hwc_lwp_suspend (); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; + } + return (coll_env); +} + +static char** +linetrace_ext_exec_prologue (const char *variant, + const char* path, char *const argv[], + char *const envp[], int *following_exec) +{ + char cmd_string[_POSIX_ARG_MAX] = {'\0'}; + + if (dbg_current_mode != FOLLOW_NONE) + TprintfT (DBG_LT0, "linetrace_ext_exec_prologue() ERROR: dbg_current_mode=%d, changing to FOLLOW_EXEC!\n", dbg_current_mode); + dbg_current_mode = FOLLOW_ON; + *following_exec = check_follow_exec (path); + if (path != NULL) + { + /* escape any newline, " or \ characters in the exec command */ + TprintfT (DBG_LT3, "linetrace_ext_exec_prologue(): arg0=%s\n", path); + /* leave space in log message for terminator (and header) */ + __collector_strlcpy (cmd_string, path, sizeof (cmd_string)); + size_t len; + unsigned argn = 0; + if (argv[0]) + { + char *p; + while (((p = argv[++argn]) != 0) && + (len = __collector_strlen (cmd_string)) < sizeof (cmd_string) - 2) + { + cmd_string[len++] = ' '; + __collector_strlcpy (cmd_string + len, p, sizeof (cmd_string) - len); + } + } + } + TprintfT (DBG_LT0, "linetrace_ext_exec_prologue(%s), lineage=%s, follow=%d, prog=%s, path=%s \n", + variant, new_lineage, *following_exec, cmd_string, path); + return linetrace_ext_exec_prologue_end (variant, cmd_string, envp, *following_exec); +} + +static void +linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec) +{ + /* For exec, this routine is only entered if the exec failed */ + /* However, posix_spawn() is expected to return */ + if (dbg_current_mode == FOLLOW_NONE) + TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: dbg_current_mode=%d!\n", dbg_current_mode); + TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue(%s):%d returned: %d, errno=%d\n", + variant, *following_exec, ret, errno); + if (!CALL_UTIL (strstr)(variant, "posix_spawn")) + { + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; + __collector_resume_experiment (); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; + } + if (CALL_UTIL (strstr)(variant, "posix_spawn")) + { + __collector_ext_dispatcher_thread_timer_resume (); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; + __collector_ext_hwc_lwp_resume (); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; + } + hrtime_t ts = GETRELTIME (); + char msg[256]; + if (ret) + { + char errmsg[256]; + strerror_r (errno, errmsg, sizeof (errmsg)); + CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg); + } + else + CALL_UTIL (snprintf)(msg, sizeof (msg), "rc=%d", ret); + if (!CALL_UTIL (strstr)(variant, "posix_spawn")) + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n", + SP_JCMD_EXEC_ERROR, + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + variant, new_lineage, *following_exec, msg); + if (envp == NULL) + TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: envp NULL after %s!\n", variant); + dbg_current_mode = FOLLOW_NONE; + *following_exec = 0; +} + +static void +linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo) +{ + char cmd_string[_POSIX_ARG_MAX] = {'\0'}; + char execfile[_POSIX_ARG_MAX] = {'\0'}; + + if (dbg_current_mode != FOLLOW_NONE) + TprintfT (DBG_LT0, "linetrace_ext_combo_prologue() ERROR: dbg_current_mode=%d! changing to FOLLOW_ON\n", + dbg_current_mode); + dbg_current_mode = FOLLOW_ON; + if (cmd != NULL) + { + /* extract executable name from combo command */ + unsigned len = strcspn (cmd, " "); + __collector_strlcpy (execfile, cmd, len + 1); + + /* escape any newline, " or \ characters in the combo command */ + /* leave space in log message for terminator (and header) */ + __collector_strlcpy (cmd_string, cmd, sizeof (cmd_string)); + } + + *following_combo = check_follow_combo (execfile); + TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(%s) follow=%d, prog=%s\n\n", + variant, *following_combo, cmd_string); + + /* Construct the lineage string for the new image */ + new_lineage[0] = 0; + __collector_strcat (new_lineage, "XXX"); + + /* write message before suspending, or it won't be written */ + hrtime_t ts = GETRELTIME (); + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n", + SP_JCMD_DESC_START, + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + variant, new_lineage, *following_combo, cmd_string); + if (*following_combo) + { + __collector_env_update (NULL); + TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(): Following %s(\"%s\")\n", variant, execfile); + } + __collector_ext_dispatcher_thread_timer_suspend (); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; + __collector_ext_hwc_lwp_suspend (); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; +} + +static void +linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo) +{ + if (dbg_current_mode == FOLLOW_NONE) + TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue() ERROR: dbg_current_mode=FOLLOW_NONE\n"); + TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue(%s):%d() returned %d\n", + variant, *following_combo, ret); + __collector_ext_dispatcher_thread_timer_resume (); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; + __collector_ext_hwc_lwp_resume (); + __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; + hrtime_t ts = GETRELTIME (); + __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" follow=\"%d\" msg=\"rc=%d\"/>\n", + SP_JCMD_DESC_STARTED, + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + variant, *following_combo, ret); + + dbg_current_mode = FOLLOW_NONE; + *following_combo = 0; +} + +/*------------------------------------------------------------- fork */ +pid_t fork () __attribute__ ((weak, alias ("__collector_fork"))); +pid_t _fork () __attribute__ ((weak, alias ("__collector_fork"))); + +pid_t +__collector_fork (void) +{ + pid_t ret; + if (NULL_PTR (fork)) + { + TprintfT (DBG_LT0, "__collector_fork() calling init_lineage_intf()\n"); + init_lineage_intf (); + } + __collector_env_print ("__collector_fork start"); + int * guard = NULL; + int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; + TprintfT (DBG_LT0, "__collector_fork() interposition: line_mode=%d combo=%d\n", + line_mode, combo_flag); + if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) + { + TprintfT (DBG_LT0, "__collector_fork() not following, returning CALL_REAL(fork)()\n"); + return CALL_REAL (fork)(); + } + int following_fork = 0; + linetrace_ext_fork_prologue ("fork", new_lineage, &following_fork); + + /* since libpthread/fork ends up calling fork1, it's a combo */ + PUSH_REENTRANCE (guard); + ret = CALL_REAL (fork)(); + POP_REENTRANCE (guard); + linetrace_ext_fork_epilogue ("fork", ret, new_lineage, &following_fork); + return ret; +} + +/*------------------------------------------------------------- vfork */ +/* vfork interposition in the usual sense is not possible, since vfork(2) + relies on specifics of the stack frames in the parent and child which + only work when the child's exec (or _exit) are in the same stack frame + as the vfork: this isn't the case when there's interposition on exec. + As a workaround, the interposing vfork calls fork1 instead of the real + vfork. Note that fork1 is somewhat less efficient than vfork, and requires + additional memory, which may result in a change of application behaviour + when libcollector is loaded (even when collection is not active), + affecting not only direct use of vfork by the subject application, + but also indirect use through system, popen, and the other combos. + */ +pid_t vfork () __attribute__ ((weak, alias ("__collector_vfork"))); +pid_t _vfork () __attribute__ ((weak, alias ("__collector_vfork"))); + +pid_t +__collector_vfork (void) +{ + if (NULL_PTR (vfork)) + init_lineage_intf (); + + int * guard = NULL; + int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; + + TprintfT (DBG_LT0, "__collector_vfork() interposing: line_mode=%d combo=%d\n", + line_mode, combo_flag); + if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) + return CALL_REAL (fork)(); + + /* this warning is also appropriate for combos which use vfork, + however, let's assume this is achieved elsewhere */ + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, + COL_WARN_VFORK, "fork"); + + char new_lineage[LT_MAXNAMELEN]; + new_lineage[0] = 0; + int following_fork = 0; + linetrace_ext_fork_prologue ("vfork", new_lineage, &following_fork); + + pid_t ret = CALL_REAL (fork)(); + linetrace_ext_fork_epilogue ("vfork", ret, new_lineage, &following_fork); + return ret; +} + +/*------------------------------------------------------------- execve */ +int execve () __attribute__ ((weak, alias ("__collector_execve"))); + +int +__collector_execve (const char* path, char *const argv[], char *const envp[]) +{ + static char **coll_env = NULL; /* environment for collection */ + if (NULL_PTR (execve)) + init_lineage_intf (); + int * guard = NULL; + int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; + TprintfT (DBG_LT0, + "__collector_execve(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n", + path ? path : "NULL", + argv ? (argv[0] ? argv[0] : "NULL") : "NULL", + envp ? (envp[0] ? envp[0] : "NULL") : "NULL", + line_mode, combo_flag); + if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ + __collector_env_unset ((char**) envp); + if (line_mode != LM_TRACK_LINEAGE || combo_flag) + return CALL_REAL (execve)(path, argv, envp); + + int following_exec = 0; + coll_env = linetrace_ext_exec_prologue ("execve", path, argv, envp, &following_exec); + TprintfT (DBG_LT2, "__collector_execve(): coll_env=0x%p\n", coll_env); + __collector_env_printall ("__collector_execve", coll_env); + int ret = CALL_REAL (execve)(path, argv, coll_env); + linetrace_ext_exec_epilogue ("execve", envp, ret, &following_exec); + return ret; +} + +int execvp () __attribute__ ((weak, alias ("__collector_execvp"))); + +int +__collector_execvp (const char* file, char *const argv[]) +{ + extern char **environ; /* the process' actual environment */ + char ** envp = environ; + if (NULL_PTR (execvp)) + init_lineage_intf (); + int * guard = NULL; + int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; + TprintfT (DBG_LT0, + "__collector_execvp(file=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n", + file ? file : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", + line_mode, combo_flag); + if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ + __collector_env_unset ((char**) envp); + if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) + return CALL_REAL (execvp)(file, argv); + + int following_exec = 0; +#ifdef DEBUG + char **coll_env = /* environment for collection */ +#endif /* DEBUG */ + linetrace_ext_exec_prologue ("execvp", file, argv, envp, &following_exec); + TprintfT (DBG_LT0, "__collector_execvp(): coll_env=0x%p\n", coll_env); + + int ret = CALL_REAL (execvp)(file, argv); + linetrace_ext_exec_epilogue ("execvp", envp, ret, &following_exec); + return ret; +} + +int execv () __attribute__ ((weak, alias ("__collector_execv"))); + +int +__collector_execv (const char* path, char *const argv[]) +{ + int ret; + extern char **environ; /* the process' actual environment */ + char ** envp = environ; + TprintfT (DBG_LT0, "__collector_execv(path=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n", + path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", + line_mode, get_combo_flag ()); + + ret = __collector_execve (path, argv, envp); + return ret; +} + +int execle (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execle"))); + +int +__collector_execle (const char* path, const char *arg0, ...) +{ + TprintfT (DBG_LT0, + "__collector_execle(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n", + path ? path : "NULL", arg0 ? arg0 : "NULL", + line_mode, get_combo_flag ()); + + char **argp; + va_list args; + char **argvec; + register char **environmentp; + int nargs = 0; + char *nextarg; + + va_start (args, arg0); + while (va_arg (args, char *) != (char *) 0) + nargs++; + + /* + * save the environment pointer, which is at the end of the + * variable argument list + */ + environmentp = va_arg (args, char **); + va_end (args); + + /* + * load the arguments in the variable argument list + * into the argument vector, and add the terminating null pointer + */ + va_start (args, arg0); + /* workaround for bugid 1242839 */ + argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *))); + argp = argvec; + *argp++ = (char *) arg0; + while ((nextarg = va_arg (args, char *)) != (char *) 0) + *argp++ = nextarg; + va_end (args); + *argp = (char *) 0; + return __collector_execve (path, argvec, environmentp); +} + +int execlp (const char* file, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execlp"))); + +int +__collector_execlp (const char* file, const char *arg0, ...) +{ + TprintfT (DBG_LT0, + "__collector_execlp(file=%s, arg0=%s) interposing: line_mode=%d combo=%d\n", + file ? file : "NULL", arg0 ? arg0 : "NULL", + line_mode, get_combo_flag ()); + char **argp; + va_list args; + char **argvec; + int nargs = 0; + char *nextarg; + + va_start (args, arg0); + while (va_arg (args, char *) != (char *) 0) + nargs++; + va_end (args); + + /* + * load the arguments in the variable argument list + * into the argument vector and add the terminating null pointer + */ + va_start (args, arg0); + + /* workaround for bugid 1242839 */ + argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *))); + argp = argvec; + *argp++ = (char *) arg0; + while ((nextarg = va_arg (args, char *)) != (char *) 0) + *argp++ = nextarg; + va_end (args); + *argp = (char *) 0; + return __collector_execvp (file, argvec); +} + +int execl (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execl"))); + +int +__collector_execl (const char* path, const char *arg0, ...) +{ + TprintfT (DBG_LT0, + "__collector_execl(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n", + path ? path : "NULL", arg0 ? arg0 : "NULL", + line_mode, get_combo_flag ()); + char **argp; + va_list args; + char **argvec; + extern char **environ; + int nargs = 0; + char *nextarg; + va_start (args, arg0); + while (va_arg (args, char *) != (char *) 0) + nargs++; + va_end (args); + + /* + * load the arguments in the variable argument list + * into the argument vector and add the terminating null pointer + */ + va_start (args, arg0); + + /* workaround for bugid 1242839 */ + argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *))); + argp = argvec; + *argp++ = (char *) arg0; + while ((nextarg = va_arg (args, char *)) != (char *) 0) + *argp++ = nextarg; + va_end (args); + *argp = (char *) 0; + return __collector_execve (path, argvec, environ); +} + +#include <spawn.h> + +/*-------------------------------------------------------- posix_spawn */ +#if ARCH(Intel) +// map interposed symbol versions +static int +__collector_posix_spawn_symver (int(real_posix_spawn) (), + pid_t *pidp, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]); + +int +__collector_posix_spawn_2_15 (pid_t *pidp, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]) +{ + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_15@%p(path=%s, argv[0]=%s, env[0]=%s)\n", + CALL_REAL (posix_spawn_2_15), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); + if (NULL_PTR (posix_spawn)) + init_lineage_intf (); + return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_15), pidp, + path, file_actions, attrp, argv, envp); +} + +__asm__(".symver __collector_posix_spawn_2_15,posix_spawn@@GLIBC_2.15"); + +#if WSIZE(32) +int +__collector_posix_spawn_2_2 (pid_t *pidp, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]) +{ + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_2@%p(path=%s, argv[0]=%s, env[0]=%s)\n", + CALL_REAL (posix_spawn_2_2), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); + if (NULL_PTR (posix_spawn)) + init_lineage_intf (); + return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_2), pidp, + path, file_actions, attrp, argv, envp); +} + +__asm__(".symver __collector_posix_spawn_2_2,posix_spawn@GLIBC_2.2"); + +#else /* ^WSIZE(32) */ +int +__collector_posix_spawn_2_2_5 (pid_t *pidp, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]) +{ + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_2_5@%p(path=%s, argv[0]=%s, env[0]=%s)\n", + CALL_REAL (posix_spawn_2_2_5), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); + if (NULL_PTR (posix_spawn)) + init_lineage_intf (); + return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_2_5), pidp, + path, file_actions, attrp, argv, envp); +} + +__asm__(".symver __collector_posix_spawn_2_2_5,posix_spawn@GLIBC_2.2.5"); +#endif /* ^WSIZE(32) */ + +static int +__collector_posix_spawn_symver (int(real_posix_spawn) (), +#else /* ^ARCH(Intel) */ +int +__collector_posix_spawn ( +#endif /* ARCH() */ + pid_t *pidp, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]) +{ + int ret; + static char **coll_env = NULL; /* environment for collection */ + if (NULL_PTR (posix_spawn)) + init_lineage_intf (); + if (NULL_PTR (posix_spawn)) + { + TprintfT (DBG_LT0, "__collector_posix_spawn(path=%s) interposing: ERROR, posix_spawn() not found by dlsym\n", + path ? path : "NULL"); + return -1; /* probably should set errno */ + } + int * guard = NULL; + int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; + TprintfT (DBG_LT0, "__collector_posix_spawn(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n", + path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL", line_mode, combo_flag); + if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ + __collector_env_unset ((char**) envp); + + if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) + { +#if ARCH(Intel) + return (real_posix_spawn) (pidp, path, file_actions, attrp, argv, envp); +#else + return CALL_REAL (posix_spawn)(pidp, path, file_actions, attrp, argv, envp); +#endif + } + int following_exec = 0; + coll_env = linetrace_ext_exec_prologue ("posix_spawn", path, argv, envp, &following_exec); + TprintfT (DBG_LT0, "__collector_posix_spawn(): coll_env=0x%p\n", coll_env); + __collector_env_printall ("__collector_posix_spawn", coll_env); + PUSH_REENTRANCE (guard); +#if ARCH(Intel) + ret = (real_posix_spawn) (pidp, path, file_actions, attrp, argv, coll_env); +#else + ret = CALL_REAL (posix_spawn)(pidp, path, file_actions, attrp, argv, coll_env); +#endif + POP_REENTRANCE (guard); + linetrace_ext_exec_epilogue ("posix_spawn", envp, ret, &following_exec); + return ret; +} + +/*-------------------------------------------------------- posix_spawnp */ +#if ARCH(Intel) +// map interposed symbol versions + +static int +__collector_posix_spawnp_symver (int(real_posix_spawnp) (), pid_t *pidp, + const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]); + +int // Common interposition +__collector_posix_spawnp_2_15 (pid_t *pidp, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]) +{ + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_15@%p(path=%s, argv[0]=%s, env[0]=%s)\n", + CALL_REAL (posix_spawnp_2_15), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); + if (NULL_PTR (posix_spawnp)) + init_lineage_intf (); + return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_15), pidp, + path, file_actions, attrp, argv, envp); +} + +__asm__(".symver __collector_posix_spawnp_2_15,posix_spawnp@@GLIBC_2.15"); + +#if WSIZE(32) + +int +__collector_posix_spawnp_2_2 (pid_t *pidp, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]) +{ + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_2@%p(path=%s, argv[0]=%s, env[0]=%s)\n", + CALL_REAL (posix_spawnp_2_2), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); + if (NULL_PTR (posix_spawnp)) + init_lineage_intf (); + return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_2), pidp, + path, file_actions, attrp, argv, envp); +} + +__asm__(".symver __collector_posix_spawnp_2_2,posix_spawnp@GLIBC_2.2"); + +#else /* ^WSIZE(32) */ +int +__collector_posix_spawnp_2_2_5 (pid_t *pidp, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]) +{ + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_2_5@%p(path=%s, argv[0]=%s, env[0]=%s)\n", + CALL_REAL (posix_spawnp_2_2_5), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL"); + if (NULL_PTR (posix_spawnp)) + init_lineage_intf (); + return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_2_5), pidp, + path, file_actions, attrp, argv, envp); +} + +__asm__(".symver __collector_posix_spawnp_2_2_5,posix_spawnp@GLIBC_2.2.5"); + +#endif /* ^WSIZE(32) */ + +static int +__collector_posix_spawnp_symver (int(real_posix_spawnp) (), +#else /* ^ARCH(Intel) */ +int +__collector_posix_spawnp ( +#endif /* ARCH() */ + pid_t *pidp, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]){ + int ret; + static char **coll_env = NULL; /* environment for collection */ + if (NULL_PTR (posix_spawnp)) + init_lineage_intf (); + if (NULL_PTR (posix_spawnp)) + { + TprintfT (DBG_LT0, "__collector_posix_spawnp(path=%s) interposing: ERROR, posix_spawnp() not found by dlsym\n", + path ? path : "NULL"); + return -1; /* probably should set errno */ + } + int * guard = NULL; + int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; + TprintfT (DBG_LT0, "__collector_posix_spawnp(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n", + path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", + envp ? (envp[0] ? envp[0] : "NULL") : "NULL", line_mode, combo_flag); + + if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ + __collector_env_unset ((char**) envp); + if (line_mode != LM_TRACK_LINEAGE || combo_flag) + { +#if ARCH(Intel) + return (real_posix_spawnp) (pidp, path, file_actions, attrp, argv, envp); +#else + return CALL_REAL (posix_spawnp)(pidp, path, file_actions, attrp, argv, envp); +#endif + } + int following_exec = 0; + coll_env = linetrace_ext_exec_prologue ("posix_spawnp", path, argv, envp, &following_exec); + TprintfT (DBG_LT0, "__collector_posix_spawnp(): coll_env=0x%p\n", coll_env); + __collector_env_printall ("__collector_posix_spawnp", coll_env); + PUSH_REENTRANCE (guard); +#if ARCH(Intel) + ret = (real_posix_spawnp) (pidp, path, file_actions, attrp, argv, coll_env); +#else + ret = CALL_REAL (posix_spawnp)(pidp, path, file_actions, attrp, argv, coll_env); +#endif + POP_REENTRANCE (guard); + linetrace_ext_exec_epilogue ("posix_spawnp", envp, ret, &following_exec); + return ret; +} + +/*------------------------------------------------------------- system */ +int system () __attribute__ ((weak, alias ("__collector_system"))); + +int +__collector_system (const char *cmd) +{ + if (NULL_PTR (system)) + init_lineage_intf (); + TprintfT (DBG_LT0, + "__collector_system(cmd=%s) interposing: line_mode=%d combo=%d\n", + cmd ? cmd : "NULL", line_mode, get_combo_flag ()); + int *guard = NULL; + if (line_mode == LM_TRACK_LINEAGE) + INIT_REENTRANCE (guard); + if (guard == NULL) + return CALL_REAL (system)(cmd); + int following_combo = 0; + linetrace_ext_combo_prologue ("system", cmd, &following_combo); + PUSH_REENTRANCE (guard); + int ret = CALL_REAL (system)(cmd); + POP_REENTRANCE (guard); + linetrace_ext_combo_epilogue ("system", ret, &following_combo); + return ret; +} + +/*------------------------------------------------------------- popen */ +// map interposed symbol versions +#if ARCH(Intel) && WSIZE(32) +static FILE * +__collector_popen_symver (FILE*(real_popen) (), const char *cmd, const char *mode); + +FILE * +__collector_popen_2_1 (const char *cmd, const char *mode) +{ + if (NULL_PTR (popen)) + init_lineage_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_popen_2_1@%p\n", CALL_REAL (popen_2_1)); + return __collector_popen_symver (CALL_REALF (popen_2_1), cmd, mode); +} + +FILE * +__collector_popen_2_0 (const char *cmd, const char *mode) +{ + if (NULL_PTR (popen)) + init_lineage_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_popen_2_0@%p\n", CALL_REAL (popen_2_0)); + return __collector_popen_symver (CALL_REALF (popen_2_0), cmd, mode); +} + +FILE * +__collector__popen_2_1 (const char *cmd, const char *mode) +{ + if (NULL_PTR (popen)) + init_lineage_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector__popen_2_1@%p\n", CALL_REAL (popen_2_1)); + return __collector_popen_symver (CALL_REALF (popen_2_1), cmd, mode); +} + +FILE * +__collector__popen_2_0 (const char *cmd, const char *mode) +{ + if (NULL_PTR (popen)) + init_lineage_intf (); + return __collector_popen_symver (CALL_REALF (popen_2_0), cmd, mode); +} + +__asm__(".symver __collector_popen_2_1,popen@@GLIBC_2.1"); +__asm__(".symver __collector_popen_2_0,popen@GLIBC_2.0"); +__asm__(".symver __collector__popen_2_1,_popen@@GLIBC_2.1"); +__asm__(".symver __collector__popen_2_0,_popen@GLIBC_2.0"); +#else // WSIZE(64) +FILE * popen () __attribute__ ((weak, alias ("__collector_popen"))); +#endif + +#if ARCH(Intel) && WSIZE(32) +static FILE * +__collector_popen_symver (FILE*(real_popen) (), const char *cmd, const char *mode) +#else + +FILE * +__collector_popen (const char *cmd, const char *mode) +#endif +{ + FILE *ret; + if (NULL_PTR (popen)) + init_lineage_intf (); + TprintfT (DBG_LT0, + "__collector_popen(cmd=%s) interposing: line_mode=%d combo=%d\n", + cmd ? cmd : "NULL", line_mode, get_combo_flag ()); + int *guard = NULL; + if (line_mode == LM_TRACK_LINEAGE) + INIT_REENTRANCE (guard); + if (guard == NULL) + { +#if ARCH(Intel) && WSIZE(32) + return (real_popen) (cmd, mode); +#else + return CALL_REALF (popen)(cmd, mode); +#endif + } + int following_combo = 0; + linetrace_ext_combo_prologue ("popen", cmd, &following_combo); + PUSH_REENTRANCE (guard); +#if ARCH(Intel) && WSIZE(32) + ret = (real_popen) (cmd, mode); +#else + ret = CALL_REALF (popen)(cmd, mode); +#endif + POP_REENTRANCE (guard); + linetrace_ext_combo_epilogue ("popen", (ret == NULL) ? (-1) : 0, &following_combo); + return ret; +} + +/*------------------------------------------------------------- grantpt */ +int grantpt () __attribute__ ((weak, alias ("__collector_grantpt"))); + +int +__collector_grantpt (const int fildes) +{ + if (NULL_PTR (grantpt)) + init_lineage_intf (); + TprintfT (DBG_LT0, + "__collector_grantpt(%d) interposing: line_mode=%d combo=%d\n", + fildes, line_mode, get_combo_flag ()); + int *guard = NULL; + if (line_mode == LM_TRACK_LINEAGE) + INIT_REENTRANCE (guard); + if (guard == NULL) + return CALL_REAL (grantpt)(fildes); + int following_combo = 0; + linetrace_ext_combo_prologue ("grantpt", "/usr/lib/pt_chmod", &following_combo); + PUSH_REENTRANCE (guard); + int ret = CALL_REAL (grantpt)(fildes); + POP_REENTRANCE (guard); + linetrace_ext_combo_epilogue ("grantpt", ret, &following_combo); + return ret; +} + +/*------------------------------------------------------------- ptsname */ +char *ptsname () __attribute__ ((weak, alias ("__collector_ptsname"))); + +char * +__collector_ptsname (const int fildes) +{ + if (NULL_PTR (ptsname)) + init_lineage_intf (); + TprintfT (DBG_LT0, + "__collector_ptsname(%d) interposing: line_mode=%d combo=%d\n", + fildes, line_mode, get_combo_flag ()); + int *guard = NULL; + if (line_mode == LM_TRACK_LINEAGE) + INIT_REENTRANCE (guard); + if (guard == NULL) + return CALL_REALC (ptsname)(fildes); + int following_combo = 0; + linetrace_ext_combo_prologue ("ptsname", "/usr/lib/pt_chmod", &following_combo); + PUSH_REENTRANCE (guard); + char *ret = CALL_REALC (ptsname)(fildes); + POP_REENTRANCE (guard); + linetrace_ext_combo_epilogue ("ptsname", (ret == NULL) ? (-1) : 1, &following_combo); + return ret; +} + +/*------------------------------------------------------------- clone */ +/* clone can be fork-like or pthread_create-like, depending on whether + * the flag CLONE_VM is set. If CLONE_VM is not set, then we interpose + * clone in the way similar to interposing fork; if CLONE_VM is set, + * then we interpose clone in the way similar to interposing pthread_create. + * One special case is not handled: when CLONE_VM is set but CLONE_THREAD + * is not, if the parent process exits earlier than the child process, + * experiment will close, losing data from child process. + */ +typedef struct __collector_clone_arg +{ + int (*fn)(void *); + void * arg; + char * new_lineage; + int following_fork; +} __collector_clone_arg_t; + +static int +__collector_clone_fn (void *fn_arg) +{ + int (*fn)(void *) = ((__collector_clone_arg_t*) fn_arg)->fn; + void * arg = ((__collector_clone_arg_t*) fn_arg)->arg; + char * new_lineage = ((__collector_clone_arg_t*) fn_arg)->new_lineage; + int following_fork = ((__collector_clone_arg_t*) fn_arg)->following_fork; + __collector_freeCSize (__collector_heap, fn_arg, sizeof (__collector_clone_arg_t)); + linetrace_ext_fork_epilogue ("clone", 0, new_lineage, &following_fork); + return fn (arg); +} + +int clone (int (*fn)(void *), void *, int, void *, ...) __attribute__ ((weak, alias ("__collector_clone"))); + +int +__collector_clone (int (*fn)(void *), void *child_stack, int flags, void *arg, + ... /* pid_t *ptid, struct user_desc *tls, pid_t *" ctid" */) +{ + int ret; + va_list va; + if (flags & CLONE_VM) + { + va_start (va, arg); + ret = __collector_ext_clone_pthread (fn, child_stack, flags, arg, va); + va_end (va); + } + else + { + if (NULL_PTR (clone)) + init_lineage_intf (); + int *guard = NULL; + int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; + TprintfT (DBG_LT0, "__collector_clone() interposition: line_mode=%d combo=%d\n", + line_mode, combo_flag); + char new_lineage[LT_MAXNAMELEN]; + int following_fork = 0; + __collector_clone_arg_t *funcinfo = __collector_allocCSize (__collector_heap, sizeof (__collector_clone_arg_t), 1); + (*funcinfo).fn = fn; + (*funcinfo).arg = arg; + (*funcinfo).new_lineage = new_lineage; + (*funcinfo).following_fork = 0; + pid_t * ptid = NULL; + struct user_desc * tls = NULL; + pid_t * ctid = NULL; + int num_args = 0; + va_start (va, arg); + if (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) + { + ptid = va_arg (va, pid_t *); + tls = va_arg (va, struct user_desc*); + ctid = va_arg (va, pid_t *); + num_args = 3; + } + else if (flags & CLONE_SETTLS) + { + ptid = va_arg (va, pid_t *); + tls = va_arg (va, struct user_desc*); + num_args = 2; + } + else if (flags & CLONE_PARENT_SETTID) + { + ptid = va_arg (va, pid_t *); + num_args = 1; + } + if ((line_mode != LM_TRACK_LINEAGE) || combo_flag || funcinfo == NULL) + { + switch (num_args) + { + case 3: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls, ctid); + break; + case 2: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls); + break; + case 1: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid); + break; + default: + ret = CALL_REAL (clone)(fn, child_stack, flags, arg); + break; + } + + va_end (va); + return ret; + } + linetrace_ext_fork_prologue ("clone", new_lineage, &following_fork); + (*funcinfo).following_fork = following_fork; + switch (num_args) + { + case 3: + ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls, ctid); + break; + case 2: + ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls); + break; + case 1: + ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid); + break; + default: + ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo); + break; + } + va_end (va); + if (ret < 0) + __collector_freeCSize (__collector_heap, funcinfo, sizeof (__collector_clone_arg_t)); + TprintfT (DBG_LT0, "__collector_clone() interposing: pid=%d\n", ret); + linetrace_ext_fork_epilogue ("clone", ret, new_lineage, &following_fork); + } + return ret; +} + +/*-------------------------------------------------------------------- setuid */ +int setuid () __attribute__ ((weak, alias ("__collector_setuid"))); +int _setuid () __attribute__ ((weak, alias ("__collector_setuid"))); + +int +__collector_setuid (uid_t ruid) +{ + if (NULL_PTR (setuid)) + init_lineage_intf (); + TprintfT (DBG_LT0, "__collector_setuid(0x%x) interposing\n", ruid); + check_reuid_change (ruid, -1); + int ret = CALL_REAL (setuid)(ruid); + TprintfT (DBG_LT0, "__collector_setuid(0x%x) returning %d\n", ruid, ret); + return ret; +} + +/*------------------------------------------------------------------- seteuid */ +int seteuid () __attribute__ ((weak, alias ("__collector_seteuid"))); +int _seteuid () __attribute__ ((weak, alias ("__collector_seteuid"))); + +int +__collector_seteuid (uid_t euid) +{ + if (NULL_PTR (seteuid)) + init_lineage_intf (); + TprintfT (DBG_LT0, "__collector_seteuid(0x%x) interposing\n", euid); + check_reuid_change (-1, euid); + int ret = CALL_REAL (seteuid)(euid); + TprintfT (DBG_LT0, "__collector_seteuid(0x%x) returning %d\n", euid, ret); + return ret; +} + +/*------------------------------------------------------------------ setreuid */ +int setreuid () __attribute__ ((weak, alias ("__collector_setreuid"))); +int _setreuid () __attribute__ ((weak, alias ("__collector_setreuid"))); + +int +__collector_setreuid (uid_t ruid, uid_t euid) +{ + if (NULL_PTR (setreuid)) + init_lineage_intf (); + TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) interposing\n", ruid, euid); + check_reuid_change (ruid, euid); + int ret = CALL_REAL (setreuid)(ruid, euid); + TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) returning %d\n", ruid, euid, ret); + return ret; +} + +/*-------------------------------------------------------------------- setgid */ +int setgid () __attribute__ ((weak, alias ("__collector_setgid"))); +int _setgid () __attribute__ ((weak, alias ("__collector_setgid"))); + +int +__collector_setgid (gid_t rgid) +{ + if (NULL_PTR (setgid)) + init_lineage_intf (); + TprintfT (DBG_LT0, "__collector_setgid(0x%x) interposing\n", rgid); + check_regid_change (rgid, -1); + int ret = CALL_REAL (setgid)(rgid); + TprintfT (DBG_LT0, "__collector_setgid(0x%x) returning %d\n", rgid, ret); + return ret; +} + +/*------------------------------------------------------------------- setegid */ +int setegid () __attribute__ ((weak, alias ("__collector_setegid"))); +int _setegid () __attribute__ ((weak, alias ("__collector_setegid"))); + +int +__collector_setegid (gid_t egid) +{ + if (NULL_PTR (setegid)) + init_lineage_intf (); + TprintfT (DBG_LT0, "__collector_setegid(0x%x) interposing\n", egid); + check_regid_change (-1, egid); + int ret = CALL_REAL (setegid)(egid); + TprintfT (DBG_LT0, "__collector_setegid(0x%x) returning %d\n", egid, ret); + return ret; +} + +/*------------------------------------------------------------------ setregid */ +int setregid () __attribute__ ((weak, alias ("__collector_setregid"))); +int _setregid () __attribute__ ((weak, alias ("__collector_setregid"))); + +int +__collector_setregid (gid_t rgid, gid_t egid) +{ + if (NULL_PTR (setregid)) + init_lineage_intf (); + TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) interposing\n", rgid, egid); + check_regid_change (rgid, egid); + int ret = CALL_REAL (setregid)(rgid, egid); + TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) returning %d\n", rgid, egid, ret); + return ret; +} + +/*------------------------------------------------------- selective following */ + +static int +linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *progname) +{ + regex_t regex_desc; + if (!follow_spec) + { + TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES NULL follow_spec\n"); + return 1; + } + int ercode = regcomp (®ex_desc, follow_spec, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + if (ercode) + { + // syntax error in parsing string +#ifdef DEBUG + char errbuf[256]; + regerror (ercode, ®ex_desc, errbuf, sizeof (errbuf)); + TprintfT (DBG_LT0, "linetrace_follow_experiment: regerror()=%s\n", errbuf); +#endif + return 1; + } + TprintfT (DBG_LT0, "linetrace_follow_experiment(): compiled spec='%s'\n", follow_spec); + if (lineage_str) + { + if (!regexec (®ex_desc, lineage_str, 0, NULL, 0)) + { + TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES lineage (follow_spec=%s,lineage=%s)\n", + follow_spec, lineage_str); + return 1; + } + } + if (progname) + { + if (!regexec (®ex_desc, progname, 0, NULL, 0)) + { + TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES progname (follow_spec=%s,progname=%s)\n", + follow_spec, progname); + return 1; + } + } + TprintfT (DBG_LT0, "linetrace_follow_experiment(): DOES NOT MATCH (follow_spec=%s,lineage=%s,progname=%s)\n", + follow_spec, lineage_str ? lineage_str : "NULL", + progname ? progname : "NULL"); + return 0; +} diff --git a/gprofng/libcollector/mapfile.aarch64-Linux b/gprofng/libcollector/mapfile.aarch64-Linux new file mode 100644 index 0000000..84d6bbc --- /dev/null +++ b/gprofng/libcollector/mapfile.aarch64-Linux @@ -0,0 +1,40 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +GLIBC_2.17 { + global: + posix_spawn; + posix_spawnp; + pthread_mutex_lock; + pthread_mutex_unlock; + pthread_join; + sem_wait; + pthread_create; + dlopen; + popen; + timer_create; + pthread_cond_wait; + pthread_cond_timedwait; + fopen; + fclose; + fdopen; + fgetpos; + fsetpos; +}; diff --git a/gprofng/libcollector/mapfile.amd64-Linux b/gprofng/libcollector/mapfile.amd64-Linux new file mode 100644 index 0000000..213061f --- /dev/null +++ b/gprofng/libcollector/mapfile.amd64-Linux @@ -0,0 +1,79 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +GLIBC_2.2.5 { + global: + posix_spawn; + posix_spawnp; + pthread_mutex_lock; + pthread_mutex_unlock; + pthread_join; + sem_wait; + pthread_create; + dlopen; + popen; + timer_create; + pthread_cond_wait; + pthread_cond_timedwait; + fopen; + fclose; + fdopen; + fgetpos; + fsetpos; +}; + +GLIBC_2.3.2 { + global: + pthread_cond_wait; + pthread_cond_timedwait; +}; + +GLIBC_2.3.3 { + global: + timer_create; +}; + +GLIBC_2.15 { + global: + posix_spawn; + posix_spawnp; +}; + +GLIBC_2.17 { + global: + posix_spawn; + posix_spawnp; + pthread_mutex_lock; + pthread_mutex_unlock; + pthread_join; + sem_wait; + pthread_create; + dlopen; + popen; + timer_create; + pthread_cond_wait; + pthread_cond_timedwait; + fopen; + fclose; + fdopen; + fgetpos; + fsetpos; +}; + diff --git a/gprofng/libcollector/mapfile.intel-Linux b/gprofng/libcollector/mapfile.intel-Linux new file mode 100644 index 0000000..97482d2 --- /dev/null +++ b/gprofng/libcollector/mapfile.intel-Linux @@ -0,0 +1,81 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +GLIBC_2.0 { + global: + pthread_mutex_lock; + pthread_mutex_unlock; + pthread_join; + pthread_create; + dlopen; + popen; + sem_wait; + pthread_cond_wait; + pthread_cond_timedwait; + fopen; + fclose; + fdopen; + fgetpos; + fsetpos; +}; + +GLIBC_2.1 { + global: + sem_wait; + pthread_create; + dlopen; + open64; + pread; + pwrite; + pwrite64; + popen; + fopen; + fclose; + fdopen; + fgetpos64; + fsetpos64; +}; + +GLIBC_2.2 { + global: + open64; + posix_spawn; + posix_spawnp; + pread; + pwrite; + pwrite64; + timer_create; + fgetpos; + fsetpos; + fgetpos64; + fsetpos64; +}; + +GLIBC_2.3.2 { + global: + pthread_cond_wait; + pthread_cond_timedwait; +}; + +GLIBC_2.15 { + global: + posix_spawn; + posix_spawnp; +}; diff --git a/gprofng/libcollector/mapfile.sparc-Linux b/gprofng/libcollector/mapfile.sparc-Linux new file mode 100644 index 0000000..30d3953 --- /dev/null +++ b/gprofng/libcollector/mapfile.sparc-Linux @@ -0,0 +1,40 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +GLIBC_2.0 { + global: + pthread_mutex_lock; + pthread_mutex_unlock; + pthread_join; +}; + +GLIBC_2.1 { + global: + sem_wait; + pthread_create; + dlopen; + popen; +}; + +GLIBC_2.3.2 { + global: + pthread_cond_wait; + pthread_cond_timedwait; +}; diff --git a/gprofng/libcollector/mapfile.sparcv9-Linux b/gprofng/libcollector/mapfile.sparcv9-Linux new file mode 100644 index 0000000..db79766 --- /dev/null +++ b/gprofng/libcollector/mapfile.sparcv9-Linux @@ -0,0 +1,58 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +GLIBC_2.0 { + global: + dlopen; +}; + +GLIBC_2.1 { + global: + dlopen; +}; + +GLIBC_2.2 { + global: + pthread_create; + popen; + pthread_mutex_lock; + pthread_mutex_unlock; + pthread_join; + sem_wait; + pthread_cond_wait; + pthread_cond_timedwait; + timer_create; + fopen; + fclose; + fdopen; + fgetpos; + fsetpos; +}; + +GLIBC_2.3.2 { + global: + pthread_cond_wait; + pthread_cond_timedwait; +}; + +GLIBC_2.3.3 { + global: + timer_create; +}; diff --git a/gprofng/libcollector/memmgr.c b/gprofng/libcollector/memmgr.c new file mode 100644 index 0000000..5ada421 --- /dev/null +++ b/gprofng/libcollector/memmgr.c @@ -0,0 +1,396 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <sys/mman.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "collector.h" +#include "libcol_util.h" +#include "gp-experiment.h" +#include "memmgr.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 +#define DBG_LT4 4 + +/* + * Memory allocation. + * + * Heap: + * chain[0] - linked list of chunks; + * chain[1] - linked list of free 16-byte objects; + * chain[2] - linked list of free 32-byte objects; + * ... + * + * Chunk: + * + * base lo hi + * V V V + * +------------------+---------+-------------------+--+--+-----+ + * | Var size object | -> <-| Const size objects| | |Chunk| + * +------------------+---------+-------------------+--+--+-----+ + * + * Limitations: + * - one var size object per chunk + * - can't allocate const size objects larger than 2^MAXCHAIN + */ + +#define MAXCHAIN 32 +#define ALIGNMENT 4 /* 2^ALIGNMENT == minimal size and alignment */ +#define ALIGN(x) ((((x) - 1)/(1 << ALIGNMENT) + 1) * (1 << ALIGNMENT)) + +struct Heap +{ + collector_mutex_t lock; /* master lock */ + void *chain[MAXCHAIN]; /* chain[0] - chunks */ + /* chain[i] - structs of size 2^i */ +}; + +typedef struct Chunk +{ + size_t size; + char *base; + char *lo; + char *hi; + struct Chunk *next; +} Chunk; + +static void +not_implemented () +{ + __collector_log_write ("<event kind=\"%s\" id=\"%d\">error memmgr not_implemented()</event>\n", + SP_JCMD_CERROR, COL_ERROR_NOZMEM); + return; +} + +/* + * void __collector_mmgr_init_mutex_locks( Heap *heap ) + * Iinitialize mmgr mutex locks. + */ +void +__collector_mmgr_init_mutex_locks (Heap *heap) +{ + if (heap == NULL) + return; + if (__collector_mutex_trylock (&heap->lock)) + { + /* + * We are in a child process immediately after the fork(). + * Parent process was in the middle of critical section when the fork() happened. + * This is a placeholder for the cleanup. + * See CR 6997020 for details. + */ + __collector_mutex_init (&heap->lock); + } + __collector_mutex_init (&heap->lock); +} + +/* + * alloc_chunk( unsigned sz ) allocates a chunk of at least sz bytes. + * If sz == 0, allocates a chunk of the default size. + */ +static Chunk * +alloc_chunk (unsigned sz, int log) +{ + static long pgsz = 0; + char *ptr; + Chunk *chnk; + size_t chunksz; + if (pgsz == 0) + { + pgsz = CALL_UTIL (sysconf)(_SC_PAGESIZE); + Tprintf (DBG_LT2, "memmgr: pgsz = %ld (0x%lx)\n", pgsz, pgsz); + } + /* Allocate 2^n >= sz bytes */ + unsigned nsz = ALIGN (sizeof (Chunk)) + sz; + for (chunksz = pgsz; chunksz < nsz; chunksz *= 2); + if (log == 1) + Tprintf (DBG_LT2, "alloc_chunk mapping %u, rounded up from %u\n", (unsigned int) chunksz, sz); + /* mmap64 is only in 32-bits; this call goes to mmap in 64-bits */ + ptr = (char*) CALL_UTIL (mmap64)(0, chunksz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, (int) -1, (off64_t) 0); + if (ptr == MAP_FAILED) + { + Tprintf (0, "alloc_chunk mapping failed COL_ERROR_NOZMEMMAP: %s\n", CALL_UTIL (strerror)(errno)); + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_NOZMEMMAP, errno, "0"); + return NULL; + } + /* Put the chunk descriptor at the end of the chunk */ + chnk = (Chunk*) (ptr + chunksz - ALIGN (sizeof (Chunk))); + chnk->size = chunksz; + chnk->base = ptr; + chnk->lo = chnk->base; + chnk->hi = (char*) chnk; + chnk->next = (Chunk*) NULL; + if (log == 1) + Tprintf (DBG_LT2, "memmgr: returning new chunk @%p, chunksx=%ld sz=%ld\n", + ptr, (long) chunksz, (long) sz); + return chnk; +} + +Heap * +__collector_newHeap () +{ + Heap *heap; + Chunk *chnk; + Tprintf (DBG_LT2, "__collector_newHeap calling alloc_chunk(0)\n"); + chnk = alloc_chunk (0, 1); + if (chnk == NULL) + return NULL; + + /* A bit of hackery: allocate heap from its own chunk */ + chnk->hi -= ALIGN (sizeof (Heap)); + heap = (Heap*) chnk->hi; + heap->chain[0] = (void*) chnk; + __collector_mutex_init (&heap->lock); + return heap; +} + +void +__collector_deleteHeap (Heap *heap) +{ + if (heap == NULL) + return; + /* Note: heap itself is in the last chunk */ + for (Chunk *chnk = heap->chain[0]; chnk;) + { + Chunk *next = chnk->next; + CALL_UTIL (munmap)((void*) chnk->base, chnk->size); + chnk = next; + } +} + +void * +__collector_allocCSize (Heap *heap, unsigned sz, int log) +{ + void *res; + Chunk *chnk; + if (heap == NULL) + return NULL; + + /* block all signals and acquire lock */ + sigset_t old_mask, new_mask; + CALL_UTIL (sigfillset)(&new_mask); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask); + __collector_mutex_lock (&heap->lock); + + /* Allocate nsz = 2^idx >= sz bytes */ + unsigned idx = ALIGNMENT; + unsigned nsz = 1 << idx; + while (nsz < sz) + nsz = 1 << ++idx; + + /* Look in the corresponding chain first */ + if (idx < MAXCHAIN) + { + if (heap->chain[idx] != NULL) + { + res = heap->chain[idx]; + heap->chain[idx] = *(void**) res; + __collector_mutex_unlock (&heap->lock); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + if (log == 1) + Tprintf (DBG_LT2, "memmgr: allocCSize %p sz %d (0x%x) req = 0x%x, from chain idx = %d\n", res, nsz, nsz, sz, idx); + return res; + } + } + else + { + not_implemented (); + __collector_mutex_unlock (&heap->lock); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + return NULL; + } + + /* Chain is empty, allocate from chunks */ + for (chnk = (Chunk*) heap->chain[0]; chnk; chnk = chnk->next) + if (chnk->lo + nsz < chnk->hi) + break; + if (chnk == NULL) + { + /* Get a new chunk */ + if (log == 1) + Tprintf (DBG_LT2, "__collector_allocCSize (%u) calling alloc_chunk(%u)\n", sz, nsz); + chnk = alloc_chunk (nsz, 1); + if (chnk == NULL) + { + __collector_mutex_unlock (&heap->lock); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + return NULL; + } + chnk->next = (Chunk*) heap->chain[0]; + heap->chain[0] = chnk; + } + + /* Allocate from the chunk */ + chnk->hi -= nsz; + res = (void*) chnk->hi; + __collector_mutex_unlock (&heap->lock); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + if (log == 1) + Tprintf (DBG_LT2, "memmgr: allocCSize %p sz %d (0x%x) req = 0x%x, new chunk\n", res, nsz, nsz, sz); + return res; +} + +void +__collector_freeCSize (Heap *heap, void *ptr, unsigned sz) +{ + if (heap == NULL || ptr == NULL) + return; + + /* block all signals and acquire lock */ + sigset_t old_mask, new_mask; + CALL_UTIL (sigfillset)(&new_mask); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask); + __collector_mutex_lock (&heap->lock); + + /* Free 2^idx >= sz bytes */ + unsigned idx = ALIGNMENT; + unsigned nsz = 1 << idx; + while (nsz < sz) + nsz = 1 << ++idx; + if (idx < MAXCHAIN) + { + *(void**) ptr = heap->chain[idx]; + heap->chain[idx] = ptr; + } + else + not_implemented (); + __collector_mutex_unlock (&heap->lock); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + Tprintf (DBG_LT4, "memmgr: freeC %p sz %ld\n", ptr, (long) sz); +} + +static void * +allocVSize_nolock (Heap *heap, unsigned sz) +{ + void *res; + Chunk *chnk; + if (sz == 0) + return NULL; + + /* Find a good chunk */ + for (chnk = (Chunk*) heap->chain[0]; chnk; chnk = chnk->next) + if (chnk->lo == chnk->base && chnk->lo + sz < chnk->hi) + break; + if (chnk == NULL) + { + /* Get a new chunk */ + Tprintf (DBG_LT2, "allocVsize_nolock calling alloc_chunk(%u)\n", sz); + chnk = alloc_chunk (sz, 0); + if (chnk == NULL) + return NULL; + chnk->next = (Chunk*) heap->chain[0]; + heap->chain[0] = chnk; + } + chnk->lo = chnk->base + sz; + res = (void*) (chnk->base); + Tprintf (DBG_LT4, "memmgr: allocV %p for %ld\n", res, (long) sz); + return res; +} + +void * +__collector_allocVSize (Heap *heap, unsigned sz) +{ + void *res; + if (heap == NULL) + return NULL; + + /* block all signals and acquire lock */ + sigset_t old_mask, new_mask; + CALL_UTIL (sigfillset)(&new_mask); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask); + __collector_mutex_lock (&heap->lock); + res = allocVSize_nolock (heap, sz); + __collector_mutex_unlock (&heap->lock); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + return res; +} + +/* + * reallocVSize( Heap *heap, void *ptr, unsigned newsz ) + * Changes the size of memory pointed by ptr to newsz. + * If ptr == NULL, allocates new memory of size newsz. + * If newsz == 0, frees ptr and returns NULL. + */ +void * +__collector_reallocVSize (Heap *heap, void *ptr, unsigned newsz) +{ + Chunk *chnk; + void *res; + if (heap == NULL) + return NULL; + if (ptr == NULL) + return __collector_allocVSize (heap, newsz); + + /* block all signals and acquire lock */ + sigset_t old_mask, new_mask; + CALL_UTIL (sigfillset)(&new_mask); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask); + __collector_mutex_lock (&heap->lock); + + /* Find its chunk */ + for (chnk = (Chunk*) heap->chain[0]; chnk; chnk = chnk->next) + if (ptr == chnk->base) + break; + if (chnk == NULL) + { + /* memory corrpution */ + not_implemented (); + __collector_mutex_unlock (&heap->lock); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + return NULL; + } + if (chnk->base + newsz < chnk->hi) + { + /* easy case */ + chnk->lo = chnk->base + newsz; + res = newsz ? chnk->base : NULL; + __collector_mutex_unlock (&heap->lock); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + Tprintf (DBG_LT4, "memmgr: reallocV %p for %ld\n", ptr, (long) newsz); + return res; + } + res = allocVSize_nolock (heap, newsz); + /* Copy to new location */ + if (res) + { + int size = chnk->lo - chnk->base; + if (newsz < size) + size = newsz; + char *s1 = (char*) res; + char *s2 = chnk->base; + while (size--) + *s1++ = *s2++; + } + /* Free old memory*/ + chnk->lo = chnk->base; + __collector_mutex_unlock (&heap->lock); + CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL); + return res; +} diff --git a/gprofng/libcollector/memmgr.h b/gprofng/libcollector/memmgr.h new file mode 100644 index 0000000..78231c2 --- /dev/null +++ b/gprofng/libcollector/memmgr.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _MEMMGR_H +#define _MEMMGR_H + +struct Heap; +typedef struct Heap Heap; + +Heap *__collector_newHeap (); +void __collector_deleteHeap (Heap *heap); + +/* + * Initialize memmgr mutex locks. + */ +void __collector_mmgr_init_mutex_locks (Heap *heap); + +/* + * Allocate non-resizable memory. + */ +void *__collector_allocCSize (Heap *heap, unsigned sz, int log); + +/* + * Free non-resizable memory. + */ +void __collector_freeCSize (Heap *heap, void *ptr, unsigned sz); + +/* + * Allocate resizable memory + */ +void *__collector_allocVSize (Heap *heap, unsigned sz); + +/* + * Change size of resizable memory. + * ptr - if not NULL, it must have been previously allocated from + * the same heap, otherwise returns allocVSize(heap, newsz); + * newsz - new size; if 0, memory is freed and no new allocation + * occurs; + */ +void *__collector_reallocVSize (Heap *heap, void *ptr, unsigned newsz); + +#endif diff --git a/gprofng/libcollector/mmaptrace.c b/gprofng/libcollector/mmaptrace.c new file mode 100644 index 0000000..ac5c997 --- /dev/null +++ b/gprofng/libcollector/mmaptrace.c @@ -0,0 +1,1691 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * memory map tracking + * incorporating former "loadobjects" into more general "map" + * (including code and data segments and dynamic functions) + */ + +#include "config.h" +#include <alloca.h> +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <elf.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <stdint.h> + +#include "gp-defs.h" +#include "collector.h" +#include "gp-experiment.h" +#include "memmgr.h" + +/* + * These are obsolete and unreliable. + * They are included here only for historical compatibility. + */ +#define MA_SHARED 0x08 /* changes are shared by mapped object */ +#define MA_ANON 0x40 /* anonymous memory (e.g. /dev/zero) */ +#define MA_ISM 0x80 /* intimate shared mem (shared MMU resources) */ +#define MA_BREAK 0x10 /* grown by brk(2) */ +#define MA_STACK 0x20 /* grown automatically on stack faults */ + +typedef struct prmap_t +{ + unsigned long pr_vaddr; /* virtual address of mapping */ + unsigned long pr_size; /* size of mapping in bytes */ + char *pr_mapname; /* name in /proc/<pid>/object */ + int pr_mflags; /* protection and attribute flags (see below) */ + unsigned long pr_offset; /* offset into mapped object, if any */ + unsigned long pr_dev; + unsigned long pr_ino; + int pr_pagesize; /* pagesize (bytes) for this mapping */ +} prmap_t; + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 +#define DBG_LT4 4 + +#define SYS_MMAP_NAME "mmap" +#define SYS_MMAP64_NAME "mmap64" +#define SYS_MUNMAP_NAME "munmap" +#define SYS_DLOPEN_NAME "dlopen" +#define SYS_DLCLOSE_NAME "dlclose" + +typedef struct MapInfo +{ + struct MapInfo *next; + unsigned long vaddr; + unsigned long size; + char *mapname; /* name in /proc/<pid>/object */ + char *filename; + unsigned long offset; + int mflags; + int pagesize; +} MapInfo; + +typedef struct NameInfo +{ + struct NameInfo *next; + char *mapname; + char filename[1]; /* dynamic length file name */ +} NameInfo; + +static NameInfo *namemaps = NULL; +static MapInfo mmaps; /* current memory maps */ +static struct DataHandle *map_hndl = NULL; +static char dyntext_fname[MAXPATHLEN]; +static void *mapcache = NULL; +static char *maptext = NULL; +static size_t maptext_sz = 4096; /* initial buffer size */ +static int mmap_mode = 0; +static int mmap_initted = 0; +static collector_mutex_t map_lock = COLLECTOR_MUTEX_INITIALIZER; +static collector_mutex_t dyntext_lock = COLLECTOR_MUTEX_INITIALIZER; + +/* a reentrance guard for the interposition functions ensures that updates to + the map cache/file are sequential, with the first doing the final update */ +static int reentrance = 0; +#define CHCK_REENTRANCE (reentrance || mmap_mode <= 0) +#define CURR_REENTRANCE reentrance +#define PUSH_REENTRANCE reentrance++ +#define POP_REENTRANCE reentrance-- + +#define CALL_REAL(x) (__real_##x) +#define NULL_PTR(x) (__real_##x == NULL) + +/* interposition function handles */ +static void *(*__real_mmap)(void* start, size_t length, int prot, int flags, + int fd, off_t offset) = NULL; +static void *(*__real_mmap64)(void* start, size_t length, int prot, int flags, + int fd, off64_t offset) = NULL; +static int (*__real_munmap)(void* start, size_t length) = NULL; +static void *(*__real_dlopen)(const char* pathname, int mode) = NULL; +#if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) +static void *(*__real_dlopen_2_1)(const char* pathname, int mode) = NULL; +static void *(*__real_dlopen_2_0)(const char* pathname, int mode) = NULL; +#endif +static int (*__real_dlclose)(void* handle) = NULL; +static void (*collector_heap_record)(int, size_t, void*) = NULL; + +/* internal function prototypes */ +static int init_mmap_intf (); +static int init_mmap_files (); +static void append_segment_record (char *format, ...); +static void update_map_segments (hrtime_t hrt, int resolve); +static void resolve_mapname (MapInfo *map, char *name); +static void record_segment_map (hrtime_t timestamp, uint64_t loadaddr, + unsigned long msize, int pagesize, int modeflags, + long long offset, unsigned check, char *name); +static void record_segment_unmap (hrtime_t timestamp, uint64_t loadaddr); + +/* Linux needs handling of the vsyscall page to get its data into the map.xml file */ +static void process_vsyscall_page (); + +#define MAXVSYSFUNCS 10 +static int nvsysfuncs = 0; +static char *sysfuncname[MAXVSYSFUNCS]; +static uint64_t sysfuncvaddr[MAXVSYSFUNCS]; +static unsigned long sysfuncsize[MAXVSYSFUNCS]; + +#define MAXDYN 20 +static int ndyn = 0; +static char *dynname [MAXDYN]; +static void *dynvaddr [MAXDYN]; +static unsigned dynsize [MAXDYN]; +static char *dynfuncname[MAXDYN]; + +/*===================================================================*/ + +/* + * void __collector_mmap_init_mutex_locks() + * Iinitialize mmap mutex locks. + */ +void +__collector_mmap_init_mutex_locks () +{ + __collector_mutex_init (&map_lock); + __collector_mutex_init (&dyntext_lock); +} + +/* __collector_ext_update_map_segments called by the audit agent + * Is is also called by dbx/collector when a (possible) map update + * is intimated, such as after dlopen/dlclose. + * Required when libcollector.so is not preloaded and interpositions inactive. + */ +int +__collector_ext_update_map_segments (void) +{ + if (!mmap_initted) + return 0; + TprintfT (0, "__collector_ext_update_map_segments(%d)\n", CURR_REENTRANCE); + if (CHCK_REENTRANCE) + return 0; + PUSH_REENTRANCE; + update_map_segments (GETRELTIME (), 1); + POP_REENTRANCE; + return 0; +} +/* + * int __collector_ext_mmap_install() + * Install and initialise mmap tracing. + */ +int +__collector_ext_mmap_install (int record) +{ + TprintfT (0, "__collector_ext_mmap_install(mmap_mode=%d)\n", mmap_mode); + if (NULL_PTR (mmap)) + { + if (init_mmap_intf ()) + { + TprintfT (0, "ERROR: collector mmap tracing initialization failed.\n"); + return COL_ERROR_EXPOPEN; + } + } + else + TprintfT (DBG_LT2, "collector mmap tracing: mmap pointer not null\n"); + + /* Initialize side door interface with the heap tracing module */ + collector_heap_record = (void(*)(int, size_t, void*))dlsym (RTLD_DEFAULT, "__collector_heap_record"); + if (record) + { + map_hndl = __collector_create_handle (SP_MAP_FILE); + if (map_hndl == NULL) + return COL_ERROR_MAPOPEN; + if (init_mmap_files ()) + { + TprintfT (0, "ERROR: collector init_mmap_files() failed.\n"); + return COL_ERROR_EXPOPEN; + } + } + mmaps.next = NULL; + mapcache = NULL; + PUSH_REENTRANCE; + update_map_segments (GETRELTIME (), 1); // initial map + POP_REENTRANCE; + mmap_mode = 1; + mmap_initted = 1; + process_vsyscall_page (); + return COL_ERROR_NONE; +} + +/* + * int __collector_ext_mmap_deinstall() + * Optionally update final map and stop tracing mmap events. + */ +int +__collector_ext_mmap_deinstall (int update) +{ + if (!mmap_initted) + return COL_ERROR_NONE; + mmap_mode = 0; + if (update) + { + /* Final map */ + PUSH_REENTRANCE; + update_map_segments (GETRELTIME (), 1); + POP_REENTRANCE; + } + TprintfT (0, "__collector_ext_mmap_deinstall(%d)\n", update); + if (map_hndl != NULL) + { + __collector_delete_handle (map_hndl); + map_hndl = NULL; + } + __collector_mutex_lock (&map_lock); // get lock before resetting + + /* Free all memory maps */ + MapInfo *mp; + for (mp = mmaps.next; mp;) + { + MapInfo *next = mp->next; + __collector_freeCSize (__collector_heap, mp, sizeof (*mp)); + mp = next; + } + mmaps.next = NULL; + + /* Free all name maps */ + NameInfo *np; + for (np = namemaps; np;) + { + NameInfo *next = np->next; + __collector_freeCSize (__collector_heap, np, sizeof (*np) + __collector_strlen (np->filename)); + np = next; + } + namemaps = NULL; + mapcache = __collector_reallocVSize (__collector_heap, mapcache, 0); + mmaps.next = NULL; + mapcache = NULL; + __collector_mutex_unlock (&map_lock); + TprintfT (0, "__collector_ext_mmap_deinstall done\n"); + return 0; +} + +/* + * void __collector_mmap_fork_child_cleanup() + * Perform all necessary cleanup steps in child process after fork(). + */ +void +__collector_mmap_fork_child_cleanup () +{ + /* Initialize all mmap "mutex" locks */ + __collector_mmap_init_mutex_locks (); + if (!mmap_initted) + return; + mmap_mode = 0; + __collector_delete_handle (map_hndl); + __collector_mutex_lock (&map_lock); // get lock before resetting + + /* Free all memory maps */ + MapInfo *mp; + for (mp = mmaps.next; mp;) + { + MapInfo *next = mp->next; + __collector_freeCSize (__collector_heap, mp, sizeof (*mp)); + mp = next; + } + mmaps.next = NULL; + + /* Free all name maps */ + NameInfo *np; + for (np = namemaps; np;) + { + NameInfo *next = np->next; + __collector_freeCSize (__collector_heap, np, sizeof (*np) + __collector_strlen (np->filename)); + np = next; + } + namemaps = NULL; + mapcache = __collector_reallocVSize (__collector_heap, mapcache, 0); + mmap_initted = 0; + reentrance = 0; + __collector_mutex_unlock (&map_lock); +} + +static int +init_mmap_files () +{ + TprintfT (DBG_LT2, "init_mmap_files\n"); + /* also create the headerless dyntext file (if required) */ + CALL_UTIL (snprintf)(dyntext_fname, sizeof (dyntext_fname), "%s/%s", + __collector_exp_dir_name, SP_DYNTEXT_FILE); + if (CALL_UTIL (access)(dyntext_fname, F_OK) != 0) + { + int fd = CALL_UTIL (open)(dyntext_fname, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) + { + char errmsg[256]; + TprintfT (0, "ERROR: init_mmap_files: open(%s) failed\n", + dyntext_fname); + __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s: %s</event>\n", + SP_JCMD_CERROR, COL_ERROR_DYNOPEN, errno, + dyntext_fname, errmsg); + return COL_ERROR_DYNOPEN; + } + else + CALL_UTIL (close)(fd); + } + return COL_ERROR_NONE; +} + +static void +append_segment_record (char *format, ...) +{ + char buf[1024]; + char *bufptr = buf; + va_list va; + va_start (va, format); + int sz = __collector_xml_vsnprintf (bufptr, sizeof (buf), format, va); + va_end (va); + + if (__collector_expstate != EXP_OPEN && __collector_expstate != EXP_PAUSED) + { + TprintfT (0, "append_segment_record: expt neither open nor paused (%d); " + "not writing to map.xml\n\t%s", __collector_expstate, buf); + return; + } + if (sz >= sizeof (buf)) + { + /* Allocate a new buffer */ + sz += 1; /* add the terminating null byte */ + bufptr = (char*) alloca (sz); + va_start (va, format); + sz = __collector_xml_vsnprintf (bufptr, sz, format, va); + va_end (va); + } + int rc = __collector_write_string (map_hndl, bufptr, sz); + if (rc != 0) + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\"></event>\n", + SP_JCMD_CERROR, COL_ERROR_MAPWRITE); +} + +static void +record_segment_map (hrtime_t timestamp, uint64_t loadaddr, unsigned long msize, + int pagesize, int modeflags, long long offset, + unsigned check, char *name) +{ + + TprintfT (DBG_LT2, "record_segment_map(%s @ 0x%llx)\n", name, (long long) loadaddr); + append_segment_record ("<event kind=\"map\" object=\"segment\" tstamp=\"%u.%09u\" " + "vaddr=\"0x%016llX\" size=\"%lu\" pagesz=\"%d\" foffset=\"%c0x%08llX\" " + "modes=\"0x%03X\" chksum=\"0x%0X\" name=\"%s\"/>\n", + (unsigned) (timestamp / NANOSEC), + (unsigned) (timestamp % NANOSEC), + loadaddr, msize, pagesize, + offset < 0 ? '-' : '+', offset < 0 ? -offset : offset, + modeflags, check, name); +} + +static void +record_segment_unmap (hrtime_t timestamp, uint64_t loadaddr) +{ + TprintfT (DBG_LT2, "record_segment_unmap(@ 0x%llx)\n", (long long) loadaddr); + append_segment_record ("<event kind=\"unmap\" tstamp=\"%u.%09u\" vaddr=\"0x%016llX\"/>\n", + (unsigned) (timestamp / NANOSEC), + (unsigned) (timestamp % NANOSEC), loadaddr); +} + +#if WSIZE(64) +#define ELF_EHDR Elf64_Ehdr +#define ELF_PHDR Elf64_Phdr +#define ELF_SHDR Elf64_Shdr +#define ELF_DYN Elf64_Dyn +#define ELF_AUX Elf64_auxv_t +#define ELF_SYM Elf64_Sym +#define ELF_ST_BIND ELF64_ST_BIND +#define ELF_ST_TYPE ELF64_ST_TYPE +#elif WSIZE(32) +#define ELF_EHDR Elf32_Ehdr +#define ELF_PHDR Elf32_Phdr +#define ELF_SHDR Elf32_Shdr +#define ELF_DYN Elf32_Dyn +#define ELF_AUX Elf32_auxv_t +#define ELF_SYM Elf32_Sym +#define ELF_ST_BIND ELF32_ST_BIND +#define ELF_ST_TYPE ELF32_ST_TYPE +#endif + +static unsigned +checksum_mapname (MapInfo* map) +{ + unsigned checksum = 0; + /* only checksum code segments */ + if ((map->mflags & (PROT_EXEC | PROT_READ)) == 0 || + (map->mflags & PROT_WRITE) != 0) + return 0; + checksum = (unsigned) - 1; + TprintfT (DBG_LT2, "checksum_mapname checksum = 0x%0X\n", checksum); + return checksum; +} + + +#if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) +static void* +dlopen_searchpath_symver (void*(real_dlopen) (), void* caller_addr, const char* basename, int mode) +#else +static void* +dlopen_searchpath (void* caller_addr, const char* basename, int mode) +#endif +{ + TprintfT (DBG_LT2, "dlopen_searchpath(%p, %s, %d)\n", caller_addr, basename, mode); + Dl_info dl_info; + if (dladdr (caller_addr, &dl_info) == 0) + { + TprintfT (0, "ERROR: dladdr(%p): %s\n", caller_addr, dlerror ()); + return 0; + } + TprintfT (DBG_LT2, "dladdr(%p): %p fname=%s\n", + caller_addr, dl_info.dli_fbase, dl_info.dli_fname); + int noload = RTLD_BINDING_MASK | RTLD_NOLOAD; //XXXX why RTLD_BINDING_MASK? +#define WORKAROUND_RTLD_BUG 1 +#ifdef WORKAROUND_RTLD_BUG + // A dynamic linker dlopen bug can result in corruption/closure of open streams + // XXXX workaround should be removed once linker patches are all available +#if WSIZE(64) +#define MAINBASE 0x400000 +#elif WSIZE(32) +#define MAINBASE 0x08048000 +#endif + const char* tmp_path = + (dl_info.dli_fbase == (void*) MAINBASE) ? NULL : dl_info.dli_fname; + void* caller_hndl = NULL; +#if ((ARCH(Intel) && WSIZE(32)) || ARCH(SPARC)) + caller_hndl = (real_dlopen) (tmp_path, noload); +#else + caller_hndl = CALL_REAL (dlopen)(tmp_path, noload); +#endif + +#else //XXXX workaround should be removed once linker patches are all available + + void* caller_hndl = NULL; +#if (ARCH(Intel) && WSIZE(32) || ARCH(SPARC) + caller_hndl = (real_dlopen) (dl_info.dli_fname, noload); +#else + caller_hndl = CALL_REAL (dlopen)(dl_info.dli_fname, noload); +#endif + +#endif //XXXX workaround should be removed once linker patches are all available + + if (!caller_hndl) + { + TprintfT (0, "ERROR: dlopen(%s,NOLOAD): %s\n", dl_info.dli_fname, dlerror ()); + return 0; + } + Dl_serinfo _info, *info = &_info; + Dl_serpath *path; + + /* determine search path count and required buffer size */ + dlinfo (caller_hndl, RTLD_DI_SERINFOSIZE, (void *) info); + + /* allocate new buffer and initialize */ + /* + CR# 7191331 + There is a bug in Linux that causes the first call + to dlinfo() to return a small value for the dls_size. + + The first call to dlinfo() determines the search path + count and the required buffer size. The second call to + dlinfo() tries to obtain the search path information. + + However, the size of the buffer that is returned by + the first call to the dlinfo() is incorrect (too small). + The second call to dlinfo() uses the incorrect size to + allocate memory on the stack and internally uses the memcpy() + function to copy the search paths to the allocated memory space. + The length of the search path is much larger than the buffer + that is allocated on the stack. The memcpy() overwrites some + of the information that are saved on the stack, specifically, + it overwrites the "basename" parameter. + + collect crashes right after the second call to dlinfo(). + + The search paths are used to locate the shared libraries. + dlinfo() creates the search paths based on the paths + that are assigned to LD_LIBRARY_PATH environment variable + and the standard library paths. The standard library paths + consists of the /lib and the /usr/lib paths. The + standard library paths are always included to the search + paths by dlinfo() even if the LD_LIBRARY_PATH environment + variable is not defined. Therefore, at the very least the + dls_cnt is assigned to 2 (/lib and /usr/lib) and dlinfo() + will never assign dls_cnt to zero. The dls_cnt is the count + of the potential paths for searching the shared libraries. + + So we need to increase the buffer size before the second + call to dlinfo(). There are number of ways to increase + the buffer size. However, none of them can calculate the + buffer size precisely. Some users on the web have suggested + to multiply the MAXPATHLEN by dls_cnt for the buffer size. + The MAXPATHLEN is assigned to 1024 bytes. In my opinion + this is too much. So I have decided to multiply dls_size + by dls_cnt for the buffer size since the dls_size is much + smaller than 1024 bytes. + + I have already confirmed with our user that the workaround + is working with his real application. Additionally, + the dlopen_searchpath() function is called only by the + libcorrector init() function when the experiment is started. + Therefore, allocating some extra bytes on the stack which + is local to this routine is harmless. + */ + + info = alloca (_info.dls_size * _info.dls_cnt); + info->dls_size = _info.dls_size; + info->dls_cnt = _info.dls_cnt; + + /* obtain search path information */ + dlinfo (caller_hndl, RTLD_DI_SERINFO, (void *) info); + path = &info->dls_serpath[0]; + + char pathname[MAXPATHLEN]; + for (unsigned int cnt = 1; cnt <= info->dls_cnt; cnt++, path++) + { + __collector_strlcpy (pathname, path->dls_name, sizeof (pathname)); + __collector_strlcat (pathname, "/", sizeof (pathname)); + __collector_strlcat (pathname, basename, sizeof (pathname)); + void* ret = NULL; +#if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) + ret = (real_dlopen) (pathname, mode); +#else + ret = CALL_REAL (dlopen)(pathname, mode); +#endif + TprintfT (DBG_LT2, "try %d/%d: %s = %p\n", cnt, info->dls_cnt, pathname, ret); + if (ret) + return ret; // success! + } + return 0; +} + +static void +resolve_mapname (MapInfo *map, char *name) +{ + map->filename = ""; + map->mapname = ""; + if (name == NULL || *name == '\0') + { + if (map->mflags & MA_STACK) + map->filename = "<" SP_MAP_STACK ">"; + else if (map->mflags & MA_BREAK) + map->filename = "<" SP_MAP_HEAP ">"; + else if (map->mflags & MA_ISM) + map->filename = "<" SP_MAP_SHMEM ">"; + return; + } + NameInfo *np; + for (np = namemaps; np; np = np->next) + if (__collector_strcmp (np->mapname, name) == 0) + break; + + if (np == NULL) + { + const char *fname; + fname = name; + /* Create and link a new name map */ + size_t fnamelen = __collector_strlen (fname) + 1; + np = (NameInfo*) __collector_allocCSize (__collector_heap, sizeof (NameInfo) + fnamelen, 1); + if (np == NULL) // We could not get memory + return; + np->mapname = np->filename; + __collector_strlcpy (np->filename, fname, fnamelen); + np->next = namemaps; + namemaps = np; + } + map->mapname = np->mapname; + map->filename = np->filename; + if (map->filename[0] == (char) 0) + map->filename = map->mapname; + TprintfT (DBG_LT2, "resolve_mapname: %s resolved to %s\n", map->mapname, map->filename); +} + +static unsigned long +str2ulong (char **ss) +{ + char *s = *ss; + unsigned long val = 0UL; + const int base = 16; + for (;;) + { + char c = *s++; + if (c >= '0' && c <= '9') + val = val * base + (c - '0'); + else if (c >= 'a' && c <= 'f') + val = val * base + (c - 'a') + 10; + else if (c >= 'A' && c <= 'F') + val = val * base + (c - 'A') + 10; + else + break; + } + *ss = s - 1; + return val; +} + +static void +update_map_segments (hrtime_t hrt, int resolve) +{ + size_t filesz; + if (__collector_mutex_trylock (&map_lock)) + { + TprintfT (0, "WARNING: update_map_segments(resolve=%d) BUSY\n", resolve); + return; + } + TprintfT (DBG_LT2, "\n"); + TprintfT (DBG_LT2, "begin update_map_segments(hrt, %d)\n", resolve); + + // Note: there is similar code to read /proc/$PID/map[s] in + // perfan/er_kernel/src/KSubExp.cc KSubExp::write_subexpt_map() + const char* proc_map = "/proc/self/maps"; + size_t bufsz = maptext_sz; + int done = 0; + filesz = 0; + int map_fd = CALL_UTIL (open)(proc_map, O_RDONLY); + while (!done) + { + bufsz *= 2; + maptext = __collector_reallocVSize (__collector_heap, maptext, bufsz); + TprintfT (DBG_LT2, " update_map_segments: Loop for bufsize=%ld\n", + (long) bufsz); + for (;;) + { + int n = CALL_UTIL (read)(map_fd, maptext + filesz, bufsz - filesz); + TprintfT (DBG_LT2, " update_map_segments: __collector_read(bufp=%p nbyte=%ld)=%d\n", + maptext + filesz, (long) ( bufsz - filesz), n); + if (n < 0) + { + TprintfT (0, "ERROR: update_map_segments: read(maps): errno=%d\n", errno); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_MAPREAD, errno, proc_map); + CALL_UTIL (close)(map_fd); + __collector_mutex_unlock (&map_lock); + return; + } + else if (n == 0) + { + done = 1; + break; + } + filesz += n; + if (filesz >= bufsz) /* Buffer too small */ + break; + } + } + CALL_UTIL (close)(map_fd); + maptext_sz = filesz; + + int mapcache_entries = 0; + char *str, *str1; + for (str = maptext;; str = str1) + { + for (str1 = str; str1 - maptext < filesz; str1++) + { + if (*str1 == '\n') + { + *str1 = (char) 0; + break; + } + } + if (str1 - maptext >= filesz) + break; + str1++; + mapcache_entries++; + mapcache = __collector_reallocVSize (__collector_heap, mapcache, + sizeof (prmap_t) * mapcache_entries); + prmap_t *map = ((prmap_t *) mapcache) + (mapcache_entries - 1); + map->pr_vaddr = str2ulong (&str); + str++; + unsigned long eaddr = str2ulong (&str); + str++; + map->pr_size = eaddr - map->pr_vaddr; + map->pr_mflags = 0; + map->pr_mflags += (*str++ == 'r' ? PROT_READ : 0); + map->pr_mflags += (*str++ == 'w' ? PROT_WRITE : 0); + map->pr_mflags += (*str++ == 'x' ? PROT_EXEC : 0); + map->pr_mflags += (*str++ == 's' ? MA_SHARED : 0); + str++; + map->pr_offset = str2ulong (&str); + str++; + map->pr_dev = str2ulong (&str) * 0x100; + str++; + map->pr_dev += str2ulong (&str); + str++; + map->pr_ino = str2ulong (&str); + if (map->pr_dev == 0) + map->pr_mflags |= MA_ANON; + while (*str == ' ') + str++; + map->pr_mapname = str; + map->pr_pagesize = 4096; + } + + /* Compare two maps and record all differences */ + unsigned nidx = 0; + MapInfo *prev = &mmaps; + MapInfo *oldp = mmaps.next; + for (;;) + { + prmap_t *newp = nidx < mapcache_entries ? + (prmap_t*) mapcache + nidx : NULL; + if (oldp == NULL && newp == NULL) + break; + + /* If two maps are equal proceed to the next pair */ + if (oldp && newp && + oldp->vaddr == newp->pr_vaddr && + oldp->size == newp->pr_size && + __collector_strcmp (oldp->mapname, newp->pr_mapname) == 0) + { + prev = oldp; + oldp = oldp->next; + nidx++; + continue; + } + /* Check if we need to unload the old map first */ + if (newp == NULL || (oldp && oldp->vaddr <= newp->pr_vaddr)) + { + if (oldp != NULL) + { + /* Don't record MA_ANON maps except MA_STACK and MA_BREAK */ + if ((!(oldp->mflags & MA_ANON) || (oldp->mflags & (MA_STACK | MA_BREAK)))) + record_segment_unmap (hrt, oldp->vaddr); + /* Remove and free map */ + prev->next = oldp->next; + MapInfo *tmp = oldp; + oldp = oldp->next; + __collector_freeCSize (__collector_heap, tmp, sizeof (*tmp)); + } + } + else + { + MapInfo *map = (MapInfo*) __collector_allocCSize (__collector_heap, sizeof (MapInfo), 1); + if (map == NULL) + { + __collector_mutex_unlock (&map_lock); + return; + } + map->vaddr = newp->pr_vaddr; + map->size = newp->pr_size; + map->offset = newp->pr_offset; + map->mflags = newp->pr_mflags; + map->pagesize = newp->pr_pagesize; + resolve_mapname (map, newp->pr_mapname); + + /* Insert new map */ + map->next = prev->next; + prev->next = map; + prev = map; + + /* Don't record MA_ANON maps except MA_STACK and MA_BREAK */ + if (!(newp->pr_mflags & MA_ANON) || (newp->pr_mflags & (MA_STACK | MA_BREAK))) + { + unsigned checksum = checksum_mapname (map); + record_segment_map (hrt, map->vaddr, map->size, + map->pagesize, map->mflags, + map->offset, checksum, map->filename); + } + nidx++; + } + } + TprintfT (DBG_LT2, "update_map_segments: done\n\n"); + __collector_mutex_unlock (&map_lock); +} /* update_map_segments */ + +/* + * Map addr to a segment. Cope with split segments. + */ +int +__collector_check_segment_internal (unsigned long addr, unsigned long *base, + unsigned long *end, int maxnretries, int MA_FLAGS) +{ + int number_of_tries = 0; +retry: + ; + + unsigned long curbase = 0; + unsigned long curfoff = 0; + unsigned long cursize = 0; + + MapInfo *mp; + for (mp = mmaps.next; mp; mp = mp->next) + { + + if (curbase + cursize == mp->vaddr && + curfoff + cursize == mp->offset && + ((mp->mflags & MA_FLAGS) == MA_FLAGS + || __collector_strncmp (mp->mapname, "[vdso]", 6) == 0 + || __collector_strncmp (mp->mapname, "[vsyscall]", 10) == 0 + )) + cursize = mp->vaddr + mp->size - curbase; + else if (addr < mp->vaddr) + break; + else if ((mp->mflags & MA_FLAGS) != MA_FLAGS + && __collector_strncmp (mp->mapname, "[vdso]", 6) + && __collector_strncmp (mp->mapname, "[vsyscall]", 10)) + { + curbase = 0; + curfoff = 0; + cursize = 0; + } + else + { + curbase = mp->vaddr; + curfoff = mp->offset; + cursize = mp->size; + } + } + + if (addr >= curbase && addr < curbase + cursize) + { + *base = curbase; + *end = curbase + cursize; + return 1; + } + + /* + * 21275311 Unwind failure in native stack for java application running on jdk8 on x86 + * + * On JDK8, we've observed cases where Java-compiled methods end up + * in virtual address segments that were "dead zones" (mflags&PROT_READ==0) at + * the time of the last update_map_segments() but are now "live". So if we + * fail to find a segment, let's call update_map_segments and then retry + * before giving up. + */ + if (number_of_tries < maxnretries) + { + number_of_tries++; + __collector_ext_update_map_segments (); + goto retry; + } + *base = 0; + *end = 0; + return 0; +} + +/** + * Check if address belongs to a readable and executable segment + * @param addr + * @param base + * @param end + * @param maxnretries + * @return 1 - yes, 0 - no + */ +int +__collector_check_segment (unsigned long addr, unsigned long *base, + unsigned long *end, int maxnretries) +{ + int MA_FLAGS = PROT_READ | PROT_EXEC; + int res = __collector_check_segment_internal (addr, base, end, maxnretries, MA_FLAGS); + return res; +} + +/** + * Check if address belongs to a readable segment + * @param addr + * @param base + * @param end + * @param maxnretries + * @return 1 - yes, 0 - no + */ +int +__collector_check_readable_segment( unsigned long addr, unsigned long *base, unsigned long *end, int maxnretries ) +{ + int MA_FLAGS = PROT_READ; + int res = __collector_check_segment_internal(addr, base, end, maxnretries, MA_FLAGS); + return res; +} + +static ELF_AUX *auxv = NULL; + +static void +process_vsyscall_page () +{ + TprintfT (DBG_LT2, "process_vsyscall_page()\n"); + if (ndyn != 0) + { + /* We've done this one in this process, and cached the results */ + /* use the cached results */ + for (int i = 0; i < ndyn; i++) + { + append_segment_record ("<event kind=\"map\" object=\"dynfunc\" name=\"%s\" " + "vaddr=\"0x%016lX\" size=\"%u\" funcname=\"%s\" />\n", + dynname[i], dynvaddr[i], dynsize[i], dynfuncname[i]); + TprintfT (DBG_LT2, "process_vsyscall_page: append_segment_record map dynfunc='%s' vaddr=0x%016lX size=%ld funcname='%s' -- from cache\n", + dynname[i], (unsigned long) dynvaddr[i], + (long) dynsize[i], dynfuncname[i]); + } + } + if (nvsysfuncs != 0) + { + /* We've done this one in this process, and cached the results */ + /* use the cached results */ + hrtime_t hrt = GETRELTIME (); + for (int i = 0; i < nvsysfuncs; i++) + { + append_segment_record ("<event kind=\"map\" object=\"function\" tstamp=\"%u.%09u\" " + "vaddr=\"0x%016lX\" size=\"%u\" name=\"%s\" />\n", + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), + (unsigned long) sysfuncvaddr[i], (unsigned) sysfuncsize[i], sysfuncname[i]); + TprintfT (DBG_LT2, "process_vsyscall_page: append_segment_record map function='%s' vaddr=0x%016lX size=%ld -- from cache\n", + sysfuncname[i], (unsigned long) sysfuncvaddr[i], (long) sysfuncsize[i]); + } + } + if (ndyn + nvsysfuncs != 0) + return; + + /* After fork we can't rely on environ as it might have + * been moved by putenv(). Use the pointer saved by the parent. + */ + if (auxv == NULL) + { + char **envp = (char**) environ; + if (envp == NULL) + return; + while (*envp++ != NULL); + auxv = (ELF_AUX*) envp; + } + TprintfT (DBG_LT2, "process_vsyscall_page, auxv = ox%p\n", auxv); + + ELF_AUX *ap; +#ifdef DEBUG + for (ap = auxv; ap->a_type != AT_NULL; ap++) + TprintfT (DBG_LT2, "process_vsyscall_page: ELF_AUX: " + " a_type = 0x%016llx %10lld " + " a_un.a_val = 0x%016llx %10lld\n", + (long long) ap->a_type, (long long) ap->a_type, + (long long) ap->a_un.a_val, (long long) ap->a_un.a_val); +#endif + + // find the first ELF_AUX of type AT_SYSINFO_EHDR + ELF_EHDR *ehdr = NULL; + for (ap = auxv; ap->a_type != AT_NULL; ap++) + { + if (ap->a_type == AT_SYSINFO_EHDR) + { + // newer Linuxes do not have a_ptr field, they just have a_val + ehdr = (ELF_EHDR*) ap->a_un.a_val; + if (ehdr != NULL) + break; + } + } + + // If one is found + if (ehdr != NULL) + { + char *mapName = "SYSINFO_EHDR"; + MapInfo *mp; + for (mp = mmaps.next; mp; mp = mp->next) + { + if ((unsigned long) ehdr == mp->vaddr) + { + mp->mflags |= PROT_EXEC; + if (mp->mapname && mp->mapname[0]) + mapName = mp->mapname; + break; + } + } + + // Find the dynsym section and record all symbols + char *base = (char*) ehdr; + ELF_SHDR *shdr = (ELF_SHDR*) (base + ehdr->e_shoff); + int i; + +#if 0 + TprintfT (DBG_LT2, "process_vsyscall_page: ehdr: EI_CLASS=%lld EI_DATA=%lld EI_OSABI=%lld e_type=%lld e_machine=%lld e_version=%lld\n" + " e_entry =0x%016llx %10lld e_phoff =0x%016llx %10lld\n" + " e_shoff =0x%016llx %10lld e_flags =0x%016llx %10lld\n" + " e_ehsize =0x%016llx %10lld e_phentsize =0x%016llx %10lld\n" + " e_phnum =0x%016llx %10lld e_shentsize =0x%016llx %10lld\n" + " e_shnum =0x%016llx %10lld e_shstrndx =0x%016llx %10lld\n", + (long long) ehdr->e_ident[EI_CLASS], (long long) ehdr->e_ident[EI_DATA], (long long) ehdr->e_ident[EI_OSABI], + (long long) ehdr->e_type, (long long) ehdr->e_machine, (long long) ehdr->e_version, + (long long) ehdr->e_entry, (long long) ehdr->e_entry, + (long long) ehdr->e_phoff, (long long) ehdr->e_phoff, + (long long) ehdr->e_shoff, (long long) ehdr->e_shoff, + (long long) ehdr->e_flags, (long long) ehdr->e_flags, + (long long) ehdr->e_ehsize, (long long) ehdr->e_ehsize, + (long long) ehdr->e_phentsize, (long long) ehdr->e_phentsize, + (long long) ehdr->e_phnum, (long long) ehdr->e_phnum, + (long long) ehdr->e_shentsize, (long long) ehdr->e_shentsize, + (long long) ehdr->e_shnum, (long long) ehdr->e_shnum, + (long long) ehdr->e_shstrndx, (long long) ehdr->e_shstrndx); + for (i = 1; i < ehdr->e_shnum; i++) + { + TprintfT (DBG_LT2, "process_vsyscall_page: SECTION=%d sh_name=%lld '%s'\n" + " sh_type =0x%016llx %10lld\n" + " sh_flags =0x%016llx %10lld\n" + " sh_addr =0x%016llx %10lld\n" + " sh_offset =0x%016llx %10lld\n" + " sh_size =0x%016llx %10lld\n" + " sh_link =0x%016llx %10lld\n" + " sh_info =0x%016llx %10lld\n" + " sh_addralign =0x%016llx %10lld\n" + " sh_entsize =0x%016llx %10lld\n", + i, (long long) shdr[i].sh_name, base + shdr[ehdr->e_shstrndx].sh_offset + shdr[i].sh_name, + (long long) shdr[i].sh_type, (long long) shdr[i].sh_type, + (long long) shdr[i].sh_flags, (long long) shdr[i].sh_flags, + (long long) shdr[i].sh_addr, (long long) shdr[i].sh_addr, + (long long) shdr[i].sh_offset, (long long) shdr[i].sh_offset, + (long long) shdr[i].sh_size, (long long) shdr[i].sh_size, + (long long) shdr[i].sh_link, (long long) shdr[i].sh_link, + (long long) shdr[i].sh_info, (long long) shdr[i].sh_info, + (long long) shdr[i].sh_addralign, (long long) shdr[i].sh_addralign, + (long long) shdr[i].sh_entsize, (long long) shdr[i].sh_entsize); + } +#endif + + int dynSec = -1; + for (i = 1; i < ehdr->e_shnum; i++) + if (shdr[i].sh_type == SHT_DYNSYM) + { + dynSec = i; + break; + } + if (dynSec != -1) + { + char *symbase = base + shdr[shdr[dynSec].sh_link].sh_offset; + ELF_SYM *symbols = (ELF_SYM*) (base + shdr[dynSec].sh_offset); + int nextSec = 0; + int n = shdr[dynSec].sh_size / shdr[dynSec].sh_entsize; + for (i = 0; i < n; i++) + { + ELF_SYM *sym = symbols + i; + TprintfT (DBG_LT2, "process_vsyscall_page: symbol=%d st_name=%lld '%s'\n" + " st_size = 0x%016llx %10lld\n" + " st_value = 0x%016llx %10lld\n" + " st_shndx = 0x%016llx %10lld\n" + " st_info = 0x%016llx %10lld\n", + i, (long long) sym->st_name, symbase + sym->st_name, + (long long) sym->st_size, (long long) sym->st_size, + (long long) sym->st_value, (long long) sym->st_value, + (long long) sym->st_shndx, (long long) sym->st_shndx, + (long long) sym->st_info, (long long) sym->st_info); + if (sym->st_shndx <= 0 || sym->st_size <= 0 || + ELF_ST_BIND (sym->st_info) != STB_GLOBAL || ELF_ST_TYPE (sym->st_info) != STT_FUNC) + continue; + if (nextSec == 0) + nextSec = sym->st_shndx; + else if (nextSec > sym->st_shndx) + nextSec = sym->st_shndx; + } + if (nextSec == 0) + ehdr = NULL; + + while (nextSec != 0) + { + int curSec = nextSec; + char *bgn = base + shdr[curSec].sh_offset; + char *end = bgn + shdr[curSec].sh_size; + for (i = 0; i < n; i++) + { + ELF_SYM *sym = symbols + i; + if (sym->st_shndx <= 0 || sym->st_size <= 0 || + ELF_ST_BIND (sym->st_info) != STB_GLOBAL || ELF_ST_TYPE (sym->st_info) != STT_FUNC) + continue; + if (sym->st_shndx > curSec) + { + if (nextSec == curSec) + nextSec = sym->st_shndx; + else if (nextSec > sym->st_shndx) + nextSec = sym->st_shndx; + nextSec = sym->st_shndx; + continue; + } + if (sym->st_shndx != curSec) + continue; + long long st_delta = (sym->st_value >= shdr[sym->st_shndx].sh_addr) ? + (sym->st_value - shdr[sym->st_shndx].sh_addr) : -1; + char *st_value = bgn + st_delta; + if (st_delta >= 0 && st_value + sym->st_size <= end) + { + append_segment_record ("<event kind=\"map\" object=\"dynfunc\" name=\"%s\" " + "vaddr=\"0x%016lX\" size=\"%u\" funcname=\"%s\" />\n", + mapName, (void*) st_value, sym->st_size, symbase + sym->st_name); + + TprintfT (DBG_LT2, "process_vsyscall_page: append_segment_record map dynfunc='%s' vaddr=%016lX size=%ld funcname='%s'\n", + mapName, (unsigned long) st_value, + (long) sym->st_size, symbase + sym->st_name); + + /* now cache this for a subsequent experiment */ + if (ndyn >= MAXDYN) + __collector_log_write ("<event kind=\"%s\" id=\"%d\">MAXDYN=%d</event>\n", + SP_JCMD_CERROR, COL_ERROR_MAPCACHE, MAXDYN); + else + { + dynname [ndyn] = CALL_UTIL (libc_strdup)(mapName); + dynvaddr [ndyn] = (void *) st_value; + dynsize [ndyn] = (unsigned) sym->st_size; + dynfuncname[ndyn] = CALL_UTIL (libc_strdup)(symbase + sym->st_name); + TprintfT (DBG_LT2, "process_vsyscall_page: cached entry %d map function='%s' vaddr=0x%016lX size=%ld '%s'\n", + ndyn, dynname[ndyn], (unsigned long) dynvaddr[ndyn], + (long) dynsize[ndyn], dynfuncname[ndyn]); + ndyn++; + } + } + } + __collector_int_func_load (DFUNC_KERNEL, mapName, NULL, + (void*) (base + shdr[curSec].sh_offset), shdr[curSec].sh_size, 0, NULL); + + /* now cache this function for a subsequent experiment */ + if (nvsysfuncs >= MAXVSYSFUNCS) + __collector_log_write ("<event kind=\"%s\" id=\"%d\">MAXVSYSFUNCS=%d</event>\n", + SP_JCMD_CERROR, COL_ERROR_MAPCACHE, MAXVSYSFUNCS); + else + { + sysfuncname[nvsysfuncs] = CALL_UTIL (libc_strdup)(mapName); + sysfuncvaddr[nvsysfuncs] = (unsigned long) (base + shdr[curSec].sh_offset); + sysfuncsize[nvsysfuncs] = (unsigned long) (shdr[curSec].sh_size); + TprintfT (DBG_LT2, "process_vsyscall_page: cached entry %d map function='%s' vaddr=0x%016lX size=%ld\n", + nvsysfuncs, sysfuncname[nvsysfuncs], + (unsigned long) sysfuncvaddr[nvsysfuncs], + (long) sysfuncsize[nvsysfuncs]); + nvsysfuncs++; + } + TprintfT (DBG_LT2, "process_vsyscall_page: collector_int_func_load='%s' vaddr=0x%016lX size=%ld\n", + mapName, (unsigned long) (base + shdr[curSec].sh_offset), + (long) shdr[curSec].sh_size); + if (curSec == nextSec) + break; + } + } + } + +#if WSIZE(32) + unsigned long vsysaddr = (unsigned long) 0xffffe000; +#elif WSIZE(64) + unsigned long vsysaddr = (unsigned long) 0xffffffffff600000; +#endif + // Make sure the vsyscall map has PROT_EXEC + MapInfo *mp; + for (mp = mmaps.next; mp; mp = mp->next) + { + TprintfT (DBG_LT2, "MapInfo: vaddr=0x%016llx [size=%lld] mflags=0x%llx offset=%lld pagesize=%lld\n" + " mapname='%s' filename='%s'\n", + (unsigned long long) mp->vaddr, (long long) mp->size, + (long long) mp->mflags, (long long) mp->offset, (long long) mp->pagesize, + mp->mapname ? mp->mapname : "NULL", + mp->filename ? mp->filename : "NULL"); + if (vsysaddr == mp->vaddr) + mp->mflags |= PROT_EXEC; + if ((unsigned long) ehdr == (unsigned long) mp->vaddr) + continue; + if (__collector_strncmp (mp->mapname, "[vdso]", 6) == 0 + || __collector_strncmp (mp->mapname, "[vsyscall]", 10) == 0) + { + /* + * On rubbia ( 2.6.9-5.ELsmp #1 SMP 32-bit ) access to ehdr causes SEGV. + * There doesn't seem to be a way to reliably determine the actual presence + * of the page: even when /proc reports it's there it can't be accessed. + * We will have to put up with <Unknown> on some Linuxes until this is resolved. + __collector_int_func_load(DFUNC_KERNEL, mp->mapname, NULL, (void*) mp->vaddr, mp->size, 0, NULL); + */ + hrtime_t hrt = GETRELTIME (); + append_segment_record ( + "<event kind=\"map\" object=\"function\" tstamp=\"%u.%09u\" " + "vaddr=\"0x%016lX\" size=\"%u\" name=\"%s\" />\n", + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), + (unsigned long) mp->vaddr, (unsigned) mp->size, mp->mapname); + TprintfT (DBG_LT2, "process_vsyscall_page: append_segment_record map function = %s, vaddr = 0x%016lX, size = %u\n", + mp->mapname, (unsigned long) mp->vaddr, (unsigned) mp->size); + + /* now cache this function for a subsequent experiment */ + if (nvsysfuncs >= MAXVSYSFUNCS) + __collector_log_write ("<event kind=\"%s\" id=\"%d\">MAXVSYSFUNCS=%d</event>\n", + SP_JCMD_CERROR, COL_ERROR_MAPCACHE, MAXVSYSFUNCS); + else + { + sysfuncname[nvsysfuncs] = CALL_UTIL (libc_strdup)(mp->mapname); + sysfuncvaddr[nvsysfuncs] = mp->vaddr; + sysfuncsize[nvsysfuncs] = (unsigned long) mp->size; + TprintfT (DBG_LT2, "process_vsyscall_page: cached entry %d map function='%s' vaddr=0x%016lX size=%ld\n", + nvsysfuncs, sysfuncname[nvsysfuncs], + (unsigned long) sysfuncvaddr[nvsysfuncs], + (long) sysfuncsize[nvsysfuncs]); + nvsysfuncs++; + + } + } + } +} + +/* + * collector API for dynamic functions + */ +void collector_func_load () __attribute__ ((weak, alias ("__collector_func_load"))); +void +__collector_func_load (char *name, char *alias, char *sourcename, + void *vaddr, int size, int lntsize, DT_lineno *lntable) +{ + __collector_int_func_load (DFUNC_API, name, sourcename, + vaddr, size, lntsize, lntable); +} + +void collector_func_unload () __attribute__ ((weak, alias ("__collector_func_unload"))); +void +__collector_func_unload (void *vaddr) +{ + __collector_int_func_unload (DFUNC_API, vaddr); +} + +/* routines for handling dynamic functions */ +static void +rwrite (int fd, void *buf, size_t nbyte) +{ + size_t left = nbyte; + size_t res; + char *ptr = (char*) buf; + while (left > 0) + { + res = CALL_UTIL (write)(fd, ptr, left); + if (res == -1) + { + TprintfT (0, "ERROR: rwrite(%s) failed: errno=%d\n", dyntext_fname, errno); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_DYNWRITE, errno, dyntext_fname); + return; + } + left -= res; + ptr += res; + } +} + +void +__collector_int_func_load (dfunc_mode_t mode, char *name, char *sourcename, + void *vaddr, int size, int lntsize, DT_lineno *lntable) +{ + char name_buf[32]; + int slen; + static char pad[16]; + int padn; + if (!mmap_initted) + return; + hrtime_t hrt = GETRELTIME (); + + if (name == NULL) + { + /* generate a name based on vaddr */ + CALL_UTIL (snprintf)(name_buf, sizeof (name_buf), "0x%lx", (unsigned long) vaddr); + name = name_buf; + } + + switch (mode) + { + case DFUNC_API: + case DFUNC_KERNEL: + append_segment_record ("<event kind=\"map\" object=\"function\" tstamp=\"%u.%09u\" " + "vaddr=\"0x%016lX\" size=\"%u\" name=\"%s\" />\n", + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), + (unsigned long) vaddr, (unsigned) size, name); + break; + case DFUNC_JAVA: + append_segment_record ("<event kind=\"map\" object=\"jcm\" tstamp=\"%u.%09u\" " + "vaddr=\"0x%016lX\" size=\"%u\" methodId=\"%s\" />\n", + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), + (unsigned long) vaddr, (unsigned) size, name); + break; + default: + return; + } + + /* 21275311 Unwind failure in native stack for java application running on jdk8 on x86 + * Check: + * - function starts in a known segment (base1 != 0) + * - function ends in the same segment (base1==base2 && end1==end2) + * If not, then call update_map_segments(). + */ + unsigned long base1, end1, base2, end2; + __collector_check_segment ((unsigned long) vaddr, &base1, &end1, 0); + if (base1) + __collector_check_segment (((unsigned long) vaddr)+((unsigned long) size), &base2, &end2, 0); + if (base1 == 0 || base1 != base2 || end1 != end2) + __collector_ext_update_map_segments (); + + /* Write a copy of actual code to the "dyntext" file */ + DT_header dt_hdr; + dt_hdr.type = DT_HEADER; + dt_hdr.size = sizeof (dt_hdr); + dt_hdr.time = hrt; + unsigned long t = (unsigned long) vaddr; /* to suppress a warning from gcc */ + dt_hdr.vaddr = (uint64_t) t; + + DT_code dt_code; + dt_code.type = DT_CODE; + void *code = vaddr; + if (vaddr != NULL && size > 0) + { + dt_code.size = sizeof (dt_code) + ((size + 0xf) & ~0xf); + if (mode == DFUNC_KERNEL) + { + /* Some Linuxes don't accept vaddrs from the vsyscall + * page in write(). Make a copy. + */ + code = alloca (size); + __collector_memcpy (code, vaddr, size); + } + } + else + dt_code.size = 0; + + DT_srcfile dt_src; + dt_src.type = DT_SRCFILE; + if (sourcename) + { + slen = CALL_UTIL (strlen)(sourcename) + 1; + dt_src.size = slen ? sizeof (dt_src) + ((slen + 0xf) & ~0xf) : 0; + } + else + { + slen = 0; + dt_src.size = 0; + } + + DT_ltable dt_ltbl; + dt_ltbl.type = DT_LTABLE; + if (lntable != NULL && lntsize > 0) + dt_ltbl.size = sizeof (dt_ltbl) + lntsize * sizeof (DT_lineno); + else + dt_ltbl.size = 0; + + int fd = CALL_UTIL (open)(dyntext_fname, O_RDWR | O_APPEND); + if (fd == -1) + { + TprintfT (0, "ERROR: __collector_int_func_load: open(%s) failed: errno=%d\n", + dyntext_fname, errno); + (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_DYNOPEN, errno, dyntext_fname); + return; + } + + /* Lock the whole file */ + __collector_mutex_lock (&dyntext_lock); + rwrite (fd, &dt_hdr, sizeof (dt_hdr)); + if (dt_code.size) + { + padn = dt_code.size - sizeof (dt_code) - size; + rwrite (fd, &dt_code, sizeof (dt_code)); + rwrite (fd, code, size); + rwrite (fd, &pad, padn); + } + if (dt_src.size) + { + padn = dt_src.size - sizeof (dt_src) - slen; + rwrite (fd, &dt_src, sizeof (dt_src)); + rwrite (fd, sourcename, slen); + rwrite (fd, &pad, padn); + } + if (dt_ltbl.size) + { + rwrite (fd, &dt_ltbl, sizeof (dt_ltbl)); + rwrite (fd, lntable, dt_ltbl.size - sizeof (dt_ltbl)); + } + + /* Unlock the file */ + __collector_mutex_unlock( &dyntext_lock ); + CALL_UTIL(close( fd ) ); +} + +void +__collector_int_func_unload (dfunc_mode_t mode, void *vaddr) +{ + if (!mmap_initted) + return; + hrtime_t hrt = GETRELTIME (); + if (mode == DFUNC_API) + append_segment_record ("<event kind=\"unmap\" tstamp=\"%u.%09u\" vaddr=\"0x%016lX\"/>\n", + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), (unsigned long) vaddr); + else if (mode == DFUNC_JAVA) + /* note that the "vaddr" is really a method id, not an address */ + append_segment_record ("<event kind=\"unmap\" tstamp=\"%u.%09u\" methodId=\"0x%016lX\"/>\n", + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), (unsigned long) vaddr); + else + return; +} + +/* + * int init_mmap_intf() + * Set up interposition (if not already done). + */ +static int +init_mmap_intf () +{ + if (__collector_dlsym_guard) + return 1; + void *dlflag; + __real_mmap = (void*(*)(void* addr, size_t len, int prot, int flags, + int fildes, off_t off))dlsym (RTLD_NEXT, SYS_MMAP_NAME); + if (__real_mmap == NULL) + { + + /* We are probably dlopened after libthread/libc, + * try to search in the previously loaded objects + */ + __real_mmap = (void*(*)(void* addr, size_t len, int prot, int flags, + int fildes, off_t off))dlsym (RTLD_DEFAULT, SYS_MMAP_NAME); + if (__real_mmap == NULL) + { + TprintfT (0, "ERROR: collector real mmap not found\n"); + return 1; + } + TprintfT (DBG_LT2, "collector real mmap found with RTLD_DEFAULT\n"); + dlflag = RTLD_DEFAULT; + } + else + { + TprintfT (DBG_LT2, "collector real mmap found with RTLD_NEXT\n"); + dlflag = RTLD_NEXT; + } + + TprintfT (DBG_LT2, "init_mmap_intf() @%p __real_mmap\n", __real_mmap); + __real_mmap64 = (void*(*)(void *, size_t, int, int, int, off64_t)) + dlsym (dlflag, SYS_MMAP64_NAME); + TprintfT (DBG_LT2, "init_mmap_intf() @%p __real_mmap64\n", __real_mmap64); + __real_munmap = (int(*)(void *, size_t)) dlsym (dlflag, SYS_MUNMAP_NAME); + TprintfT (DBG_LT2, "init_mmap_intf() @%p __real_munmap\n", __real_munmap); + + // dlopen/dlmopen/dlclose are in libdl.so + __real_dlopen = (void*(*)(const char *, int)) + dlvsym (dlflag, SYS_DLOPEN_NAME, SYS_DLOPEN_VERSION); + TprintfT (DBG_LT2, "init_mmap_intf() [%s] @%p __real_dlopen\n", + SYS_DLOPEN_VERSION, __real_dlopen); +#if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) + __real_dlopen_2_1 = __real_dlopen; + __real_dlopen_2_0 = (void*(*)(const char *, int)) + dlvsym (dlflag, SYS_DLOPEN_NAME, "GLIBC_2.0"); +#endif + + __real_dlclose = (int(*)(void* handle))dlsym (dlflag, SYS_DLCLOSE_NAME); + TprintfT (DBG_LT2, "init_mmap_intf() @%p __real_dlclose\n", __real_dlclose); + TprintfT (DBG_LT2, "init_mmap_intf() done\n"); + + return 0; +} + +/*------------------------------------------------------------- mmap */ +void * +mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset) +{ + int err = 0; + if (NULL_PTR (mmap)) + err = init_mmap_intf (); + if (err) + return MAP_FAILED; + + /* hrtime_t hrt = GETRELTIME(); */ + void *ret = CALL_REAL (mmap)(start, length, prot, flags, fd, offset); + + if (!CHCK_REENTRANCE && (ret != MAP_FAILED) && collector_heap_record != NULL) + { + PUSH_REENTRANCE; + /* write a separate record for mmap tracing */ + collector_heap_record (MMAP_TRACE, length, ret); + POP_REENTRANCE; + } + TprintfT (DBG_LT2, "libcollector.mmap(%p, %ld, %d, %d, %d, 0x%lld) = %p\n", + start, (long) length, prot, flags, fd, (long long) offset, ret); + return ret; +} + +/*------------------------------------------------------------- mmap64 */ +#if WSIZE(32) /* mmap64 only defined for non-64-bit */ + +void * +mmap64 (void *start, size_t length, int prot, int flags, int fd, off64_t offset) +{ + if (NULL_PTR (mmap64)) + init_mmap_intf (); + + /* hrtime_t hrt = GETRELTIME(); */ + void *ret = CALL_REAL (mmap64)(start, length, prot, flags, fd, offset); + if (!CHCK_REENTRANCE && (ret != MAP_FAILED) && collector_heap_record != NULL) + { + PUSH_REENTRANCE; + /* write a separate record for mmap tracing */ + collector_heap_record (MMAP_TRACE, length, ret); + POP_REENTRANCE; + } + TprintfT (DBG_LT2, "libcollector.mmap64(%p, %ld, %d, %d, %d, 0x%lld) = %p\n", + start, (long) length, prot, flags, fd, (long long) offset, ret); + return ret; +} +#endif /* WSIZE(32) */ + +/*------------------------------------------------------------- munmap */ +int +munmap (void *start, size_t length) +{ + if (NULL_PTR (munmap)) + init_mmap_intf (); + + /* hrtime_t hrt = GETRELTIME(); */ + int rc = CALL_REAL (munmap)(start, length); + if (!CHCK_REENTRANCE && (rc == 0) && collector_heap_record != NULL) + { + PUSH_REENTRANCE; + /* write a separate record for mmap tracing */ + collector_heap_record (MUNMAP_TRACE, length, start); + POP_REENTRANCE; + } + TprintfT (DBG_LT2, "libcollector.munmap(%p, %ld) = %d\n", start, (long) length, rc); + return rc; +} + + +/*------------------------------------------------------------- dlopen */ +// map interposed symbol versions +#if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) + +static void * +__collector_dlopen_symver (void*(real_dlopen) (), void *caller, const char *pathname, int mode); + +void * +__collector_dlopen_2_1 (const char *pathname, int mode) +{ + if (NULL_PTR (dlopen)) + init_mmap_intf (); + void *caller = __builtin_return_address (0); // must be called inside dlopen first layer interpostion + return __collector_dlopen_symver (CALL_REAL (dlopen_2_1), caller, pathname, mode); +} + +void * +__collector_dlopen_2_0 (const char *pathname, int mode) +{ + if (NULL_PTR (dlopen)) + init_mmap_intf (); + void* caller = __builtin_return_address (0); // must be called inside dlopen first layer interpostion + return __collector_dlopen_symver (CALL_REAL (dlopen_2_0), caller, pathname, mode); +} + +__asm__(".symver __collector_dlopen_2_1,dlopen@@GLIBC_2.1"); +__asm__(".symver __collector_dlopen_2_0,dlopen@GLIBC_2.0"); + +#endif + +#if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) +static void * +__collector_dlopen_symver (void*(real_dlopen) (), void *caller, const char *pathname, int mode) +#else +void * +dlopen (const char *pathname, int mode) +#endif +{ + const char * real_pathname = pathname; + char new_pathname[MAXPATHLEN]; + int origin_offset = 0; + TprintfT (DBG_LT2, "dlopen: pathname=%s, mode=%d\n", pathname ? pathname : "NULL", mode); + if (pathname && __collector_strStartWith (pathname, "$ORIGIN/") == 0) + origin_offset = 8; + else if (pathname && __collector_strStartWith (pathname, "${ORIGIN}/") == 0) + origin_offset = 10; + if (origin_offset) + { +#if ! ((ARCH(Intel) && WSIZE(32)) || ARCH(SPARC)) + // 'caller' is not passed as an argument + void * caller = __builtin_return_address (0); // must be called inside dlopen first layer interpostion +#endif + Dl_info dl_info; + if (caller && dladdr (caller, &dl_info) != 0) + { + TprintfT (DBG_LT2, "dladdr(%p): %p fname=%s\n", + caller, dl_info.dli_fbase, dl_info.dli_fname); + new_pathname[0] = '\0'; + const char *p = __collector_strrchr (dl_info.dli_fname, '/'); + if (p) + __collector_strlcpy (new_pathname, dl_info.dli_fname, + (p - dl_info.dli_fname + 2) < MAXPATHLEN ? (p - dl_info.dli_fname + 2) : MAXPATHLEN); + __collector_strlcat (new_pathname, pathname + origin_offset, MAXPATHLEN - CALL_UTIL (strlen)(new_pathname)); + real_pathname = new_pathname; + } + else + TprintfT (0, "ERROR: dladdr(%p): %s\n", caller, dlerror ()); + } + if (NULL_PTR (dlopen)) + init_mmap_intf (); + TprintfT (DBG_LT2, "libcollector.dlopen(%s,%d) interposing\n", + pathname ? pathname : "", mode); + void* ret = NULL; + + // set guard for duration of handling dlopen, since want to ensure + // new mappings are resolved after the actual dlopen has occurred + PUSH_REENTRANCE; + hrtime_t hrt = GETRELTIME (); + + if (real_pathname && !__collector_strchr (real_pathname, '/')) + { // got an unqualified name + // get caller and use its searchpath +#if ! ((ARCH(Intel) && WSIZE(32)) || ARCH(SPARC)) + void* caller = __builtin_return_address (0); // must be called inside dlopen +#endif + if (caller) + { +#if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) + ret = dlopen_searchpath_symver (real_dlopen, caller, real_pathname, mode); +#else + ret = dlopen_searchpath (caller, real_pathname, mode); +#endif + } + } + + if (!ret) + { +#if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) + ret = (real_dlopen) (real_pathname, mode); +#else + ret = CALL_REAL (dlopen)(real_pathname, mode); +#endif + } + TprintfT (DBG_LT2, "libcollector -- dlopen(%s) returning %p\n", pathname, ret); + + /* Don't call update if dlopen failed: preserve dlerror() */ + if (ret && (mmap_mode > 0) && !(mode & RTLD_NOLOAD)) + update_map_segments (hrt, 1); + TprintfT (DBG_LT2, "libcollector -- dlopen(%s) returning %p\n", pathname, ret); + POP_REENTRANCE; + return ret; +} + +/*------------------------------------------------------------- dlclose */ +int +dlclose (void *handle) +{ + if (NULL_PTR (dlclose)) + init_mmap_intf (); + TprintfT (DBG_LT2, "__collector_dlclose(%p) entered\n", handle); + hrtime_t hrt = GETRELTIME (); + if (!CHCK_REENTRANCE) + { + PUSH_REENTRANCE; + update_map_segments (hrt, 1); + POP_REENTRANCE; + hrt = GETRELTIME (); + } + int ret = CALL_REAL (dlclose)(handle); + + /* Don't call update if dlclose failed: preserve dlerror() */ + if (!ret && !CHCK_REENTRANCE) + { + PUSH_REENTRANCE; + update_map_segments (hrt, 1); + POP_REENTRANCE; + } + TprintfT (DBG_LT2, "__collector_dlclose(%p) returning %d\n", handle, ret); + return ret; +} diff --git a/gprofng/libcollector/profile.c b/gprofng/libcollector/profile.c new file mode 100644 index 0000000..996d3f0 --- /dev/null +++ b/gprofng/libcollector/profile.c @@ -0,0 +1,287 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Profile handling + * + * Note: SIGPROF signal-handling and interval timer (once exclusive to + * profile handling) are now common services provided by the dispatcher. + */ + +#include "config.h" +#include <dlfcn.h> +#include <stdlib.h> +#include <string.h> +#include <ucontext.h> +#include <unistd.h> + +#include "gp-defs.h" +#include "collector_module.h" +#include "gp-experiment.h" +#include "data_pckts.h" +#include "libcol_util.h" +#include "hwprofile.h" +#include "tsd.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +static int init_interface (CollectorInterface*); +static int open_experiment (const char *); +static int start_data_collection (void); +static int stop_data_collection (void); +static int close_experiment (void); +static int detach_experiment (void); + +static ModuleInterface module_interface ={ + SP_PROFILE_FILE, /* description */ + init_interface, /* initInterface */ + open_experiment, /* openExperiment */ + start_data_collection, /* startDataCollection */ + stop_data_collection, /* stopDataCollection */ + close_experiment, /* closeExperiment */ + detach_experiment /* detachExperiment (fork child) */ +}; + +static CollectorInterface *collector_interface = NULL; +static int prof_mode = 0; +static CollectorModule prof_hndl = COLLECTOR_MODULE_ERR; +static unsigned prof_key = COLLECTOR_TSD_INVALID_KEY; + +typedef struct ClockPacket +{ /* clock profiling packet */ + CM_Packet comm; + pthread_t lwp_id; + pthread_t thr_id; + uint32_t cpu_id; + hrtime_t tstamp __attribute__ ((packed)); + uint64_t frinfo __attribute__ ((packed)); + int mstate; /* kernel microstate */ + int nticks; /* number of ticks in that state */ +} ClockPacket; + +/* XXX should be able to use local types */ +#define CLOCK_TYPE OPROF_PCKT + +#define CHCK_REENTRANCE(x) ( !prof_mode || ((x) = collector_interface->getKey( prof_key )) == NULL || (*(x) != 0) ) +#define PUSH_REENTRANCE(x) ((*(x))++) +#define POP_REENTRANCE(x) ((*(x))--) + +#ifdef DEBUG +#define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ ) +#define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ ) +#else +#define Tprintf(...) +#define TprintfT(...) +#endif + +static void init_module () __attribute__ ((constructor)); + +static void +init_module () +{ + __collector_dlsym_guard = 1; + RegModuleFunc reg_module = (RegModuleFunc) dlsym (RTLD_DEFAULT, "__collector_register_module"); + __collector_dlsym_guard = 0; + if (reg_module == NULL) + { + TprintfT (0, "clockprof: init_module FAILED -- reg_module = NULL\n"); + return; + } + prof_hndl = reg_module (&module_interface); + if (prof_hndl == COLLECTOR_MODULE_ERR && collector_interface != NULL) + { + Tprintf (0, "clockprof: ERROR: handle not created.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n", SP_JCMD_CERROR, COL_ERROR_PROFINIT); + } + TprintfT (0, "clockprof: init_module, prof_hndl = %d\n", prof_hndl); + return; +} + +static int +init_interface (CollectorInterface *_collector_interface) +{ + collector_interface = _collector_interface; + return COL_ERROR_NONE; +} + +static int +open_experiment (const char *exp) +{ + if (collector_interface == NULL) + { + Tprintf (0, "clockprof: ERROR: collector_interface is null.\n"); + return COL_ERROR_PROFINIT; + } + const char *params = collector_interface->getParams (); + while (params) + { + if (__collector_strStartWith (params, "p:") == 0) + { + params += 2; + break; + } + while (*params != 0 && *params != ';') + params++; + if (*params == 0) + params = NULL; + else + params++; + } + if (params == NULL) /* Clock profiling not specified */ + return COL_ERROR_PROFINIT; + TprintfT (0, "clockprof: open_experiment %s -- %s\n", exp, params); + int prof_interval = CALL_UTIL (strtol)(params, NULL, 0); + prof_key = collector_interface->createKey (sizeof ( int), NULL, NULL); + if (prof_key == (unsigned) - 1) + { + Tprintf (0, "clockprof: ERROR: TSD key create failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n", SP_JCMD_CERROR, COL_ERROR_PROFINIT); + return COL_ERROR_PROFINIT; + } + + /* set dispatcher interval timer period used for all timed activities */ + int prof_interval_actual = __collector_ext_itimer_set (prof_interval); + TprintfT (0, "clockprof: open_experiment(): __collector_ext_itimer_set (actual period=%d, req_period=%d)\n", + prof_interval_actual, prof_interval); + if (prof_interval_actual <= 0) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">itimer could not be set</event>\n", SP_JCMD_CERROR, COL_ERROR_PROFINIT); + return COL_ERROR_PROFINIT; + } + if ((prof_interval_actual >= (prof_interval + prof_interval / 10)) || + (prof_interval_actual <= (prof_interval - prof_interval / 10))) + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", SP_JCMD_CWARN, COL_WARN_PROFRND, prof_interval, prof_interval_actual); + else if (prof_interval_actual != prof_interval) + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", SP_JCMD_COMMENT, COL_WARN_PROFRND, prof_interval, prof_interval_actual); + prof_interval = prof_interval_actual; + collector_interface->writeLog ("<profile name=\"%s\" ptimer=\"%d\" numstates=\"%d\">\n", + SP_JCMD_PROFILE, prof_interval, LMS_MAGIC_ID_LINUX); + collector_interface->writeLog (" <profdata fname=\"%s\"/>\n", + module_interface.description); + + /* Record Profile packet description */ + ClockPacket *cp = NULL; + collector_interface->writeLog (" <profpckt kind=\"%d\" uname=\"" STXT ("Clock profiling data") "\">\n", CLOCK_TYPE); + collector_interface->writeLog (" <field name=\"LWPID\" uname=\"" STXT ("Lightweight process id") "\" offset=\"%d\" type=\"%s\"/>\n", + &cp->lwp_id, sizeof (cp->lwp_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"THRID\" uname=\"" STXT ("Thread number") "\" offset=\"%d\" type=\"%s\"/>\n", + &cp->thr_id, sizeof (cp->thr_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"CPUID\" uname=\"" STXT ("CPU id") "\" offset=\"%d\" type=\"%s\"/>\n", + &cp->cpu_id, sizeof (cp->cpu_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"" STXT ("High resolution timestamp") "\" offset=\"%d\" type=\"%s\"/>\n", + &cp->tstamp, sizeof (cp->tstamp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n", + &cp->frinfo, sizeof (cp->frinfo) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"MSTATE\" uname=\"" STXT ("Thread state") "\" offset=\"%d\" type=\"%s\"/>\n", + &cp->mstate, sizeof (cp->mstate) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"NTICK\" uname=\"" STXT ("Duration") "\" offset=\"%d\" type=\"%s\"/>\n", + &cp->nticks, sizeof (cp->nticks) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" </profpckt>\n"); + collector_interface->writeLog ("</profile>\n"); + return COL_ERROR_NONE; +} + +static int +start_data_collection (void) +{ + TprintfT (0, "clockprof: start_data_collection\n"); + prof_mode = 1; + return 0; +} + +static int +stop_data_collection (void) +{ + prof_mode = 0; + TprintfT (0, "clockprof: stop_data_collection\n"); + return 0; +} + +static int +close_experiment (void) +{ + prof_mode = 0; + prof_key = COLLECTOR_TSD_INVALID_KEY; + TprintfT (0, "clockprof: close_experiment\n"); + return 0; +} + +/* fork child. Clean up state but don't write to experiment */ +static int +detach_experiment (void) +{ + prof_mode = 0; + prof_key = COLLECTOR_TSD_INVALID_KEY; + TprintfT (0, "clockprof: detach_experiment\n"); + return 0; +} + +/* + * void collector_lost_profile_context + * Placeholder/marker function used when profiling given NULL context. + */ +void +__collector_lost_profile_context (void) { } + +/* + * void __collector_ext_profile_handler( siginfo_t *info, ucontext_t *context ) + * Handle real profile events to collect profile data. + */ +void +__collector_ext_profile_handler (siginfo_t *info, ucontext_t *context) +{ + int *guard; + if (!prof_mode) /* sigprof timer running only because hwprofile.c needs it */ + return; + if (CHCK_REENTRANCE (guard)) + { + TprintfT (0, "__collector_ext_profile_handler: ERROR: prof_mode=%d guard=%d!\n", + prof_mode, guard ? *guard : -2); + return; + } + PUSH_REENTRANCE (guard); + TprintfT (DBG_LT3, "__collector_ext_profile_handler\n"); + ucontext_t uctxmem; + if (context == NULL) + { + /* assume this case is rare, and accept overhead of creating dummy_uc */ + TprintfT (0, "collector_profile_handler: ERROR: got NULL context!\n"); + context = &uctxmem; + getcontext (context); /* initialize dummy context */ + SETFUNCTIONCONTEXT (context, &__collector_lost_profile_context); + } + ClockPacket pckt; + CALL_UTIL (memset)(&pckt, 0, sizeof ( pckt)); + pckt.comm.tsize = sizeof ( pckt); + pckt.comm.type = CLOCK_TYPE; + pckt.lwp_id = __collector_lwp_self (); + pckt.thr_id = __collector_thr_self (); + pckt.cpu_id = CALL_UTIL (getcpuid)(); + pckt.tstamp = collector_interface->getHiResTime (); + pckt.frinfo = collector_interface->getFrameInfo (COLLECTOR_MODULE_ERR, pckt.tstamp, FRINFO_FROM_UC, context); + pckt.mstate = LMS_LINUX_CPU; + pckt.nticks = 1; + collector_interface->writeDataPacket (prof_hndl, (CM_Packet*) & pckt); + POP_REENTRANCE (guard); +} diff --git a/gprofng/libcollector/synctrace.c b/gprofng/libcollector/synctrace.c new file mode 100644 index 0000000..401c8f2 --- /dev/null +++ b/gprofng/libcollector/synctrace.c @@ -0,0 +1,1064 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Synchronization events + */ +#include "config.h" +#include <alloca.h> +#include <dlfcn.h> +#include <unistd.h> +#include <semaphore.h> /* sem_wait() */ +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> +#include <pthread.h> + +#include "gp-defs.h" +#include "collector_module.h" +#include "gp-experiment.h" +#include "data_pckts.h" +#include "i18n.h" +#include "tsd.h" +#include "cc_libcollector.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LTT 0 // for interposition on GLIBC functions +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +/* define the packet that will be written out */ +typedef struct Sync_packet +{ /* Synchronization delay tracing packet */ + Common_packet comm; + hrtime_t requested; /* time of synchronization request */ + Vaddr_type objp; /* vaddr of synchronization object */ +} Sync_packet; + +static int open_experiment (const char *); +static int start_data_collection (void); +static int stop_data_collection (void); +static int close_experiment (void); +static int detach_experiment (void); +static int init_thread_intf (); +static int sync_calibrate (); + +static ModuleInterface module_interface ={ + SP_SYNCTRACE_FILE, /* description */ + NULL, /* initInterface */ + open_experiment, /* openExperiment */ + start_data_collection, /* startDataCollection */ + stop_data_collection, /* stopDataCollection */ + close_experiment, /* closeExperiment */ + detach_experiment /* detachExperiment (fork child) */ +}; + +static CollectorInterface *collector_interface = NULL; +static int sync_mode = 0; +static long sync_scope = 0; +static int sync_native = 0; +static int sync_java = 0; +static CollectorModule sync_hndl = COLLECTOR_MODULE_ERR; +static unsigned sync_key = COLLECTOR_TSD_INVALID_KEY; +static long sync_threshold = -1; /* calibrate the value */ +static int init_thread_intf_started = 0; +static int init_thread_intf_finished = 0; + +#define CHCK_NREENTRANCE(x) (!sync_native || !sync_mode || ((x) = collector_interface->getKey( sync_key )) == NULL || (*(x) != 0)) +#define RECHCK_NREENTRANCE(x) (!sync_native || !sync_mode || ((x) = collector_interface->getKey( sync_key )) == NULL || (*(x) == 0)) +#define CHCK_JREENTRANCE(x) (!sync_java || !sync_mode || ((x) = collector_interface->getKey( sync_key )) == NULL || (*(x) != 0)) +#define RECHCK_JREENTRANCE(x) (!sync_java || !sync_mode || ((x) = collector_interface->getKey( sync_key )) == NULL || (*(x) == 0)) +#define PUSH_REENTRANCE(x) ((*(x))++) +#define POP_REENTRANCE(x) ((*(x))--) + +#define CALL_REAL(x) (*(int(*)())__real_##x) +#define NULL_PTR(x) ( __real_##x == NULL ) +#define gethrtime collector_interface->getHiResTime + +#ifdef DEBUG +#define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ ) +#define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ ) +#else +#define Tprintf(...) +#define TprintfT(...) +#endif + +/* + * In most cases, the functions which require interposition are implemented as + * weak symbols corresponding to an associated internal function named with a + * leading underscore: e.g., mutex_lock() is simply an alias for _mutex_lock(). + * For the wait functions, however, the published version (used by applications) + * is distinct from the internal version (used by system libraries), i.e., + * cond_wait() is an alias for _cond_wait_cancel() rather than _cond_wait(). + */ +static void *__real_strtol = NULL; +static void *__real_fprintf = NULL; +static void *__real___collector_jprofile_enable_synctrace = NULL; +static void *__real_pthread_mutex_lock = NULL; +static void *__real_pthread_mutex_unlock = NULL; /* not interposed, used in calibrate */ +static void *__real_pthread_cond_wait = NULL; +static void *__real_pthread_cond_timedwait = NULL; +static void *__real_pthread_join = NULL; +static void *__real_sem_wait = NULL; +static void *__real_pthread_cond_wait_2_3_2 = NULL; +static void *__real_pthread_cond_timedwait_2_3_2 = NULL; + +#if WSIZE(32) +static void *__real_sem_wait_2_1 = NULL; +static void *__real_sem_wait_2_0 = NULL; +static void *__real_pthread_cond_wait_2_0 = NULL; +static void *__real_pthread_cond_timedwait_2_0 = NULL; +#elif WSIZE(64) +#if ARCH(Intel) +static void *__real_pthread_cond_wait_2_2_5 = NULL; +static void *__real_pthread_cond_timedwait_2_2_5 = NULL; +#elif ARCH(SPARC) +static void *__real_pthread_cond_wait_2_2 = NULL; +static void *__real_pthread_cond_timedwait_2_2 = NULL; +#endif /* ARCH() */ +#endif /* WSIZE() */ + +static void +collector_memset (void *s, int c, size_t n) +{ + unsigned char *s1 = s; + while (n--) + *s1++ = (unsigned char) c; +} + +void +__collector_module_init (CollectorInterface *_collector_interface) +{ + if (_collector_interface == NULL) + return; + collector_interface = _collector_interface; + TprintfT (0, "synctrace: __collector_module_init\n"); + sync_hndl = collector_interface->registerModule (&module_interface); + + /* Initialize next module */ + ModuleInitFunc next_init = (ModuleInitFunc) dlsym (RTLD_NEXT, "__collector_module_init"); + if (next_init != NULL) + next_init (_collector_interface); +} + +static int +open_experiment (const char *exp) +{ + long thresh = 0; + if (init_thread_intf_finished == 0) + init_thread_intf (); + if (collector_interface == NULL) + { + Tprintf (0, "synctrace: collector_interface is null.\n"); + return COL_ERROR_SYNCINIT; + } + if (sync_hndl == COLLECTOR_MODULE_ERR) + { + Tprintf (0, "synctrace: handle create failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_SYNCINIT); + return COL_ERROR_SYNCINIT; + } + TprintfT (0, "synctrace: open_experiment %s\n", exp); + + char *params = (char *) collector_interface->getParams (); + while (params) + { + if ((params[0] == 's') && (params[1] == ':')) + { + char *ptr = params + 2; + Tprintf (DBG_LT1, "synctrace: open_experiment s: parameter = %s\n", ptr); + while (*ptr != ',' && *ptr != ';') + ptr++; + sync_scope = 0; + if (*ptr == ',') + { + sync_scope = CALL_REAL (strtol) (ptr + 1, NULL, 0); + switch (sync_scope) + { + case 1: + sync_java = 0; + sync_native = 1; + break; + case 2: + sync_java = 1; + sync_native = 0; + break; + default: + case 3: + sync_native = 1; + sync_java = 1; + break; + } + Tprintf (0, "\tsynctrace: sync_scope found as %ld\n", sync_scope); + } + else + { + /* the old-style descriptor, without scope */ + /* if there was no comma, use the old default */ + sync_scope = 3; + sync_java = 1; + sync_native = 1; + Tprintf (0, "\tsynctrace: sync_scope not found set to %ld\n", sync_scope); + } + if (__real___collector_jprofile_enable_synctrace == NULL) + sync_java = 0; + thresh = CALL_REAL (strtol)(params + 2, NULL, 0); + break; /* from the loop to find the "s:thresh,scope" entry */ + } + else + params++; + } + if (params == NULL) /* Sync data collection not specified */ + return COL_ERROR_SYNCINIT; + if (thresh < 0) /* calibrate the threshold, keep it as a negative number */ + thresh = -sync_calibrate (); + + sync_key = collector_interface->createKey (sizeof ( int), NULL, NULL); + if (sync_key == (unsigned) - 1) + { + Tprintf (0, "synctrace: TSD key create failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_SYNCINIT); + return COL_ERROR_SYNCINIT; + } + /* if Java synctrace was requested, tell the jprofile module */ + if (sync_java) + { + TprintfT (0, "synctrace: enabling Java synctrace\n"); + CALL_REAL (__collector_jprofile_enable_synctrace)(); + } + collector_interface->writeLog ("<profile name=\"%s\" threshold=\"%ld\" scope=\"%ld\">\n", + SP_JCMD_SYNCTRACE, thresh, sync_scope); + collector_interface->writeLog (" <profdata fname=\"%s\"/>\n", + module_interface.description); + /* Record Sync_packet description */ + Sync_packet *pp = NULL; + collector_interface->writeLog (" <profpckt kind=\"%d\" uname=\"Synchronization tracing data\">\n", SYNC_PCKT); + collector_interface->writeLog (" <field name=\"LWPID\" uname=\"Lightweight process id\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.lwp_id, sizeof (pp->comm.lwp_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"THRID\" uname=\"Thread number\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.thr_id, sizeof (pp->comm.thr_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"CPUID\" uname=\"CPU id\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.cpu_id, sizeof (pp->comm.cpu_id) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"High resolution timestamp\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.tstamp, sizeof (pp->comm.tstamp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n", + &pp->comm.frinfo, sizeof (pp->comm.frinfo) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"SRQST\" uname=\"Synchronization start time\" offset=\"%d\" type=\"%s\"/>\n", + &pp->requested, sizeof (pp->requested) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" <field name=\"SOBJ\" uname=\"Synchronization object address\" offset=\"%d\" type=\"%s\"/>\n", + &pp->objp, sizeof (pp->objp) == 4 ? "INT32" : "INT64"); + collector_interface->writeLog (" </profpckt>\n"); + collector_interface->writeLog ("</profile>\n"); + + /* Convert threshold from microsec to nanosec */ + sync_threshold = (thresh > 0 ? thresh : -thresh) * 1000; + TprintfT (0, "synctrace: open_experiment complete %ld\n", sync_threshold); + return COL_ERROR_NONE; +} + +static int +start_data_collection (void) +{ + sync_mode = 1; + TprintfT (0, "synctrace: start_data_collection\n"); + return 0; +} + +static int +stop_data_collection (void) +{ + sync_mode = 0; + TprintfT (0, "synctrace: stop_data_collection\n"); + return 0; +} + +static int +close_experiment (void) +{ + sync_mode = 0; + sync_threshold = -1; + sync_key = COLLECTOR_TSD_INVALID_KEY; + TprintfT (0, "synctrace: close_experiment\n"); + return 0; +} + +/* fork child. Clean up state but don't write to experiment */ +static int +detach_experiment (void) +{ + sync_mode = 0; + sync_threshold = -1; + sync_key = COLLECTOR_TSD_INVALID_KEY; + TprintfT (0, "synctrace: detach_experiment\n"); + return 0; +} + +#define NUM_ITER 100 /* number of iterations in calibration */ +#define NUM_WARMUP 3 /* number of warm up iterations */ + +static int +sync_calibrate () +{ + pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER; + hrtime_t bt, at, delta; + hrtime_t avg, max, min; + int i; + int ret; + avg = (hrtime_t) 0; + min = max = (hrtime_t) 0; + for (i = 0; i < NUM_ITER + NUM_WARMUP; i++) + { + /* Here we simulate a real call */ + bt = gethrtime (); + ret = CALL_REAL (pthread_mutex_lock)(&mt); + at = gethrtime (); + CALL_REAL (pthread_mutex_unlock)(&mt); + if (i < NUM_WARMUP) /* skip these iterations */ + continue; + /* add the time of this one */ + delta = at - bt; + avg += delta; + if (min == 0) + min = delta; + if (delta < min) + min = delta; + if (delta > max) + max = delta; + } + /* compute average time */ + avg = avg / NUM_ITER; + + /* pretty simple, let's see how it works */ + if (max < 6 * avg) + max = 6 * avg; + /* round up to the nearest microsecond */ + ret = (int) ((max + 999) / 1000); + return ret; +} + +static int +init_thread_intf () +{ + void *dlflag = RTLD_NEXT; + int err = 0; + /* if we detect recursion/reentrance, SEGV so we can get a stack */ + init_thread_intf_started++; + if (!init_thread_intf_finished && init_thread_intf_started >= 3) + { + /* pull the plug if recursion occurs... */ + abort (); + } + /* lookup fprint to print fatal error message */ + void *ptr = dlsym (RTLD_DEFAULT, "fprintf"); + if (ptr) + { + __real_fprintf = (void *) ptr; + } + else + { + abort (); + } + + /* find the __collector_jprofile_enable_synctrace routine in jprofile module */ + ptr = dlsym (RTLD_DEFAULT, "__collector_jprofile_enable_synctrace"); + if (ptr) + __real___collector_jprofile_enable_synctrace = (void *) ptr; + else + { +#if defined(GPROFNG_JAVA_PROFILING) + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT __collector_jprofile_enable_synctrace\n"); + err = COL_ERROR_SYNCINIT; +#endif + sync_java = 0; + } + +#if WSIZE(32) + /* ########################################## begin WSIZE(32) */ + /* IMPORTANT!! The GLIBC_* versions below must match those in the er_sync.*.mapfile ! */ + dlflag = RTLD_NEXT; + ptr = dlvsym (dlflag, "pthread_mutex_lock", "GLIBC_2.0"); + if (ptr == NULL) + { + /* We are probably dlopened after libthread/libc, + * try to search in the previously loaded objects + */ + dlflag = RTLD_DEFAULT; + ptr = dlvsym (dlflag, "pthread_mutex_lock", "GLIBC_2.0"); + if (ptr != NULL) + { + __real_pthread_mutex_lock = ptr; + Tprintf (0, "synctrace: WARNING: init_thread_intf() using RTLD_DEFAULT for OS sync routines\n"); + } + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_mutex_lock\n"); + err = COL_ERROR_SYNCINIT; + } + } + else + __real_pthread_mutex_lock = ptr; + + ptr = dlvsym (dlflag, "pthread_mutex_unlock", "GLIBC_2.0"); + if (ptr) + __real_pthread_mutex_unlock = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_mutex_unlock\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.3.2"); + if (ptr) + __real_pthread_cond_wait = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_wait\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.3.2"); + if (ptr) + __real_pthread_cond_timedwait = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_timedwait\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_join", "GLIBC_2.0"); + if (ptr) + __real_pthread_join = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_join\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "sem_wait", "GLIBC_2.1"); + if (ptr) + __real_sem_wait = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT sem_wait\n"); + err = COL_ERROR_SYNCINIT; + } + +#if ARCH(Intel) + /* ############## Intel specific additional pointers for 32-bits */ + ptr = __real_sem_wait_2_1 = __real_sem_wait; + ptr = dlvsym (dlflag, "sem_wait", "GLIBC_2.0"); + if (ptr) + __real_sem_wait_2_0 = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT sem_wait_2_0\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.0"); + if (ptr) + __real_pthread_cond_wait_2_0 = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_wait_2_0\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.0"); + if (ptr) + __real_pthread_cond_timedwait_2_0 = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT __real_pthread_cond_timedwait_2_0\n"); + err = COL_ERROR_SYNCINIT; + } +#endif /* ARCH(Intel) */ + +#else /* WSIZE(64) */ + /* # most versions are different between platforms */ + /* # the few that are common are set after the ARCH ifdef */ +#if ARCH(Aarch64) + dlflag = RTLD_NEXT; +#define GLIBC_N "GLIBC_2.17" + __real_pthread_mutex_lock = dlvsym(dlflag, "pthread_mutex_lock", GLIBC_N); + __real_pthread_mutex_unlock = dlvsym(dlflag, "pthread_mutex_unlock", GLIBC_N); + __real_pthread_cond_wait = dlvsym(dlflag, "pthread_cond_wait", GLIBC_N); + __real_pthread_cond_timedwait = dlvsym(dlflag, "pthread_cond_timedwait", GLIBC_N); + __real_pthread_join = dlvsym(dlflag, "pthread_join", GLIBC_N); + __real_sem_wait = dlvsym(dlflag, "sem_wait", GLIBC_N); + +#elif ARCH(Intel) + dlflag = RTLD_NEXT; + ptr = dlvsym (dlflag, "pthread_mutex_lock", "GLIBC_2.2.5"); + if (ptr == NULL) + { + /* We are probably dlopened after libthread/libc, + * try to search in the previously loaded objects + */ + dlflag = RTLD_DEFAULT; + ptr = dlvsym (dlflag, "pthread_mutex_lock", "GLIBC_2.2.5"); + if (ptr != NULL) + { + __real_pthread_mutex_lock = ptr; + Tprintf (0, "synctrace: WARNING: init_thread_intf() using RTLD_DEFAULT for Solaris sync routines\n"); + } + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_mutex_lock\n"); + err = COL_ERROR_SYNCINIT; + } + } + else + __real_pthread_mutex_lock = ptr; + ptr = dlvsym (dlflag, "pthread_mutex_unlock", "GLIBC_2.2.5"); + if (ptr) + __real_pthread_mutex_unlock = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_mutex_unlock\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.3.2"); + if (ptr) + __real_pthread_cond_wait = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_wait\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.3.2"); + if (ptr) + __real_pthread_cond_timedwait = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_timedwait\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_join", "GLIBC_2.2.5"); + if (ptr) + __real_pthread_join = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_join\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "sem_wait", "GLIBC_2.2.5"); + if (ptr) + __real_sem_wait = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT sem_wait\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.2.5"); + if (ptr) + __real_pthread_cond_wait_2_2_5 = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_wait_2_2_5\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.2.5"); + if (ptr) + __real_pthread_cond_timedwait_2_2_5 = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_timedwait_2_2_5\n"); + err = COL_ERROR_SYNCINIT; + } + +#elif ARCH(SPARC) + dlflag = RTLD_NEXT; + ptr = dlvsym (dlflag, "pthread_mutex_lock", "GLIBC_2.2"); + if (ptr == NULL) + { + /* We are probably dlopened after libthread/libc, + * try to search in the previously loaded objects + */ + dlflag = RTLD_DEFAULT; + ptr = dlvsym (dlflag, "pthread_mutex_lock", "GLIBC_2.2"); + if (ptr != NULL) + { + __real_pthread_mutex_lock = ptr; + Tprintf (0, "synctrace: WARNING: init_thread_intf() using RTLD_DEFAULT for Solaris sync routines\n"); + } + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT mutex_lock\n"); + err = COL_ERROR_SYNCINIT; + } + } + else + __real_pthread_mutex_lock = ptr; + ptr = dlvsym (dlflag, "pthread_mutex_unlock", "GLIBC_2.2"); + if (ptr) + __real_pthread_mutex_unlock = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_mutex_unlock\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.3.2"); + if (ptr) + __real_pthread_cond_wait = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_wait\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.3.2"); + if (ptr) + __real_pthread_cond_timedwait = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_timedwait\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_join", "GLIBC_2.2"); + if (ptr) + __real_pthread_join = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_join\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "sem_wait", "GLIBC_2.2"); + if (ptr) + __real_sem_wait = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT sem_wait\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.2"); + if (ptr) + __real_pthread_cond_wait_2_2 = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_wait_2_2_5\n"); + err = COL_ERROR_SYNCINIT; + } + ptr = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.2"); + if (ptr) + __real_pthread_cond_timedwait_2_2 = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_timedwait_2_2\n"); + err = COL_ERROR_SYNCINIT; + } +#endif /* ARCH() */ +#endif /* WSIZE(64) */ + /* the pointers that are common to 32- and 64-bits, and to SPARC and Intel */ + + __real_pthread_cond_wait_2_3_2 = __real_pthread_cond_wait; + __real_pthread_cond_timedwait_2_3_2 = __real_pthread_cond_timedwait; + ptr = dlsym (dlflag, "strtol"); + if (ptr) + __real_strtol = (void *) ptr; + else + { + CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT strtol\n"); + err = COL_ERROR_SYNCINIT; + } + init_thread_intf_finished++; + TprintfT (0, "synctrace init_thread_intf complete\n"); + return err; +} + +/* These next two routines are used from jprofile to record Java synctrace data */ +void +__collector_jsync_begin () +{ + int *guard; + if (CHCK_JREENTRANCE (guard)) + { + Tprintf (DBG_LT1, "__collector_jsync_begin: skipped\n"); + return; + } + Tprintf (DBG_LT1, "__collector_jsync_begin: start event\n"); + PUSH_REENTRANCE (guard); +} + +void +__collector_jsync_end (hrtime_t reqt, void *object) +{ + int *guard; + if (RECHCK_JREENTRANCE (guard)) + { + Tprintf (DBG_LT1, "__collector_jsync_end: skipped\n"); + return; + } + hrtime_t grnt = gethrtime (); + if (grnt - reqt >= sync_threshold) + { + Sync_packet spacket; + collector_memset (&spacket, 0, sizeof ( Sync_packet)); + spacket.comm.tsize = sizeof ( Sync_packet); + spacket.comm.tstamp = grnt; + spacket.requested = reqt; + spacket.objp = (Vaddr_type) object; + spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl, spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket); + collector_interface->writeDataRecord (sync_hndl, (Common_packet*) & spacket); + } + Tprintf (DBG_LT1, "__collector_jsync_begin: end event\n"); + POP_REENTRANCE (guard); +} + +/*-------------------------------------------------------- pthread_mutex_lock */ +int +pthread_mutex_lock (pthread_mutex_t *mp) +{ + int *guard; + if (NULL_PTR (pthread_mutex_lock)) + init_thread_intf (); + if (CHCK_NREENTRANCE (guard)) + return CALL_REAL (pthread_mutex_lock)(mp); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + int ret = CALL_REAL (pthread_mutex_lock)(mp); + if (RECHCK_NREENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + if (grnt - reqt >= sync_threshold) + { + Sync_packet spacket; + collector_memset (&spacket, 0, sizeof ( Sync_packet)); + spacket.comm.tsize = sizeof ( Sync_packet); + spacket.comm.tstamp = grnt; + spacket.requested = reqt; + spacket.objp = (Vaddr_type) mp; + spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl, spacket.comm.tstamp, FRINFO_FROM_STACK, &spacket); + collector_interface->writeDataRecord (sync_hndl, (Common_packet*) & spacket); + } + POP_REENTRANCE (guard); + return ret; +} + + +/*------------------------------------------------------------- pthread_cond_wait */ +// map interposed symbol versions +static int +__collector_pthread_cond_wait_symver (int(real_pthread_cond_wait) (), pthread_cond_t *cond, pthread_mutex_t *mutex); + +int +__collector_pthread_cond_wait_2_3_2 (pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + if (NULL_PTR (pthread_cond_wait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_pthread_cond_wait_2_3_2@%p\n", CALL_REAL (pthread_cond_wait_2_3_2)); + return __collector_pthread_cond_wait_symver (CALL_REAL (pthread_cond_wait_2_3_2), cond, mutex); +} + +#if ARCH(Intel) || ARCH(SPARC) +__asm__(".symver __collector_pthread_cond_wait_2_3_2,pthread_cond_wait@@GLIBC_2.3.2"); +#endif + +#if WSIZE(32) + +int +__collector_pthread_cond_wait_2_0 (pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + if (NULL_PTR (pthread_cond_wait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_pthread_cond_wait_2_0@%p\n", CALL_REAL (pthread_cond_wait_2_0)); + return __collector_pthread_cond_wait_symver (CALL_REAL (pthread_cond_wait_2_0), cond, mutex); +} + +__asm__(".symver __collector_pthread_cond_wait_2_0,pthread_cond_wait@GLIBC_2.0"); + +#else // WSIZE(64) +#if ARCH(Intel) +int +__collector_pthread_cond_wait_2_2_5 (pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + if (NULL_PTR (pthread_cond_wait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_pthread_cond_wait_2_2_5@%p\n", CALL_REAL (pthread_cond_wait_2_2_5)); + return __collector_pthread_cond_wait_symver (CALL_REAL (pthread_cond_wait_2_2_5), cond, mutex); +} + +__asm__(".symver __collector_pthread_cond_wait_2_2_5,pthread_cond_wait@GLIBC_2.2.5"); +#elif ARCH(SPARC) + +int +__collector_pthread_cond_wait_2_2 (pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + if (NULL_PTR (pthread_cond_wait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_pthread_cond_wait_2_2@%p\n", CALL_REAL (pthread_cond_wait_2_2)); + return __collector_pthread_cond_wait_symver (CALL_REAL (pthread_cond_wait_2_2), cond, mutex); +} + +__asm__(".symver __collector_pthread_cond_wait_2_2,pthread_cond_wait@GLIBC_2.2"); +#endif // ARCH() +#endif // WSIZE() + +static int +__collector_pthread_cond_wait_symver (int(real_pthread_cond_wait) (), pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + int *guard; + if (NULL_PTR (pthread_cond_wait)) + init_thread_intf (); + if (CHCK_NREENTRANCE (guard)) + return (real_pthread_cond_wait) (cond, mutex); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + int ret = -1; + ret = (real_pthread_cond_wait) (cond, mutex); + if (RECHCK_NREENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + if (grnt - reqt >= sync_threshold) + { + Sync_packet spacket; + collector_memset (&spacket, 0, sizeof ( Sync_packet)); + spacket.comm.tsize = sizeof ( Sync_packet); + spacket.comm.tstamp = grnt; + spacket.requested = reqt; + spacket.objp = (Vaddr_type) mutex; + spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl, spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket); + collector_interface->writeDataRecord (sync_hndl, (Common_packet*) & spacket); + } + POP_REENTRANCE (guard); + return ret; +} + +/*---------------------------------------------------- pthread_cond_timedwait */ +// map interposed symbol versions +static int +__collector_pthread_cond_timedwait_symver (int(real_pthread_cond_timedwait) (), + pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime); + +int +__collector_pthread_cond_timedwait_2_3_2 (pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + if (NULL_PTR (pthread_cond_timedwait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_pthread_cond_timedwait_2_3_2@%p\n", CALL_REAL (pthread_cond_timedwait_2_3_2)); + return __collector_pthread_cond_timedwait_symver (CALL_REAL (pthread_cond_timedwait_2_3_2), cond, mutex, abstime); +} + +#if ARCH(Intel) || ARCH(SPARC) +__asm__(".symver __collector_pthread_cond_timedwait_2_3_2,pthread_cond_timedwait@@GLIBC_2.3.2"); +#endif // ARCH() + +#if WSIZE(32) +int +__collector_pthread_cond_timedwait_2_0 (pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + if (NULL_PTR (pthread_cond_timedwait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_pthread_cond_timedwait_2_0@%p\n", CALL_REAL (pthread_cond_timedwait_2_0)); + return __collector_pthread_cond_timedwait_symver (CALL_REAL (pthread_cond_timedwait_2_0), cond, mutex, abstime); +} + +__asm__(".symver __collector_pthread_cond_timedwait_2_0,pthread_cond_timedwait@GLIBC_2.0"); + +#else // WSIZE(64) +#if ARCH(Intel) +int +__collector_pthread_cond_timedwait_2_2_5 (pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + if (NULL_PTR (pthread_cond_timedwait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_pthread_cond_timedwait_2_2_5@%p\n", CALL_REAL (pthread_cond_timedwait_2_2_5)); + return __collector_pthread_cond_timedwait_symver (CALL_REAL (pthread_cond_timedwait_2_2_5), cond, mutex, abstime); +} + +__asm__(".symver __collector_pthread_cond_timedwait_2_2_5,pthread_cond_timedwait@GLIBC_2.2.5"); +#elif ARCH(SPARC) + +int +__collector_pthread_cond_timedwait_2_2 (pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + if (NULL_PTR (pthread_cond_timedwait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_pthread_cond_timedwait_2_2@%p\n", CALL_REAL (pthread_cond_timedwait_2_2)); + return __collector_pthread_cond_timedwait_symver (CALL_REAL (pthread_cond_timedwait_2_2), cond, mutex, abstime); +} + +__asm__(".symver __collector_pthread_cond_timedwait_2_2,pthread_cond_timedwait@GLIBC_2.2"); +#endif // ARCH() +#endif // WSIZE() + +static int +__collector_pthread_cond_timedwait_symver (int(real_pthread_cond_timedwait) (), + pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + int *guard; + if (NULL_PTR (pthread_cond_timedwait)) + init_thread_intf (); + if (CHCK_NREENTRANCE (guard)) + return (real_pthread_cond_timedwait) (cond, mutex, abstime); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + int ret = -1; + ret = (real_pthread_cond_timedwait) (cond, mutex, abstime); + if (RECHCK_NREENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + if (grnt - reqt >= sync_threshold) + { + Sync_packet spacket; + collector_memset (&spacket, 0, sizeof ( Sync_packet)); + spacket.comm.tsize = sizeof ( Sync_packet); + spacket.comm.tstamp = grnt; + spacket.requested = reqt; + spacket.objp = (Vaddr_type) mutex; + spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl, spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket); + collector_interface->writeDataRecord (sync_hndl, (Common_packet*) & spacket); + } + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- pthread_join */ +int +pthread_join (pthread_t target_thread, void **status) +{ + int *guard; + if (NULL_PTR (pthread_join)) + init_thread_intf (); + if (CHCK_NREENTRANCE (guard)) + return CALL_REAL (pthread_join)(target_thread, status); + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + int ret = CALL_REAL (pthread_join)(target_thread, status); + if (RECHCK_NREENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + if (grnt - reqt >= sync_threshold) + { + Sync_packet spacket; + collector_memset (&spacket, 0, sizeof ( Sync_packet)); + spacket.comm.tsize = sizeof ( Sync_packet); + spacket.comm.tstamp = grnt; + spacket.requested = reqt; + spacket.objp = (Vaddr_type) target_thread; + spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl, spacket.comm.tstamp, FRINFO_FROM_STACK, &spacket); + collector_interface->writeDataRecord (sync_hndl, (Common_packet*) & spacket); + } + POP_REENTRANCE (guard); + return ret; +} + +/*------------------------------------------------------------- sem_wait */ +// map interposed symbol versions +#if ARCH(Intel) && WSIZE(32) +static int +__collector_sem_wait_symver (int(real_sem_wait) (), sem_t *sp); + +int +__collector_sem_wait_2_1 (sem_t *sp) +{ + if (NULL_PTR (sem_wait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_sem_wait_2_1@%p\n", CALL_REAL (sem_wait_2_1)); + return __collector_sem_wait_symver (CALL_REAL (sem_wait_2_1), sp); +} + +int +__collector_sem_wait_2_0 (sem_t *sp) +{ + if (NULL_PTR (sem_wait)) + init_thread_intf (); + TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_sem_wait_2_0@%p\n", CALL_REAL (sem_wait_2_0)); + return __collector_sem_wait_symver (CALL_REAL (sem_wait_2_0), sp); +} + +__asm__(".symver __collector_sem_wait_2_1,sem_wait@@GLIBC_2.1"); +__asm__(".symver __collector_sem_wait_2_0,sem_wait@GLIBC_2.0"); +#endif + +#if ARCH(Intel) && WSIZE(32) +static int +__collector_sem_wait_symver (int(real_sem_wait) (), sem_t *sp) +{ +#else +int +sem_wait (sem_t *sp) +{ +#endif + int *guard; + if (NULL_PTR (sem_wait)) + init_thread_intf (); + if (CHCK_NREENTRANCE (guard)) + { +#if ARCH(Intel) && WSIZE(32) + return (real_sem_wait) (sp); +#else + return CALL_REAL (sem_wait)(sp); +#endif + } + PUSH_REENTRANCE (guard); + hrtime_t reqt = gethrtime (); + int ret = -1; +#if ARCH(Intel) && WSIZE(32) + ret = (real_sem_wait) (sp); +#else + ret = CALL_REAL (sem_wait)(sp); +#endif + if (RECHCK_NREENTRANCE (guard)) + { + POP_REENTRANCE (guard); + return ret; + } + hrtime_t grnt = gethrtime (); + if (grnt - reqt >= sync_threshold) + { + Sync_packet spacket; + collector_memset (&spacket, 0, sizeof ( Sync_packet)); + spacket.comm.tsize = sizeof ( Sync_packet); + spacket.comm.tstamp = grnt; + spacket.requested = reqt; + spacket.objp = (Vaddr_type) sp; + +#if ARCH(Intel) && WSIZE(32) + spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl, spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket); +#else + spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl, spacket.comm.tstamp, FRINFO_FROM_STACK, &spacket); +#endif + collector_interface->writeDataRecord (sync_hndl, (Common_packet*) & spacket); + } + POP_REENTRANCE (guard); + return ret; +} diff --git a/gprofng/libcollector/tsd.c b/gprofng/libcollector/tsd.c new file mode 100644 index 0000000..416c3e7 --- /dev/null +++ b/gprofng/libcollector/tsd.c @@ -0,0 +1,149 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <pthread.h> + +#include "collector.h" +#include "libcol_util.h" +#include "tsd.h" +#include "memmgr.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +/* + * Build our thread-specific-data support on pthread interfaces. + */ +#define MAXNKEYS 64 /* hard-wired? really? well, it depends only on us and we have a sense for how many keys we will use */ +static pthread_key_t tsd_pkeys[MAXNKEYS]; +static size_t tsd_sizes[MAXNKEYS]; +static unsigned tsd_nkeys = 0; + +int +__collector_tsd_init () +{ + return 0; +} + +void +__collector_tsd_fini () +{ + Tprintf (DBG_LT1, "tsd_fini()\n"); + while (tsd_nkeys) + { + tsd_nkeys--; + pthread_key_delete (tsd_pkeys[tsd_nkeys]); + tsd_sizes[tsd_nkeys] = 0; // should be unneeded + } +} + +int +__collector_tsd_allocate () +{ + return 0; +} + +void +__collector_tsd_release () { } + +static void +tsd_destructor (void *p) +{ + if (p) + __collector_freeCSize (__collector_heap, p, *((size_t *) p)); +} + +unsigned +__collector_tsd_create_key (size_t sz, void (*init)(void*), void (*fini)(void*)) +{ + /* + * We no longer support init and fini arguments (and weren't using them anyhow). + * Our hard-wired MAXNKEYS presumably is considerably higher than the number of keys we use. + */ + if (init || fini || (tsd_nkeys >= MAXNKEYS)) + return COLLECTOR_TSD_INVALID_KEY; + + /* + * A pthread key has a value that is (void *). + * We don't know where it is stored, and can access its value only through {get|set}specific. + * But libcollector expects a pointer to memory that it can modify. + * So we have to allocate that memory and store the pointer. + * + * For now, we just have to register a destructor that will free the memory + * when the thread finishes. + */ + if (pthread_key_create (&tsd_pkeys[tsd_nkeys], &tsd_destructor)) + return COLLECTOR_TSD_INVALID_KEY; + tsd_sizes[tsd_nkeys] = sz; + tsd_nkeys++; + return (tsd_nkeys - 1); +} + +void * +__collector_tsd_get_by_key (unsigned key_index) +{ + if (key_index == COLLECTOR_TSD_INVALID_KEY) + return NULL; + if (key_index < 0 || key_index >= tsd_nkeys) + return NULL; + pthread_key_t key = tsd_pkeys[key_index]; + size_t sz = tsd_sizes[key_index]; + + /* + * When we use __collector_freeCSize(), we need to know the + * size that had been allocated. So, stick a header to the + * front of the allocation to hold the size. The header could + * just be sizeof(size_t), but pad it to preserve alignment for + * the usable area. + */ + size_t header = 8; + void *value = pthread_getspecific (key); + + // check whether we have allocated the memory + if (value == NULL) + { + // add room to record the size + value = __collector_allocCSize (__collector_heap, sz + header, 0); + if (value == NULL) + { + // do we need to guard against trying to alloc each time? + return NULL; + } + // write the size of the allocation + *((size_t *) value) = sz + header; + CALL_UTIL (memset)(((char *) value) + header, 0, sz); + + // record the allocation for future retrieval + if (pthread_setspecific (key, value)) + return NULL; + } + // return the pointer, skipping the header + return ((char *) value) +header; +} + +void +__collector_tsd_fork_child_cleanup () +{ + __collector_tsd_fini (); +} diff --git a/gprofng/libcollector/tsd.h b/gprofng/libcollector/tsd.h new file mode 100644 index 0000000..38b3d53 --- /dev/null +++ b/gprofng/libcollector/tsd.h @@ -0,0 +1,80 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* Thread-specific data */ + +#ifndef _TSD_H +#define _TSD_H + +#include <sys/types.h> + +int __collector_tsd_init (); +/* Function: Init tsd module. Call once before using other functions. + MT-Level: Unsafe + Return: 0 if successful + */ + +void __collector_tsd_fini (); +/* Function: Shutdown tsd module. + MT-Level: Unsafe + Return: None + */ + +void __collector_tsd_fork_child_cleanup (); +/* Function: Reset tsd module. Call immediately after fork() in child process. + MT-Level: Unsafe + Return: None + */ + +int __collector_tsd_allocate (); +/* Function: Allocate thread info. + Call from threads before using tsd_get_by_key(). + Call from main thread should be made before calls from other threads. + MT-Level: First call is unsafe. Safe afterwards. + Return: 0 if successful + */ + +void __collector_tsd_release (); +/* Function: Free thread info. + Call from threads just before thread termination. + MT-Level: Safe + Return: None + */ + +#define COLLECTOR_TSD_INVALID_KEY ((unsigned)-1) +unsigned __collector_tsd_create_key (size_t memsize, void (*init)(void*), void (*fini)(void*)); +/* Function: Reserve TDS memory. + MT-Level: Unsafe + Inputs: <memsize>: number of bytes to reserve + <init>: key memory initialization. Must be callable even if + the associated thread has not yet been created. + <fini>: key memory finalization. Must be callable even if + the associated thread has been terminated. + Return: key or COLLECTOR_TSD_INVALID_KEY if not successful. + */ + +void *__collector_tsd_get_by_key (unsigned key); +/* Function: Get TSD memory. + Call from threads after calling tsd_allocate(). + MT-Level: Safe + Inputs: <key>: return value from tsd_create_key() + Return: memory if successful, NULL otherwise + */ +#endif /* _TSD_H */ diff --git a/gprofng/libcollector/unwind.c b/gprofng/libcollector/unwind.c new file mode 100644 index 0000000..ffb06f9 --- /dev/null +++ b/gprofng/libcollector/unwind.c @@ -0,0 +1,4630 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <alloca.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <pthread.h> + +#include "gp-defs.h" +#include "collector.h" +#include "gp-experiment.h" +#include "memmgr.h" +#include "tsd.h" + +/* Get dynamic module interface*/ +#include "collector_module.h" + +/* Get definitions for SP_LEAF_CHECK_MARKER, SP_TRUNC_STACK_MARKER */ +#include "data_pckts.h" + +#if ARCH(SPARC) +struct frame +{ + long fr_local[8]; /* saved locals */ + long fr_arg[6]; /* saved arguments [0 - 5] */ + struct frame *fr_savfp; /* saved frame pointer */ + long fr_savpc; /* saved program counter */ +#if WSIZE(32) + char *fr_stret; /* struct return addr */ +#endif + long fr_argd[6]; /* arg dump area */ + long fr_argx[1]; /* array of args past the sixth */ +}; + +#elif ARCH(Intel) +struct frame +{ + unsigned long fr_savfp; + unsigned long fr_savpc; +}; +#endif + +/* Set the debug trace level */ +#define DBG_LT0 0 +#define DBG_LT1 1 +#define DBG_LT2 2 +#define DBG_LT3 3 + +int (*__collector_VM_ReadByteInstruction)(unsigned char *) = NULL; +#define VM_NO_ACCESS (-1) +#define VM_NOT_VM_MEMORY (-2) +#define VM_NOT_X_SEGMENT (-3) + +#define isInside(p, bgn, end) ((p) >= (bgn) && (p) < (end)) + +/* + * Weed through all the arch dependent stuff to get the right definition + * for 'pc' in the ucontext structure. The system header files are mess + * dealing with all the arch (just look for PC, R_PC, REG_PC). + * + */ + +#if ARCH(SPARC) + +#define IN_BARRIER(x) \ + ( barrier_hdl && \ + (unsigned long)x >= barrier_hdl && \ + (unsigned long)x < barrier_hdlx ) +static unsigned long barrier_hdl = 0; +static unsigned long barrier_hdlx = 0; + +#if WSIZE(64) +#define STACK_BIAS 2047 +#define IN_TRAP_HANDLER(x) \ + ( misalign_hdl && \ + (unsigned long)x >= misalign_hdl && \ + (unsigned long)x < misalign_hdlx ) +static unsigned long misalign_hdl = 0; +static unsigned long misalign_hdlx = 0; +#elif WSIZE(32) +#define STACK_BIAS 0 +#endif + +#if WSIZE(64) +#define GET_GREG(ctx,reg) (((ucontext_t*)ctx)->uc_mcontext.mc_gregs[(reg)]) +#define GET_SP(ctx) (((ucontext_t*)ctx)->uc_mcontext.mc_gregs[MC_O6]) +#define GET_PC(ctx) (((ucontext_t*)ctx)->uc_mcontext.mc_gregs[MC_PC]) +#else +#define GET_GREG(ctx,reg) (((ucontext_t*)ctx)->uc_mcontext.gregs[(reg)]) +#define GET_SP(ctx) (((ucontext_t*)ctx)->uc_mcontext.gregs[REG_O6]) +#define GET_PC(ctx) (((ucontext_t*)ctx)->uc_mcontext.gregs[REG_PC]) +#endif + +#elif ARCH(Intel) +#include "opcodes/disassemble.h" + +static int +fprintf_func (void *arg ATTRIBUTE_UNUSED, const char *fmt ATTRIBUTE_UNUSED, ...) +{ + return 0; +} + +/* Get LENGTH bytes from info's buffer, at target address memaddr. + Transfer them to myaddr. */ +static int +read_memory_func (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, + disassemble_info *info) +{ + unsigned int opb = info->octets_per_byte; + size_t end_addr_offset = length / opb; + size_t max_addr_offset = info->buffer_length / opb; + size_t octets = (memaddr - info->buffer_vma) * opb; + if (memaddr < info->buffer_vma + || memaddr - info->buffer_vma > max_addr_offset + || memaddr - info->buffer_vma + end_addr_offset > max_addr_offset + || (info->stop_vma && (memaddr >= info->stop_vma + || memaddr + end_addr_offset > info->stop_vma))) + return -1; + memcpy (myaddr, info->buffer + octets, length); + return 0; +} + +static void +print_address_func (bfd_vma addr ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) { } + +static asymbol * +symbol_at_address_func (bfd_vma addr ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) +{ + return NULL; +} + +static bfd_boolean +symbol_is_valid (asymbol *sym ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) +{ + return TRUE; +} + +static void +memory_error_func (int status ATTRIBUTE_UNUSED, bfd_vma addr ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) { } + + +#if WSIZE(32) +#define GET_PC(ctx) (((ucontext_t*)ctx)->uc_mcontext.gregs[REG_EIP]) +#define GET_SP(ctx) (((ucontext_t*)ctx)->uc_mcontext.gregs[REG_ESP]) +#define GET_FP(ctx) (((ucontext_t*)ctx)->uc_mcontext.gregs[REG_EBP]) + +#elif WSIZE(64) +#define GET_PC(ctx) (((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP]) +#define GET_SP(ctx) (((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RSP]) +#define GET_FP(ctx) (((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RBP]) +#endif /* WSIZE() */ + +#elif ARCH(Aarch64) +#define GET_PC(ctx) (((ucontext_t*)ctx)->uc_mcontext.regs[15]) +#define GET_SP(ctx) (((ucontext_t*)ctx)->uc_mcontext.regs[13]) +#define GET_FP(ctx) (((ucontext_t*)ctx)->uc_mcontext.regs[14]) +#endif /* ARCH() */ + +/* + * FILL_CONTEXT() for all platforms + * Could use getcontext() except: + * - it's not guaranteed to be async signal safe + * - it's a system call and not that lightweight + * - it's not portable as of POSIX.1-2008 + * So we just use low-level mechanisms to fill in the few fields we need. + */ +#if ARCH(SPARC) +#if WSIZE(32) +#define FILL_CONTEXT(context) \ + { \ + greg_t fp; \ + __asm__ __volatile__( "mov %%i6, %0" : "=r" (fp) ); \ + __asm__ __volatile__( "ta 3" ); \ + GET_SP(context) = fp; \ + GET_PC(context) = (greg_t)0; \ + } + +#elif WSIZE(64) +#define FILL_CONTEXT(context) \ + { \ + greg_t fp; \ + __asm__ __volatile__( "mov %%i6, %0" : "=r" (fp) ); \ + __asm__ __volatile__( "flushw" ); \ + GET_SP(context) = fp; \ + GET_PC(context) = (greg_t)0; \ + } +#endif /* WSIZE() */ + +#elif ARCH(Intel) +#define FILL_CONTEXT(context) \ + { \ + context->uc_link = NULL; \ + void *sp = __collector_getsp(); \ + GET_SP(context) = (greg_t)sp; \ + GET_FP(context) = (greg_t)__collector_getfp(); \ + GET_PC(context) = (greg_t)__collector_getpc(); \ + context->uc_stack.ss_sp = sp; \ + context->uc_stack.ss_size = 0x100000; \ + } + +#elif ARCH(Aarch64) +#define FILL_CONTEXT(context) \ + { getcontext(context); \ + context->uc_mcontext.sp = (__u64) __builtin_frame_address(0); \ + } + +#endif /* ARCH() */ + +static int +getByteInstruction (unsigned char *p) +{ + if (__collector_VM_ReadByteInstruction) + { + int v = __collector_VM_ReadByteInstruction (p); + if (v != VM_NOT_VM_MEMORY) + return v; + } + return *p; +} + +struct DataHandle *dhndl = NULL; + +static unsigned unwind_key = COLLECTOR_TSD_INVALID_KEY; + +/* To support two OpenMP API's we use a pointer + * to the actual function. + */ +int (*__collector_omp_stack_trace)(char*, int, hrtime_t, void*) = NULL; +int (*__collector_mpi_stack_trace)(char*, int, hrtime_t) = NULL; + +#define DEFAULT_MAX_NFRAMES 256 +static int max_native_nframes = DEFAULT_MAX_NFRAMES; +static int max_java_nframes = DEFAULT_MAX_NFRAMES; + +#define NATIVE_FRAME_BYTES(nframes) ( ((nframes)+1) * sizeof(long) ) +#define JAVA_FRAME_BYTES(nframes) ( ((nframes)+1) * sizeof(long) * 2 + 16 ) +#define OVERHEAD_BYTES ( 2 * sizeof(long) + 2 * sizeof(Stack_info) ) + +#define ROOT_UID 801425552975190205ULL +#define ROOT_UID_INV 92251691606677ULL +#define ROOT_IDX 13907816567264074199ULL +#define ROOT_IDX_INV 2075111ULL +#define UIDTableSize 1048576 +static volatile uint64_t *UIDTable = NULL; +static volatile int seen_omp = 0; + +static int stack_unwind (char *buf, int size, void *bptr, void *eptr, ucontext_t *context, int mode); +static FrameInfo compute_uid (Frame_packet *frp); +static int omp_no_walk = 0; + +#if ARCH(Intel) +#define ValTableSize 1048576 +#define OmpValTableSize 65536 +static unsigned long *AddrTable_RA_FROMFP = NULL; // Cache for RA_FROMFP pcs +static unsigned long *AddrTable_RA_EOSTCK = NULL; // Cache for RA_EOSTCK pcs +static struct WalkContext *OmpCurCtxs = NULL; +static struct WalkContext *OmpCtxs = NULL; +static uint32_t *OmpVals = NULL; +static unsigned long *OmpRAs = NULL; +static unsigned long adjust_ret_addr (unsigned long ra, unsigned long segoff, unsigned long tend); +static int parse_x86_AVX_instruction (unsigned char *pc); + +struct WalkContext +{ + unsigned long pc; + unsigned long sp; + unsigned long fp; + unsigned long ln; + unsigned long sbase; /* stack boundary */ + unsigned long tbgn; /* current memory segment start */ + unsigned long tend; /* current memory segment end */ +}; +#endif + +#if defined(DEBUG) && ARCH(Intel) +#include <execinfo.h> + +static void +dump_stack (int nline) +{ + if ((__collector_tracelevel & SP_DUMP_STACK) == 0) + return; + + enum Constexpr { MAX_SIZE = 1024 }; + void *array[MAX_SIZE]; + size_t sz = backtrace (array, MAX_SIZE); + char **strings = backtrace_symbols (array, sz); + DprintfT (SP_DUMP_STACK, "\ndump_stack: %d size=%d\n", nline, (int) sz); + for (int i = 0; i < sz; i++) + DprintfT (SP_DUMP_STACK, " %3d: %p %s\n", i, array[i], + strings[i] ? strings[i] : "???"); +} + +#define dump_targets(nline, ntrg, targets) \ + if ((__collector_tracelevel & SP_DUMP_UNWIND) != 0) \ + for(int i = 0; i < ntrg; i++) \ + DprintfT (SP_DUMP_UNWIND, " %2d: 0x%lx\n", i, (long) targets[i]) +#else +#define dump_stack(x) +#define dump_targets(nline, ntrg, targets) +#endif + +void +__collector_ext_unwind_key_init (int isPthread, void * stack) +{ + void * ptr = __collector_tsd_get_by_key (unwind_key); + if (ptr == NULL) + { + TprintfT (DBG_LT2, "__collector_ext_unwind_key_init: cannot get tsd\n"); + return; + } + if (isPthread) + { + size_t stack_size = 0; + void *stack_addr = 0; + pthread_t pthread = pthread_self (); + pthread_attr_t attr; + int err = pthread_getattr_np (pthread, &attr); + TprintfT (DBG_LT1, "__collector_ext_unwind_key_init: pthread: 0x%lx err: %d\n", pthread, err); + if (err == 0) + { + err = pthread_attr_getstack (&attr, &stack_addr, &stack_size); + if (err == 0) + stack_addr = (char*) stack_addr + stack_size; + TprintfT (DBG_LT1, "__collector_ext_unwind_key_init: stack_size=0x%lx eos=%p err=%d\n", + (long) stack_size, stack_addr, err); + err = pthread_attr_destroy (&attr); + TprintfT (DBG_LT1, "__collector_ext_unwind_key_init: destroy: %d\n", err); + } + *(void**) ptr = stack_addr; + } + else + *(void**) ptr = stack; // cloned thread +} + +void +__collector_ext_unwind_init (int record) +{ + int sz = UIDTableSize * sizeof (*UIDTable); + UIDTable = (uint64_t*) __collector_allocCSize (__collector_heap, sz, 1); + if (UIDTable == NULL) + { + __collector_terminate_expt (); + return; + } + CALL_UTIL (memset)((void*) UIDTable, 0, sz); + + char *str = CALL_UTIL (getenv)("GPROFNG_JAVA_MAX_CALL_STACK_DEPTH"); + if (str != NULL && *str != 0) + { + char *endptr; + int n = CALL_UTIL (strtol)(str, &endptr, 0); + if (endptr != str && n >= 0) + { + if (n < 5) + n = 5; + if (n > MAX_STACKDEPTH) + n = MAX_STACKDEPTH; + max_java_nframes = n; + } + } + + str = CALL_UTIL (getenv)("GPROFNG_MAX_CALL_STACK_DEPTH"); + if (str != NULL && *str != 0) + { + char *endptr = str; + int n = CALL_UTIL (strtol)(str, &endptr, 0); + if (endptr != str && n >= 0) + { + if (n < 5) + n = 5; + if (n > MAX_STACKDEPTH) + n = MAX_STACKDEPTH; + max_native_nframes = n; + } + } + + TprintfT (DBG_LT0, "GPROFNG_MAX_CALL_STACK_DEPTH=%d GPROFNG_JAVA_MAX_CALL_STACK_DEPTH=%d\n", + max_native_nframes, max_java_nframes); + omp_no_walk = 1; + + if (__collector_VM_ReadByteInstruction == NULL) + __collector_VM_ReadByteInstruction = (int(*)()) dlsym (RTLD_DEFAULT, "Async_VM_ReadByteInstruction"); + +#if ARCH(SPARC) +#if WSIZE(64) + misalign_hdl = (unsigned long) dlsym (RTLD_DEFAULT, "__misalign_trap_handler"); + misalign_hdlx = (unsigned long) dlsym (RTLD_DEFAULT, "__misalign_trap_handler_end"); + if (misalign_hdlx == 0) + misalign_hdlx = misalign_hdl + 292; + barrier_hdl = (unsigned long) dlsym (RTLD_DEFAULT, "__mt_EndOfTask_Barrier_"); + barrier_hdlx = (unsigned long) dlsym (RTLD_DEFAULT, "__mt_EndOfTask_Barrier_Dummy_"); + if (barrier_hdlx == 0) + barrier_hdl = 0; +#else + barrier_hdl = (unsigned long) dlsym (RTLD_DEFAULT, "__mt_EndOfTask_Barrier_"); + barrier_hdlx = (unsigned long) dlsym (RTLD_DEFAULT, "__mt_EndOfTask_Barrier_Dummy_"); + if (barrier_hdlx == 0) + barrier_hdl = 0; +#endif /* WSIZE() */ + +#elif ARCH(Intel) + sz = ValTableSize * sizeof (*AddrTable_RA_FROMFP); + AddrTable_RA_FROMFP = (unsigned long*) __collector_allocCSize (__collector_heap, sz, 1); + sz = ValTableSize * sizeof (*AddrTable_RA_EOSTCK); + AddrTable_RA_EOSTCK = (unsigned long*) __collector_allocCSize (__collector_heap, sz, 1); + if (omp_no_walk && (__collector_omp_stack_trace != NULL || __collector_mpi_stack_trace != NULL)) + { + sz = OmpValTableSize * sizeof (*OmpCurCtxs); + OmpCurCtxs = (struct WalkContext *) __collector_allocCSize (__collector_heap, sz, 1); + sz = OmpValTableSize * sizeof (*OmpCtxs); + OmpCtxs = (struct WalkContext *) __collector_allocCSize (__collector_heap, sz, 1); + sz = OmpValTableSize * sizeof (*OmpVals); + OmpVals = (uint32_t*) __collector_allocCSize (__collector_heap, sz, 1); + sz = OmpValTableSize * sizeof (*OmpRAs); + OmpRAs = (unsigned long*) __collector_allocCSize (__collector_heap, sz, 1); + if (OmpCurCtxs == NULL || OmpCtxs == NULL || OmpVals == NULL || OmpRAs == NULL) + { + TprintfT (0, "unwind_init() ERROR: failed; terminating experiment\n"); + __collector_terminate_expt (); + return; + } + } +#endif /* ARCH() */ + + if (record) + { + dhndl = __collector_create_handle (SP_FRINFO_FILE); + __collector_log_write ("<%s name=\"%s\" format=\"binary\"/>\n", SP_TAG_DATAPTR, SP_FRINFO_FILE); + } + + unwind_key = __collector_tsd_create_key (sizeof (void*), NULL, NULL); + if (unwind_key == COLLECTOR_TSD_INVALID_KEY) + { + TprintfT (0, "unwind_init: ERROR: TSD key create failed.\n"); + __collector_log_write ("<%s kind=\"%s\" id=\"%d\">TSD key not created</%s>\n", + SP_TAG_EVENT, SP_JCMD_CERROR, COL_ERROR_GENERAL, SP_TAG_EVENT); + return; + } + TprintfT (0, "unwind_init() completed normally\n"); + return; +} + +void +__collector_ext_unwind_close () +{ + __collector_delete_handle (dhndl); + dhndl = NULL; +} + +void* +__collector_ext_return_address (unsigned level) +{ + if (NULL == UIDTable) //unwind not initialized yet + return NULL; + unsigned size = (level + 4) * sizeof (long); // need to strip __collector_get_return_address and its caller + ucontext_t context; + FILL_CONTEXT ((&context)); + char* buf = (char*) alloca (size); + if (buf == NULL) + { + TprintfT (DBG_LT0, "__collector_get_return_address: ERROR: alloca(%d) fails\n", size); + return NULL; + } + int sz = stack_unwind (buf, size, NULL, NULL, &context, 0); + if (sz < (level + 3) * sizeof (long)) + { + TprintfT (DBG_LT0, "__collector_get_return_address: size=%d, but stack_unwind returns %d\n", size, sz); + return NULL; + } + long *lbuf = (long*) buf; + TprintfT (DBG_LT2, "__collector_get_return_address: return %lx\n", lbuf[level + 2]); + return (void *) (lbuf[level + 2]); +} +/* + * Collector interface method getFrameInfo + */ +FrameInfo +__collector_get_frame_info (hrtime_t ts, int mode, void *arg) +{ + ucontext_t *context = NULL; + void *bptr = NULL; + CM_Array *array = NULL; + + int unwind_mode = 0; + int do_walk = 1; + + if (mode & FRINFO_NO_WALK) + do_walk = 0; + int bmode = mode & 0xffff; + int pseudo_context = 0; + if (bmode == FRINFO_FROM_STACK_ARG || bmode == FRINFO_FROM_STACK) + { + bptr = arg; + context = (ucontext_t*) alloca (sizeof (ucontext_t)); + FILL_CONTEXT (context); + unwind_mode |= bmode; + } + else if (bmode == FRINFO_FROM_UC) + { + context = (ucontext_t*) arg; + if (context == NULL) + return (FrameInfo) 0; + if (GET_SP (context) == 0) + pseudo_context = 1; + } + else if (bmode == FRINFO_FROM_ARRAY) + { + array = (CM_Array*) arg; + if (array == NULL || array->length <= 0) + return (FrameInfo) 0; + } + else + return (FrameInfo) 0; + + int max_frame_size = OVERHEAD_BYTES + NATIVE_FRAME_BYTES (max_native_nframes); + if (__collector_java_mode && __collector_java_asyncgetcalltrace_loaded && context && !pseudo_context) + max_frame_size += JAVA_FRAME_BYTES (max_java_nframes); + + Frame_packet *frpckt = alloca (sizeof (Frame_packet) + max_frame_size); + frpckt->type = FRAME_PCKT; + frpckt->hsize = sizeof (Frame_packet); + + char *d = (char*) (frpckt + 1); + int size = max_frame_size; + +#define MIN(a,b) ((a)<(b)?(a):(b)) + /* get Java info */ + if (__collector_java_mode && __collector_java_asyncgetcalltrace_loaded && context && !pseudo_context) + { + /* use only 2/3 of the buffer and leave the rest for the native stack */ + int tmpsz = MIN (size, JAVA_FRAME_BYTES (max_java_nframes)); + if (tmpsz > 0) + { + int sz = __collector_ext_jstack_unwind (d, tmpsz, context); + d += sz; + size -= sz; + } + } + + /* get native stack */ + if (context) + { + Stack_info *sinfo = (Stack_info*) d; + int sz = sizeof (Stack_info); + d += sz; + size -= sz; +#if ARCH(Intel) + if (omp_no_walk == 0) + do_walk = 1; +#endif + if (do_walk == 0) + unwind_mode |= FRINFO_NO_WALK; + + int tmpsz = MIN (size, NATIVE_FRAME_BYTES (max_native_nframes)); + if (tmpsz > 0) + { + sz = stack_unwind (d, tmpsz, bptr, NULL, context, unwind_mode); + d += sz; + size -= sz; + } + sinfo->kind = STACK_INFO; + sinfo->hsize = (d - (char*) sinfo); + } + + /* create a stack image from user data */ + if (array && array->length > 0) + { + Stack_info *sinfo = (Stack_info*) d; + int sz = sizeof (Stack_info); + d += sz; + size -= sz; + sz = array->length; + if (sz > size) + sz = size; // YXXX should we mark this with truncation frame? + __collector_memcpy (d, array->bytes, sz); + d += sz; + size -= sz; + sinfo->kind = STACK_INFO; + sinfo->hsize = (d - (char*) sinfo); + } + + /* Compute the total size */ + frpckt->tsize = d - (char*) frpckt; + FrameInfo uid = compute_uid (frpckt); + return uid; +} + +FrameInfo +compute_uid (Frame_packet *frp) +{ + uint64_t idxs[LAST_INFO]; + uint64_t uid = ROOT_UID; + uint64_t idx = ROOT_IDX; + + Common_info *cinfo = (Common_info*) ((char*) frp + frp->hsize); + char *end = (char*) frp + frp->tsize; + for (;;) + { + if ((char*) cinfo >= end || cinfo->hsize == 0 || + (char*) cinfo + cinfo->hsize > end) + break; + + /* Start with a different value to avoid matching with uid */ + uint64_t uidt = 1; + uint64_t idxt = 1; + long *ptr = (long*) ((char*) cinfo + cinfo->hsize); + long *bnd = (long*) ((char*) cinfo + sizeof (Common_info)); + TprintfT (DBG_LT2, "compute_uid: Cnt=%ld: ", (long) cinfo->hsize); + while (ptr > bnd) + { + long val = *(--ptr); + tprintf (DBG_LT2, "0x%8.8llx ", (unsigned long long) val); + uidt = (uidt + val) * ROOT_UID; + idxt = (idxt + val) * ROOT_IDX; + uid = (uid + val) * ROOT_UID; + idx = (idx + val) * ROOT_IDX; + } + if (cinfo->kind == STACK_INFO || cinfo->kind == JAVA_INFO) + { + cinfo->uid = uidt; + idxs[cinfo->kind] = idxt; + } + cinfo = (Common_info*) ((char*) cinfo + cinfo->hsize); + } + tprintf (DBG_LT2, "\n"); + + /* Check if we have already recorded that uid. + * The following fragment contains benign data races. + * It's important, though, that all reads from UIDTable + * happen before writes. + */ + int found1 = 0; + int idx1 = (int) ((idx >> 44) % UIDTableSize); + if (UIDTable[idx1] == uid) + found1 = 1; + int found2 = 0; + int idx2 = (int) ((idx >> 24) % UIDTableSize); + if (UIDTable[idx2] == uid) + found2 = 1; + int found3 = 0; + int idx3 = (int) ((idx >> 4) % UIDTableSize); + if (UIDTable[idx3] == uid) + found3 = 1; + if (!found1) + UIDTable[idx1] = uid; + if (!found2) + UIDTable[idx2] = uid; + if (!found3) + UIDTable[idx3] = uid; + + if (found1 || found2 || found3) + return (FrameInfo) uid; + frp->uid = uid; + + /* Compress info's */ + cinfo = (Common_info*) ((char*) frp + frp->hsize); + for (;;) + { + if ((char*) cinfo >= end || cinfo->hsize == 0 || + (char*) cinfo + cinfo->hsize > end) + break; + if (cinfo->kind == STACK_INFO || cinfo->kind == JAVA_INFO) + { + long *ptr = (long*) ((char*) cinfo + sizeof (Common_info)); + long *bnd = (long*) ((char*) cinfo + cinfo->hsize); + uint64_t uidt = cinfo->uid; + uint64_t idxt = idxs[cinfo->kind]; + int found = 0; + int first = 1; + while (ptr < bnd - 1) + { + int idx1 = (int) ((idxt >> 44) % UIDTableSize); + if (UIDTable[idx1] == uidt) + { + found = 1; + break; + } + else if (first) + { + first = 0; + UIDTable[idx1] = uidt; + } + long val = *ptr++; + uidt = uidt * ROOT_UID_INV - val; + idxt = idxt * ROOT_IDX_INV - val; + } + if (found) + { + char *d = (char*) ptr; + char *s = (char*) bnd; + if (!first) + { + int i; + for (i = 0; i<sizeof (uidt); i++) + { + *d++ = (char) uidt; + uidt = uidt >> 8; + } + } + int delta = s - d; + while (s < end) + *d++ = *s++; + cinfo->kind |= COMPRESSED_INFO; + cinfo->hsize -= delta; + frp->tsize -= delta; + end -= delta; + } + } + cinfo = (Common_info*) ((char*) cinfo + cinfo->hsize); + } + __collector_write_packet (dhndl, (CM_Packet*) frp); + return (FrameInfo) uid; +} + +FrameInfo +__collector_getUID (CM_Array *arg, FrameInfo suid) +{ + if (arg->length % sizeof (long) != 0 || + (long) arg->bytes % sizeof (long) != 0) + return (FrameInfo) - 1; + if (arg->length == 0) + return suid; + + uint64_t uid = suid ? suid : 1; + uint64_t idx = suid ? suid : 1; + long *ptr = (long*) ((char*) arg->bytes + arg->length); + long *bnd = (long*) (arg->bytes); + while (ptr > bnd) + { + long val = *(--ptr); + uid = (uid + val) * ROOT_UID; + idx = (idx + val) * ROOT_IDX; + } + + /* Check if we have already recorded that uid. + * The following fragment contains benign data races. + * It's important, though, that all reads from UIDTable + * happen before writes. + */ + int found1 = 0; + int idx1 = (int) ((idx >> 44) % UIDTableSize); + if (UIDTable[idx1] == uid) + found1 = 1; + int found2 = 0; + int idx2 = (int) ((idx >> 24) % UIDTableSize); + if (UIDTable[idx2] == uid) + found2 = 1; + int found3 = 0; + int idx3 = (int) ((idx >> 4) % UIDTableSize); + if (UIDTable[idx3] == uid) + found3 = 1; + + if (!found1) + UIDTable[idx1] = uid; + if (!found2) + UIDTable[idx2] = uid; + if (!found3) + UIDTable[idx3] = uid; + if (found1 || found2 || found3) + return (FrameInfo) uid; + + int sz = sizeof (Uid_packet) + arg->length; + if (suid) + sz += sizeof (suid); + Uid_packet *uidp = alloca (sz); + uidp->tsize = sz; + uidp->type = UID_PCKT; + uidp->flags = 0; + uidp->uid = uid; + + /* Compress */ + ptr = (long*) (arg->bytes); + bnd = (long*) ((char*) arg->bytes + arg->length); + long *dst = (long*) (uidp + 1); + uint64_t uidt = uid; + uint64_t idxt = idx; + uint64_t luid = suid; /* link uid */ + + while (ptr < bnd) + { + + long val = *ptr++; + *dst++ = val; + + if ((bnd - ptr) > sizeof (uidt)) + { + uidt = uidt * ROOT_UID_INV - val; + idxt = idxt * ROOT_IDX_INV - val; + int idx1 = (int) ((idxt >> 44) % UIDTableSize); + if (UIDTable[idx1] == uidt) + { + luid = uidt; + break; + } + } + } + if (luid) + { + char *d = (char*) dst; + for (int i = 0; i<sizeof (luid); i++) + { + *d++ = (char) luid; + luid = luid >> 8; + } + uidp->flags |= COMPRESSED_INFO; + uidp->tsize = d - (char*) uidp; + } + __collector_write_packet (dhndl, (CM_Packet*) uidp); + + return (FrameInfo) uid; +} + +int +__collector_getStackTrace (void *buf, int size, void *bptr, void *eptr, void *arg) +{ + if (arg == (void*) __collector_omp_stack_trace) + seen_omp = 1; + int do_walk = 1; + if (arg == NULL || arg == (void*) __collector_omp_stack_trace) + { + do_walk = (arg == (void*) __collector_omp_stack_trace && omp_no_walk) ? 0 : 1; + ucontext_t *context = (ucontext_t*) alloca (sizeof (ucontext_t)); + FILL_CONTEXT (context); + arg = context; + } + int unwind_mode = 0; + if (do_walk == 0) + unwind_mode |= FRINFO_NO_WALK; + return stack_unwind (buf, size, bptr, eptr, arg, unwind_mode); +} + +#if ARCH(SPARC) +/* + * These are important data structures taken from the header files reg.h and + * ucontext.h. They are used for the stack trace algorithm explained below. + * + * typedef struct ucontext { + * u_long uc_flags; + * struct ucontext *uc_link; + * usigset_t uc_sigmask; + * stack_t uc_stack; + * mcontext_t uc_mcontext; + * long uc_filler[23]; + * } ucontext_t; + * + * #define SPARC_MAXREGWINDOW 31 + * + * struct rwindow { + * greg_t rw_local[8]; + * greg_t rw_in[8]; + * }; + * + * #define rw_fp rw_in[6] + * #define rw_rtn rw_in[7] + * + * struct gwindows { + * int wbcnt; + * int *spbuf[SPARC_MAXREGWINDOW]; + * struct rwindow wbuf[SPARC_MAXREGWINDOW]; + * }; + * + * typedef struct gwindows gwindows_t; + * + * typedef struct { + * gregset_t gregs; + * gwindows_t *gwins; + * fpregset_t fpregs; + * long filler[21]; + * } mcontext_t; + * + * The stack would look like this when SIGPROF occurrs. + * + * ------------------------- <- high memory + * | | + * | | + * ------------------------- + * | | + * ------------------------- <- fp' <-| + * | | | + * : : | + * | | | + * ------------------------- | + * | fp |----------| + * | | + * ------------------------- <- sp' + * | | | | + * | gwins | <- saved stack pointers & | | + * | | register windows | |- mcontext + * ------------------------- | | + * | gregs | <- saved registers | | + * ------------------------- | + * | | |- ucontext + * ------------------------- <- ucp (ucontext pointer) | + * | | | + * | | |- siginfo + * ------------------------- <- sip (siginfo pointer) | + * | | + * ------------------------- <- sp + * + * Then the signal handler is called with: + * handler( signo, sip, uip ); + * When gwins is null, all the stack frames are saved in the user stack. + * In that case we can find sp' from gregs and walk the stack for a backtrace. + * However, if gwins is not null we will have a more complicated case. + * Wbcnt(in gwins) tells you how many saved register windows are valid. + * This is important because the kernel does not allocate the entire array. + * And the top most frame is saved in the lowest index element. The next + * paragraph explains the possible causes. + * + * There are two routines in the kernel to flush out user register windows. + * flush_user_windows and flush_user_windows_to_stack + * The first routine will not cause a page fault. Therefore if the user + * stack is not in memory, the register windows will be saved to the pcb. + * This can happen when the kernel is trying to deliver a signal and + * the user stack got swap out. The kernel will then build a new context for + * the signal handler and the saved register windows will + * be copied to the ucontext as show above. On the other hand, + * flush_user_windows_to_stack can cause a page fault, and if it failed + * then there is something wrong (stack overflow, misalign). + * The first saved register window does not necessary correspond to the + * first stack frame. So the current stack pointer must be compare with + * the stack pointers in spbuf to find a match. + * + * We will also follow the uc_link field in ucontext to trace also nested + * signal stack frames. + * + */ + +/* Dealing with trap handlers. + * When a user defined trap handler is invoked the return address + * (or actually the address of an instruction that raised the trap) + * is passed to the trap handler in %l6, whereas saved %o7 contains + * garbage. First, we need to find out if a particular pc belongs + * to the trap handler, and if so, take the %l6 value from the stack rather + * than %o7 from either the stack or the register. + * There are three possible situations represented + * by the following stacks: + * + * MARKER MARKER MARKER + * trap handler pc __func pc before 'save' __func pc after 'save' + * %l6 %o7 from reg %o7 (garbage) + * ... %l6 trap handler pc + * ... %l6 + * ... + * where __func is a function called from the trap handler. + * + * Currently this is implemented to only deal with __misalign_trap_handler + * set for v9 FORTRAN applications. Implementation of IN_TRAP_HANDLER + * macro shows it. A general solution is postponed. + */ + +/* Special handling of unwind through the parallel loop barrier code: + * + * The library defines two symbols, __mt_EndOfTask_Barrier_ and + * __mt_EndOfTask_Barrier_Dummy_ representing the first word of + * the barrier sychronization code, and the first word following + * it. Whenever the leaf PC is between these two symbols, + * the unwind code is special-cased as follows: + * The __mt_EndOfTask_Barrier_ function is guaranteed to be a leaf + * function, so its return address is in a register, not saved on + * the stack. + * + * MARKER + * __mt_EndOfTask_Barrier_ PC -- the leaf PC + * loop body function address for the task -- implied caller of __mt_EndOfTask_Barrier_ + * this address is taken from the %O0 register + * {mt_master or mt_slave} -- real caller of __mt_EndOfTask_Barrier_ + * ... + * + * With this trick, the analyzer will show the time in the barrier + * attributed to the loop at the end of which the barrier synchronization + * is taking place. That loop body routine, will be shown as called + * from the function from which it was extracted, which will be shown + * as called from the real caller, either the slave or master library routine. + */ + +/* + * These no-fault-load (0x82) assembly functions are courtesy of Rob Gardner. + * + * Note that 0x82 is ASI_PNF. See + * http://lxr.free-electrons.com/source/arch/sparc/include/uapi/asm/asi.h#L134 + * ASI address space identifier; PNF primary no fault + */ + +/* load an int from an address */ + +/* if the address is illegal, return a 0 */ +static int +SPARC_no_fault_load_int (void *addr) +{ + int val; + __asm__ __volatile__( + "lda [%1] 0x82, %0\n\t" + : "=r" (val) + : "r" (addr) + ); + + return val; +} + +/* check if an address is invalid + * + * A no-fault load of an illegal address still faults, but it does so silently to the calling process. + * It returns a 0, but so could a load of a legal address. + * So, we time the load. A "fast" load must be a successful load. + * A "slow" load is probably a fault. + * Since it could also be a cache/TLB miss or other abnormality, + * it's safest to retry a slow load. + * The cost of trying a valid address should be some nanosecs. + * The cost of trying an invalid address up to 10 times could be some microsecs. + */ +#if 0 +static +int invalid_SPARC_addr(void *addr) +{ + long t1, t2; + int i; + + for (i=0; i<10; i++) { + __asm__ __volatile__( + "rd %%tick, %0\n\t" + "lduba [%2] 0x82, %%g0\n\t" + "rd %%tick, %1\n\t" + : "=r" (t1), "=r" (t2) + : "r" (addr) ); + if ( (t2 - t1) < 100 ) + return 0; + } + return 1; +} +#endif + +/* + * The standard SPARC procedure-calling convention is that the + * calling PC (for determining the return address when the procedure + * is finished) is placed in register %o7. A called procedure + * typically executes a "save" instruction that shifts the register + * window, and %o7 becomes %i7. + * + * Optimized leaf procedures do not shift the register window. + * They assume the return address will remain %o7. So when + * we process a leaf PC, we walk instructions to see if there + * is a call, restore, or other instruction that would indicate + * we can IGNORE %o7 because this is NOT a leaf procedure. + * + * If a limited instruction walk uncovers no such hint, we save + * not only the PC but the %o7 value as well... just to be safe. + * Later, in DBE post-processing of the call stacks, we decide + * whether any recorded %o7 value should be used as a caller + * frame or should be discarded. + */ + +#define IS_ILLTRAP(x) (((x) & 0xc1c00000) == 0) +#define IS_SAVE(x) (((x) & 0xc1f80000) == 0x81e00000) +#define IS_MOVO7R(x) (((x) & 0xc1f8201f) == 0x8160000f) +#define IS_MOVRO7(x) (((x) & 0xfff82000) == 0x9f600000) +#define IS_ORRG0O7(x) (((x) & 0xff78201f) == 0x9e100000) +#define IS_ORG0RO7(x) (((x) & 0xff7fe000) == 0x9e100000) +#define IS_ORG0O7R(x) (((x) & 0xc17fe01f) == 0x8010000f) +#define IS_ORO7G0R(x) (((x) & 0xc17fe01f) == 0x8013c000) +#define IS_RESTORE(x) (((x) & 0xc1f80000) == 0x81e80000) +#define IS_RET(x) ((x) == 0x81c7e008) +#define IS_RETL(x) ((x) == 0x81c3e008) +#define IS_RETURN(x) (((x) & 0xc1f80000) == 0x81c80000) +#define IS_BRANCH(x) ((((x) & 0xc0000000) == 0) && (((x) & 0x01c00000) != 0x01000000)) +#define IS_CALL(x) (((x) & 0xc0000000) == 0x40000000) +#define IS_LDO7(x) (((x) & 0xfff80000) == 0xde000000) + +static long pagesize = 0; + +static int +process_leaf (long *lbuf, int ind, int lsize, void *context) +{ + greg_t pc = GET_PC (context); + greg_t o7 = GET_GREG (context, REG_O7); + + /* omazur: TBR START -- not used */ + if (IN_BARRIER (pc)) + { + if (ind < lsize) + lbuf[ind++] = pc; + if (ind < lsize) + lbuf[ind++] = GET_GREG (context, REG_O0); + return ind; + } + /* omazur: TBR END */ +#if WSIZE(64) + if (IN_TRAP_HANDLER (pc)) + { + if (ind < lsize) + lbuf[ind++] = pc; + return ind; + } +#endif + unsigned *instrp = (unsigned *) pc; + unsigned *end_addr = instrp + 20; + while (instrp < end_addr) + { + unsigned instr = *instrp++; + if (IS_ILLTRAP (instr)) + break; + else if (IS_SAVE (instr)) + { + if (ind < lsize) + lbuf[ind++] = pc; + if (o7 && ind < lsize) + lbuf[ind++] = o7; + return ind; + } + else if (IS_MOVO7R (instr) || IS_ORG0O7R (instr) || IS_ORO7G0R (instr)) + break; + else if (IS_MOVRO7 (instr) || IS_ORG0RO7 (instr)) + { + int rs2 = (instr & 0x1f) + REG_G1 - 1; + o7 = (rs2 <= REG_O7) ? GET_GREG (context, rs2) : 0; + break; + } + else if (IS_ORRG0O7 (instr)) + { + int rs2 = ((instr & 0x7c000) >> 14) + REG_G1 - 1; + o7 = (rs2 <= REG_O7) ? GET_GREG (context, rs2) : 0; + break; + } + else if (IS_RESTORE (instr)) + { + o7 = 0; + break; + } + else if (IS_RETURN (instr)) + { + o7 = 0; + break; + } + else if (IS_RET (instr)) + { + o7 = 0; + break; + } + else if (IS_RETL (instr)) + { + /* process delay slot */ + instr = *instrp++; + if (IS_RESTORE (instr)) + o7 = 0; + break; + } + else if (IS_BRANCH (instr)) + { + unsigned *backbegin = ((unsigned *) pc - 1); + unsigned *backend = backbegin - 12 + (instrp - (unsigned *) pc); + while (backbegin > backend) + { + // 21920143 stack unwind: SPARC process_leaf backtracks too far + /* + * We've already dereferenced backbegin+1. + * So if backbegin is on the same page, we're fine. + * If we've gone to a different page, possibly things are not fine. + * We don't really know how to test that. + * Let's just assume the worst: that dereferencing backbegin would segv. + * We won't know if we're in a leaf function or not. + */ + if (pagesize == 0) + pagesize = CALL_UTIL (sysconf)(_SC_PAGESIZE); + if ((((long) (backbegin + 1)) & (pagesize - 1)) < sizeof (unsigned*)) + break; + unsigned backinstr = *backbegin--; + if (IS_LDO7 (backinstr)) + { + o7 = 0; + break; + } + else if (IS_ILLTRAP (backinstr)) + break; + else if (IS_RETURN (backinstr)) + break; + else if (IS_RET (backinstr)) + break; + else if (IS_RETL (backinstr)) + break; + else if (IS_CALL (backinstr)) + break; + else if (IS_SAVE (backinstr)) + { + o7 = 0; + break; + } + } + break; + } + else if (IS_CALL (instr)) + o7 = 0; + } + +#if WSIZE(64) + if (o7 != 0 && ((long) o7) < 32 && ((long) o7) > -32) + { + /* 20924821 SEGV in unwind code on SPARC/Linux + * We've seen this condition in some SPARC-Linux runs. + * o7 is non-zero but not a valid address. + * Values like 4 or -7 have been seen. + * Let's check if o7 is unreasonably small. + * If so, set to 0 so that it won't be recorded. + * Otherwise, there is risk of it being dereferenced in process_sigreturn(). + */ + // __collector_log_write("<event kind=\"%s\" id=\"%d\">time %lld, internal debug unwind at leaf; o7 = %ld, pc = %x</event>\n", + // SP_JCMD_COMMENT, COL_COMMENT_NONE, __collector_gethrtime() - __collector_start_time, (long) o7, pc ); + o7 = 0; + } +#endif + + if (o7) + { + if (ind < lsize) + lbuf[ind++] = SP_LEAF_CHECK_MARKER; + if (ind < lsize) + lbuf[ind++] = pc; + if (ind < lsize) + lbuf[ind++] = o7; + } + else if (ind < lsize) + lbuf[ind++] = pc; + return ind; +} + +#if WSIZE(64) +// detect signal handler +static int +process_sigreturn (long *lbuf, int ind, int lsize, unsigned char * tpc, + struct frame **pfp, void * bptr, int extra_frame) +{ + // cheap checks whether tpc is obviously not an instruction address + if ((4096 > (unsigned long) tpc) // the first page is off limits + || (3 & (unsigned long) tpc)) + return ind; // the address is not aligned + + // get the instruction at tpc, skipping over as many as 7 nop's (0x01000000) + int insn, i; + for (i = 0; i < 7; i++) + { + insn = SPARC_no_fault_load_int ((void *) tpc); + if (insn != 0x01000000) + break; + tpc += 4; + } + + // we're not expecting 0 (and it could mean an illegal address) + if (insn == 0) + return ind; + + // We are looking for __rt_sigreturn_stub with the instruction + // 0x82102065 : mov 0x65 /* __NR_rt_sigreturn */, %g1 + if (insn == 0x82102065) + { + /* + * according to linux kernel source code, + * syscall(_NR_rt_sigreturn) uses the following data in stack: + * struct rt_signal_frame { + * struct sparc_stackf ss; + * siginfo_t info; + * struct pt_regs regs; + * ....}; + * sizeof(struct sparc_stackf) is 192; + * sizeof(siginfo_t) is 128; + * we need to get the register values from regs, which is defined as: + * struct pt_regs { + * unsigned long u_regs[16]; + * unsigned long tstate; + * unsigned long tpc; + * unsigned long tnpc; + * ....}; + * pc and fp register has offset of 120 and 112; + * the pc of kill() is stored in tnpc, whose offest is 136. + */ + greg_t pc = *((unsigned long*) ((char*) ((*pfp)) + 192 + 128 + 136)); + greg_t pc1 = *((unsigned long*) ((char*) ((*pfp)) + 192 + 128 + 120)); + (*pfp) = *((struct frame**) ((char*) ((*pfp)) + 192 + 128 + 112)); + if (pc && pc1) + { + if (bptr != NULL && extra_frame && ((char*) (*pfp) + STACK_BIAS) < (char*) bptr && ind < 2) + { + lbuf[0] = pc1; + if (ind == 0) + ind++; + } + if (bptr == NULL || ((char*) (*pfp) + STACK_BIAS) >= (char*) bptr) + { + if (ind < lsize) + lbuf[ind++] = (unsigned long) tpc; + if (ind < lsize) + lbuf[ind++] = pc; + if (ind < lsize) + lbuf[ind++] = pc1; + } + } + DprintfT (SP_DUMP_UNWIND, "unwind.c: resolved sigreturn pc=0x%lx, pc1=0x%lx, fp=0x%lx\n", pc, pc1, *(pfp)); + } + return ind; +} +#endif + +/* + * int stack_unwind( char *buf, int size, ucontext_t *context ) + * This routine looks into the mcontext and + * trace stack frames to record return addresses. + */ +int +stack_unwind (char *buf, int size, void *bptr, void *eptr, ucontext_t *context, int mode) +{ + /* + * trace the stack frames from user stack. + * We are assuming that the frame pointer and return address + * are null when we are at the top level. + */ + long *lbuf = (long*) buf; + int lsize = size / sizeof (long); + struct frame *fp = (struct frame *) GET_SP (context); /* frame pointer */ + greg_t pc; /* program counter */ + int extra_frame = 0; + if ((mode & 0xffff) == FRINFO_FROM_STACK) + extra_frame = 1; + + int ind = 0; + if (bptr == NULL) + ind = process_leaf (lbuf, ind, lsize, context); + + int extra_frame = 0; + if ((mode & 0xffff) == FRINFO_FROM_STACK) + extra_frame = 1; + int ind = 0; + if (bptr == NULL) + ind = process_leaf (lbuf, ind, lsize, context); + + while (fp) + { + if (ind >= lsize) + break; + fp = (struct frame *) ((char *) fp + STACK_BIAS); + if (eptr && fp >= (struct frame *) eptr) + { + ind = ind >= 2 ? ind - 2 : 0; + break; + } +#if WSIZE(64) // detect signal handler + unsigned char * tpc = ((unsigned char*) (fp->fr_savpc)); + struct frame * tfp = (struct frame*) ((char*) (fp->fr_savfp) + STACK_BIAS); + int old_ind = ind; + ind = process_sigreturn (lbuf, old_ind, lsize, tpc, &tfp, bptr, extra_frame); + if (ind != old_ind) + { + pc = (greg_t) tpc; + fp = tfp; + } + else +#endif + { +#if WSIZE(64) + if (IN_TRAP_HANDLER (lbuf[ind - 1])) + pc = fp->fr_local[6]; + else + pc = fp->fr_savpc; +#else + pc = fp->fr_savpc; +#endif + fp = fp->fr_savfp; + if (pc) + { + if (bptr != NULL && extra_frame && ((char*) fp + STACK_BIAS) < (char*) bptr && ind < 2) + { + lbuf[0] = pc; + if (ind == 0) + ind++; + } + if (bptr == NULL || ((char*) fp + STACK_BIAS) >= (char*) bptr) + lbuf[ind++] = pc; + } + } + + /* 4616238: _door_return may have a frame that has non-zero + * saved stack pointer and zero pc + */ + if (pc == (greg_t) NULL) + break; + } + + if (ind >= lsize) + { /* truncated stack handling */ + ind = lsize - 1; + lbuf[ind++] = SP_TRUNC_STACK_MARKER; + } + return ind * sizeof (long); +} + +#elif ARCH(Intel) + +/* get __NR_<syscall_name> constants */ +#include <syscall.h> + +/* + * From uts/intel/ia32/os/sendsig.c: + * + * An amd64 signal frame looks like this on the stack: + * + * old %rsp: + * <128 bytes of untouched stack space> + * <a siginfo_t [optional]> + * <a ucontext_t> + * <siginfo_t *> + * <signal number> + * new %rsp: <return address (deliberately invalid)> + * + * The signal number and siginfo_t pointer are only pushed onto the stack in + * order to allow stack backtraces. The actual signal handling code expects the + * arguments in registers. + * + * An i386 SVR4/ABI signal frame looks like this on the stack: + * + * old %esp: + * <a siginfo32_t [optional]> + * <a ucontext32_t> + * <pointer to that ucontext32_t> + * <pointer to that siginfo32_t> + * <signo> + * new %esp: <return address (deliberately invalid)> + */ + +#if WSIZE(32) +#define OPC_REG(x) ((x)&0x7) +#define MRM_REGD(x) (((x)>>3)&0x7) +#define MRM_REGS(x) ((x)&0x7) +#define RED_ZONE 0 +#elif WSIZE(64) +#define OPC_REG(x) (B|((x)&0x7)) +#define MRM_REGD(x) (R|(((x)>>3)&0x7)) +#define MRM_REGS(x) (B|((x)&0x7)) +#define RED_ZONE 16 +#endif +#define MRM_EXT(x) (((x)>>3)&0x7) +#define MRM_MOD(x) ((x)&0xc0) + +#define RAX 0 +#define RDX 2 +#define RSP 4 +#define RBP 5 + +struct AdvWalkContext +{ + unsigned char *pc; + unsigned long *sp; + unsigned long *sp_safe; + unsigned long *fp; + unsigned long *fp_sav; + unsigned long *fp_loc; + unsigned long rax; + unsigned long rdx; + unsigned long ra_sav; + unsigned long *ra_loc; + unsigned long regs[16]; + int tidx; /* targets table index */ + uint32_t cval; /* cache value */ +}; + +static unsigned long +getRegVal (struct AdvWalkContext *cur, int r, int *undefRez) +{ + if (cur->regs[r] == 0) + { + if (r == RBP) + { + tprintf (DBG_LT3, "getRegVal: returns cur->regs[RBP]=0x%lx cur->pc=0x%lx\n", + (unsigned long) cur->fp, (unsigned long) cur->pc); + return (unsigned long) cur->fp; + } + *undefRez = 1; + } + tprintf (DBG_LT3, "getRegVal: cur->regs[%d]=0x%lx cur->pc=0x%lx\n", + r, (unsigned long) cur->regs[r], (unsigned long) cur->pc); + return cur->regs[r]; +} + +static unsigned char * +check_modrm (unsigned char *pc) +{ + unsigned char modrm = *pc++; + unsigned char mod = MRM_MOD (modrm); + if (mod == 0xc0) + return pc; + unsigned char regs = modrm & 0x07; + if (regs == RSP) + { + if (mod == 0x40) + return pc + 2; // SIB + disp8 + if (mod == 0x80) + return pc + 5; // SIB + disp32 + return pc + 1; // SIB + } + if (mod == 0x0) + { + if (regs == RBP) + pc += 4; // disp32 + } + else if (mod == 0x40) + pc += 1; /* byte */ + else if (mod == 0x80) + pc += 4; /* word */ + return pc; +} + +static int +read_int (unsigned char *pc, int w) +{ + if (w == 1) + return *((char *) pc); + if (w == 2) + return *(short*) pc; + return *(int*) pc; +} + +/* Return codes */ +enum +{ + RA_FAILURE = 0, + RA_SUCCESS, + RA_END_OF_STACK, + RA_SIGRETURN, + RA_RT_SIGRETURN +}; + +/* Cache value encodings */ +static const uint32_t RA_FROMFP = (uint32_t) - 1; /* get the RA from the frame pointer */ +static const uint32_t RA_EOSTCK = (uint32_t) - 2; /* end-of-stack */ + + +#define MAXCTX 16 +#define MAXTRGTS 64 +#define MAXJMPREG 2 +#define MAXJMPREGCTX 3 + +#define DELETE_CURCTX() __collector_memcpy (cur, buf + (--nctx), sizeof (*cur)) + +/** + * Look for pc in AddrTable_RA_FROMFP and in AddrTable_RA_EOSTCK + * @param wctx + * @return + */ +static int +cache_get (struct WalkContext *wctx) +{ + unsigned long addr; + if (AddrTable_RA_FROMFP != NULL) + { + uint64_t idx = wctx->pc % ValTableSize; + addr = AddrTable_RA_FROMFP[ idx ]; + if (addr == wctx->pc) + { // Found in AddrTable_RA_FROMFP + unsigned long *sp = NULL; + unsigned long fp = wctx->fp; + /* validate fp before use */ + if (fp < wctx->sp || fp >= wctx->sbase - sizeof (*sp)) + return RA_FAILURE; + sp = (unsigned long *) fp; + fp = *sp++; + unsigned long ra = *sp++; + unsigned long tbgn = wctx->tbgn; + unsigned long tend = wctx->tend; + if (ra < tbgn || ra >= tend) + if (!__collector_check_segment (ra, &tbgn, &tend, 0)) + return RA_FAILURE; + unsigned long npc = adjust_ret_addr (ra, ra - tbgn, tend); + if (npc == 0) + return RA_FAILURE; + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d cached pc=0x%lX\n", __LINE__, npc); + wctx->pc = npc; + wctx->sp = (unsigned long) sp; + wctx->fp = fp; + wctx->tbgn = tbgn; + wctx->tend = tend; + return RA_SUCCESS; + } + } + if (NULL == AddrTable_RA_EOSTCK) + return RA_FAILURE; + uint64_t idx = wctx->pc % ValTableSize; + addr = AddrTable_RA_EOSTCK[ idx ]; + if (addr != wctx->pc) + return RA_FAILURE; + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d cached RA_END_OF_STACK\n", __LINE__); + return RA_END_OF_STACK; +} +/** + * Save pc in RA_FROMFP or RA_EOSTCK cache depending on val + * @param wctx + */ +static void +cache_put (struct WalkContext *wctx, const uint32_t val) +{ + if (RA_FROMFP == val) + { + // save pc in RA_FROMFP cache + if (NULL != AddrTable_RA_FROMFP) + { + uint64_t idx = wctx->pc % ValTableSize; + AddrTable_RA_FROMFP[ idx ] = wctx->pc; + if (NULL != AddrTable_RA_EOSTCK) + if (AddrTable_RA_EOSTCK[ idx ] == wctx->pc) + // invalidate pc in RA_EOSTCK cache + AddrTable_RA_EOSTCK[ idx ] = 0; + } + return; + } + if (RA_EOSTCK == val) + { + // save pc in RA_EOSTCK cache + if (NULL != AddrTable_RA_EOSTCK) + { + uint64_t idx = wctx->pc % ValTableSize; + AddrTable_RA_EOSTCK[ idx ] = wctx->pc; + if (NULL != AddrTable_RA_FROMFP) + { + if (AddrTable_RA_FROMFP[ idx ] == wctx->pc) + // invalidate pc in RA_FROMFP cache + AddrTable_RA_FROMFP[ idx ] = 0; + } + } + return; + } +} + +static int +process_return_real (struct WalkContext *wctx, struct AdvWalkContext *cur, int cache_on) +{ + if ((unsigned long) cur->sp >= wctx->sbase || + (unsigned long) cur->sp < wctx->sp) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: not in stack: %p [0x%lX-0x%lX]\n", + cur->sp, wctx->sp, wctx->sbase); + return RA_FAILURE; + } + + unsigned long ra; + if (cur->sp == cur->ra_loc) + { + ra = cur->ra_sav; + cur->sp++; + } + else if (cur->sp >= cur->sp_safe && (unsigned long) cur->sp < wctx->sbase) + ra = *cur->sp++; + else + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: not safe: %p >= %p\n", cur->sp, cur->sp_safe); + return RA_FAILURE; + } + if (ra == 0) + { + if (cache_on) + cache_put (wctx, RA_EOSTCK); + wctx->pc = ra; + wctx->sp = (unsigned long) cur->sp; + wctx->fp = (unsigned long) cur->fp; + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d RA_END_OF_STACK\n", __LINE__); + return RA_END_OF_STACK; + } + + unsigned long tbgn = wctx->tbgn; + unsigned long tend = wctx->tend; + if (ra < tbgn || ra >= tend) + { + if (!__collector_check_segment (ra, &tbgn, &tend, 0)) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: not in segment: 0x%lX [0x%lX-0x%lX]\n", + ra, wctx->tbgn, wctx->tend); + return RA_FAILURE; + } + } + + if (cur->cval == RA_FROMFP) + { + if (wctx->fp == (unsigned long) (cur->sp - 2)) + { + if (cache_on) + cache_put (wctx, RA_FROMFP); + } + else + cur->cval = 0; + } + + unsigned long npc = adjust_ret_addr (ra, ra - tbgn, tend); + if (npc == 0) + { + if (cur->cval == RA_FROMFP) + { + /* We have another evidence that we can trust this RA */ + DprintfT (SP_DUMP_UNWIND, "unwind.c: trusted fp, pc = 0x%lX\n", wctx->pc); + wctx->pc = ra; + } + else + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: 0 after adjustment\n"); + return RA_FAILURE; + } + } + else + wctx->pc = npc; + wctx->sp = (unsigned long) cur->sp; + wctx->fp = (unsigned long) cur->fp; + wctx->tbgn = tbgn; + wctx->tend = tend; + return RA_SUCCESS; +} + +static int +process_return (struct WalkContext *wctx, struct AdvWalkContext *cur) +{ + return process_return_real (wctx, cur, 1); +} + +static void +omp_cache_put (unsigned long *cur_sp_safe, struct WalkContext * wctx_pc_save, + struct WalkContext *wctx, uint32_t val) +{ + if (omp_no_walk && (OmpCurCtxs == NULL || OmpCtxs == NULL || OmpVals == NULL || OmpRAs == NULL)) + { + size_t sz = OmpValTableSize * sizeof (*OmpCurCtxs); + OmpCurCtxs = (struct WalkContext *) __collector_allocCSize (__collector_heap, sz, 1); + sz = OmpValTableSize * sizeof (*OmpCtxs); + OmpCtxs = (struct WalkContext *) __collector_allocCSize (__collector_heap, sz, 1); + sz = OmpValTableSize * sizeof (*OmpVals); + OmpVals = (uint32_t*) __collector_allocCSize (__collector_heap, sz, 1); + sz = OmpValTableSize * sizeof (*OmpRAs); + OmpRAs = (unsigned long*) __collector_allocCSize (__collector_heap, sz, 1); + } + if (OmpCurCtxs == NULL || OmpCtxs == NULL || OmpVals == NULL || OmpRAs == NULL) + return; + +#define USE_18434988_OMP_CACHE_WORKAROUND +#ifndef USE_18434988_OMP_CACHE_WORKAROUND + uint64_t idx = wctx_pc_save->pc * ROOT_IDX; + OmpVals[ idx % OmpValTableSize ] = val; + idx = (idx + val) * ROOT_IDX; + __collector_memcpy (&(OmpCurCtxs[ idx % OmpValTableSize ]), wctx_pc_save, sizeof (struct WalkContext)); + idx = (idx + val) * ROOT_IDX; + __collector_memcpy (&(OmpCtxs[ idx % OmpValTableSize ]), wctx, sizeof (struct WalkContext)); +#endif + unsigned long *sp = NULL; + unsigned long fp = wctx_pc_save->fp; + int from_fp = 0; + if (val == RA_END_OF_STACK) + { + sp = (unsigned long *) (wctx->sp); + sp--; + TprintfT (DBG_LT1, "omp_cache_put: get sp from EOS, sp=%p\n", sp); + } + else + { + if (fp < wctx_pc_save->sp || fp >= wctx_pc_save->sbase - sizeof (*sp)) + { + sp = (unsigned long *) (wctx->sp); + sp--; + TprintfT (DBG_LT1, "omp_cache_put: get sp from sp, sp=%p\n", sp); + } + else + { + TprintfT (DBG_LT1, "omp_cache_put: get sp from fp=0x%lx\n", fp); + sp = (unsigned long *) fp; + from_fp = 1; + } + } + + if (sp < cur_sp_safe || ((unsigned long) sp >= wctx->sbase)) + return; + + unsigned long ra = *sp++; + if (from_fp) + { + unsigned long tbgn = wctx_pc_save->tbgn; + unsigned long tend = wctx_pc_save->tend; + if (ra < tbgn || ra >= tend) + { + sp = (unsigned long *) (wctx->sp); + sp--; + ra = *sp++; + } + } +#ifdef USE_18434988_OMP_CACHE_WORKAROUND + uint64_t idx1 = wctx_pc_save->pc * ROOT_IDX; + uint64_t idx2 = (idx1 + val) * ROOT_IDX; + uint64_t idx3 = (idx2 + val) * ROOT_IDX; + uint64_t idx4 = (idx3 + val) * ROOT_IDX; + OmpRAs [ idx4 % OmpValTableSize ] = 0; // lock + OmpVals[ idx1 % OmpValTableSize ] = val; + __collector_memcpy (&(OmpCurCtxs[ idx2 % OmpValTableSize ]), wctx_pc_save, sizeof (struct WalkContext)); + __collector_memcpy (&(OmpCtxs [ idx3 % OmpValTableSize ]), wctx, sizeof (struct WalkContext)); + OmpRAs [ idx4 % OmpValTableSize ] = ra; +#else + idx = (idx + val) * ROOT_IDX; + OmpRAs[ idx % OmpValTableSize ] = ra; +#endif + TprintfT (DBG_LT1, "omp_cache_put: pc=0x%lx\n", wctx_pc_save->pc); +} + +/* + * See bug 17166877 - malloc_internal unwind failure. + * Sometimes there are several calls right after ret, like: + * leave + * ret + * call xxx + * call xxxx + * call xxxxx + * If they are also jump targets, we should better not + * create new jump context for those, since they may + * end up into some other function. + */ +static int +is_after_ret (unsigned char * npc) +{ + if (*npc != 0xe8) + return 0; + unsigned char * onpc = npc; + int ncall = 1; + int maxsteps = 10; + int mincalls = 3; + int steps = 0; + while (*(npc - 5) == 0xe8 && steps < maxsteps) + { + npc -= 5; + ncall++; + steps++; + } + if (*(npc - 1) != 0xc3 || *(npc - 2) != 0xc9) + return 0; + steps = 0; + while (*(onpc + 5) == 0xe8 && steps < maxsteps) + { + onpc += 5; + ncall++; + steps++; + } + if (ncall < mincalls) + return 0; + return 1; +} + +static int +find_i386_ret_addr (struct WalkContext *wctx, int do_walk) +{ + if (wctx->sp == 0) + // Some artificial contexts may have %sp set to 0. See SETFUNCTIONCONTEXT() + return RA_FAILURE; + + /* Check cached values */ + int retc = cache_get (wctx); + if (retc != RA_FAILURE) + return retc; + + /* An attempt to perform code analysis for call stack tracing */ + unsigned char opcode; + unsigned char extop; + unsigned char extop2; + unsigned char modrm; + int imm8; /* immediate operand, byte */ + int immv; /* immediate operand, word(2) or doubleword(4) */ + int reg; /* register code */ + + /* Buffer for branch targets (analysis stoppers) */ + unsigned char *targets[MAXTRGTS]; + int ntrg = 0; /* number of entries in the table */ + targets[ntrg++] = (unsigned char*) wctx->pc; + targets[ntrg++] = (unsigned char*) - 1; + + struct AdvWalkContext buf[MAXCTX]; + struct AdvWalkContext *cur = buf; + CALL_UTIL (memset)((void*) cur, 0, sizeof (*cur)); + + cur->pc = (unsigned char*) wctx->pc; + cur->sp = (unsigned long*) wctx->sp; + cur->sp_safe = cur->sp - RED_ZONE; /* allow for the 128-byte red zone on amd64 */ + cur->fp = (unsigned long*) wctx->fp; + cur->tidx = 1; + DprintfT (SP_DUMP_UNWIND, "\nstack_unwind (x86 walk):%d %p start\n", __LINE__, cur->pc); + + int nctx = 1; /* number of contexts being processed */ + int cnt = 8192; /* number of instructions to analyse */ + + /* + * The basic idea of our x86 stack unwind is that we don't know + * if we can trust the frame-pointer register. So we walk + * instructions to find a return instruction, at which point + * we know the return address is on the top of the stack, etc. + * + * A severe challenge to walking x86 instructions is when we + * encounter "jmp *(reg)" instructions, where we are expected + * to jump to the (unknown-to-us) contents of a register. + * + * The "jmp_reg" code here attempts to keep track of the + * context for such a jump, deferring any handling of such + * a difficult case. We continue with other contexts, hoping + * that some other walk will take us to a return instruction. + * + * If no other walk helps, we return to "jmp_reg" contexts. + * While we don't know the jump target, it is possible that the + * bytes immediately following the jmp_reg instruction represent + * one possible target, as might be the case when a "switch" + * statement is compiled. + * + * Unfortunately, the bytes following a "jmp_reg" instruction might + * instead be a jump target from somewhere else -- execution might + * never "fall through" from the preceding "jmp_reg". Those bytes + * might not even be instructions at all. There are many uses of + * jmp_reg instructions beyond just compiling switch statements. + * + * So walking the bytes after a "jmp_reg" instruction can lead + * to bugs and undefined behavior, including SEGV and core dump. + * + * We currently do not really understand the "jmp_reg" code below. + */ + int jmp_reg_switch_mode = 0; + int num_jmp_reg = 0; // number of jmp *reg met when switch mode is off or when in current switch case + int total_num_jmp_reg = 0; // number of total jmp *reg met + struct AdvWalkContext * jmp_reg_ctx[MAXJMPREG]; // context of jmp *reg met when switch mode is off or when in current switch case + struct AdvWalkContext * jmp_reg_switch_ctx[MAXJMPREG]; // context of jmp *reg used in switch cases + struct AdvWalkContext * jmp_reg_switch_backup_ctx = NULL; // context of the first jmp *reg used in switch cases + + int cur_jmp_reg_switch = 0; // current switch table + int num_jmp_reg_switch = 0; // number of switch table + int jmp_reg_switch_case = 0; // case number in current switch table + unsigned char * jmp_reg_switch_pc = NULL; // the start pc of current switch case + unsigned char * jmp_reg_switch_pc_old = NULL; // backup for deleteing context of jump target + unsigned char * jmp_reg_switch_base = NULL; // start pc for checking offsets + int max_jmp_reg_switch_case = 2; +#if WSIZE(32) + int max_switch_pc_offset = 512; +#else // WSIZE(64) + int max_switch_pc_offset = 1024; +#endif + int expected_num_jmp_reg = 1; // should be smaller than MAXJMPREG + int max_num_jmp_reg_seen = 4; // try to resolve return if there are so many such instructions + + + int save_ctx = 0; // flag to save walk context in the cache to speed up unwind + struct WalkContext wctx_pc_save; + if (do_walk == 0) + // do_walk is the flag indicating not walking through the instructions, resolving the RA from the stack fp first + __collector_memcpy (&wctx_pc_save, wctx, sizeof (struct WalkContext)); + +startWalk: + if (do_walk == 0) + { // try to resolve RA from stack frame pointer + if (OmpCurCtxs == NULL || OmpCtxs == NULL || OmpVals == NULL || OmpRAs == NULL) + { + do_walk = 1; + goto startWalk; + } + // before goto checkFP, try the RA from cache (key: WalkContext -> value: caller's WalkContext)) + uint64_t idx = wctx->pc * ROOT_IDX; + uint32_t val = OmpVals[idx % OmpValTableSize]; + idx = (idx + val) * ROOT_IDX; +#ifdef USE_18434988_OMP_CACHE_WORKAROUND + // Check ra: if it is 0 - then cache is invalid + uint64_t idx4; + idx4 = (idx + val) * ROOT_IDX; + idx4 = (idx4 + val) * ROOT_IDX; + if (0 == OmpRAs[ idx4 % OmpValTableSize ]) // Invalid cache + goto checkFP; +#endif + struct WalkContext saved_ctx; + __collector_memcpy (&saved_ctx, &OmpCurCtxs[ idx % OmpValTableSize ], sizeof (struct WalkContext)); + if (wctx->pc == saved_ctx.pc + && wctx->sp == saved_ctx.sp + && wctx->fp == saved_ctx.fp + && wctx->tbgn == saved_ctx.tbgn + && wctx->tend == saved_ctx.tend) + { // key match, RA may be valid + idx = (idx + val) * ROOT_IDX; + unsigned long *sp = NULL; + unsigned long fp = wctx->fp; + int from_fp = 0; + if (val == RA_END_OF_STACK) + { + DprintfT (SP_DUMP_UNWIND, "find_i386_ret_addr:%d -- RA_END_OF_STACK: pc=0x%lx\n", __LINE__, wctx->pc); + __collector_memcpy (wctx, &OmpCtxs[ idx % OmpValTableSize ], sizeof (struct WalkContext)); + return val; + } + else + { + if (fp < wctx->sp || fp >= wctx->sbase - sizeof (*sp)) + { + TprintfT (DBG_LT1, "omp_cache_get -- wrong fp: pc=0x%lx\n", wctx->pc); + sp = (unsigned long *) (OmpCtxs[ idx % OmpValTableSize ].sp); + sp--; + if (sp < cur->sp_safe || (unsigned long) sp >= wctx->sbase) + { + goto checkFP; + } + unsigned long ra = *sp; + uint64_t idx2 = (idx + val) * ROOT_IDX; + if (OmpRAs[ idx2 % OmpValTableSize ] == ra) + { + __collector_memcpy (wctx, &OmpCtxs[ idx % OmpValTableSize ], sizeof (struct WalkContext)); + TprintfT (DBG_LT1, "omp_cache_get -- ra match with target sp: pc=0x%lx, ra=0x%lx, val=%d\n", wctx->pc, ra, val); + return val; + } + TprintfT (DBG_LT1, "omp_cache_get -- ra mismatch: ra=0x%lx, expected ra=0x%lx, val=%d\n", ra, OmpRAs[ idx2 % OmpValTableSize ], val); + goto checkFP; + } + sp = (unsigned long *) fp; + from_fp = 1; + } + + uint64_t idx2 = (idx + val) * ROOT_IDX; + unsigned long ra = *sp++; + if (from_fp) + { + unsigned long tbgn = wctx->tbgn; + unsigned long tend = wctx->tend; + if (ra < tbgn || ra >= tend) + { + sp = (unsigned long *) (OmpCtxs[ idx % OmpValTableSize ].sp); + sp--; + //if (sp < cur->sp_safe - 16 || (unsigned long)sp >= wctx->sbase - sizeof(*sp)) { + // The check above was replaced with the check below, + // because we do not know why "- 16" and "- sizeof(*sp)" was used. + if (sp < cur->sp_safe || (unsigned long) sp >= wctx->sbase) + goto checkFP; + else + ra = *sp; + } + } + if (OmpRAs[ idx2 % OmpValTableSize ] == ra) + { + TprintfT (DBG_LT1, "omp_cache_get -- ra match: pc=0x%lx\n", wctx->pc); + __collector_memcpy (wctx, &OmpCtxs[ idx % OmpValTableSize ], sizeof (struct WalkContext)); + return val; + } + } + goto checkFP; + } + else + { + CALL_UTIL (memset)(jmp_reg_ctx, 0, MAXJMPREG * sizeof (struct AdvWalkContext *)); + CALL_UTIL (memset)(jmp_reg_switch_ctx, 0, MAXJMPREG * sizeof (struct AdvWalkContext *)); + } + while (cnt--) + { + if (nctx == 0 && (num_jmp_reg == expected_num_jmp_reg || jmp_reg_switch_mode == 1)) + { // no context available, try jmp switch mode + int i = 0; + if (num_jmp_reg == expected_num_jmp_reg) + jmp_reg_switch_mode = 0; // first jmp reg expected, restart switch mode + DprintfT (SP_DUMP_UNWIND, "unwind.c: begin switch mode, num_jmp_reg = %d, jmp_reg_switch_backup_ctx=%p, jmp_reg_switch_case=%d, jmp_reg_switch_mode=%d.\n", + num_jmp_reg, jmp_reg_switch_backup_ctx, jmp_reg_switch_case, jmp_reg_switch_mode); + // the ideal asm of switch is + // jmp reg + // ...//case 1 + // ret + // ...//case 2 + // ret + // ...//etc + if (jmp_reg_switch_mode == 0) + { + num_jmp_reg_switch = num_jmp_reg; // backup num_jmp_reg + jmp_reg_switch_mode = 1; // begin switch mode + for (i = 0; i < num_jmp_reg_switch; i++) + { + if (jmp_reg_switch_ctx[i] == NULL) + jmp_reg_switch_ctx[i] = (struct AdvWalkContext*) alloca (sizeof (*jmp_reg_switch_ctx[i])); + if (jmp_reg_switch_ctx[i] != NULL) + { // backup jmp_reg_ctx + __collector_memcpy (jmp_reg_switch_ctx[i], jmp_reg_ctx[i], sizeof (*jmp_reg_switch_ctx[i])); + cur_jmp_reg_switch = 0; // reset the current switch table + jmp_reg_switch_case = 0; // reset the case number in current switch table + } + } + if (jmp_reg_switch_backup_ctx == NULL) + { // only backup when the first jmp *reg is met for restoring later, if switch mode fails to resolve RA + jmp_reg_switch_backup_ctx = (struct AdvWalkContext*) alloca (sizeof (*jmp_reg_switch_backup_ctx)); + if (jmp_reg_switch_backup_ctx != NULL) + __collector_memcpy (jmp_reg_switch_backup_ctx, cur, sizeof (*cur)); + DprintfT (SP_DUMP_UNWIND, "unwind.c: back up context for switch mode.\n"); + } + } + if (jmp_reg_switch_mode == 1) + { // in the process of trying switch cases + if (cur_jmp_reg_switch == num_jmp_reg_switch) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: have tried all switch with max_jmp_reg_switch_case for each\n"); + if (jmp_reg_switch_backup_ctx != NULL) + __collector_memcpy (cur, jmp_reg_switch_backup_ctx, sizeof (*cur)); + int rc = process_return_real (wctx, cur, 0); + if (rc == RA_SUCCESS) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + break; // have tried all switch with max_jmp_reg_switch_case for each, goto checkFP + } + unsigned char *npc = jmp_reg_switch_ctx[cur_jmp_reg_switch]->pc; + if (jmp_reg_switch_case == 0) + // first switch case + npc = check_modrm (npc); // pc next to "jmp reg" instruction + else if (jmp_reg_switch_pc != NULL) + npc = jmp_reg_switch_pc; // // pc next to "ret" instruction of previous case + else + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: unexpected jum switch mode situation, jmp_reg_switch_case=%d, jmp_reg_switch_pc=%p\n", + jmp_reg_switch_case, jmp_reg_switch_pc); + break; //goto checkFP + } + jmp_reg_switch_base = npc; + struct AdvWalkContext *new = buf + nctx; + nctx += 1; + __collector_memcpy (new, jmp_reg_switch_ctx[cur_jmp_reg_switch], sizeof (*new)); + new->pc = npc; + cur = new; /* advance the new context first */ + jmp_reg_switch_pc = NULL; + jmp_reg_switch_case++; + if (jmp_reg_switch_case == max_jmp_reg_switch_case) + { // done many cases, change to another switch table + cur_jmp_reg_switch++; + jmp_reg_switch_case = 0; + } + } + num_jmp_reg = 0; + } + if (jmp_reg_switch_mode == 1) + { // when processing switch cases, check pc each time + unsigned long tbgn = wctx->tbgn; + unsigned long tend = wctx->tend; + if ((unsigned long) (cur->pc) < tbgn || (unsigned long) (cur->pc) >= tend) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: pc out of range, pc=0x%lx\n", (unsigned long) (cur->pc)); + break; + } + if (jmp_reg_switch_base != NULL && cur->pc > jmp_reg_switch_base + max_switch_pc_offset) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: limit the walk offset after jmp reg instruction\n"); + if (jmp_reg_switch_backup_ctx != NULL) + __collector_memcpy (cur, jmp_reg_switch_backup_ctx, sizeof (*cur)); + int rc = process_return_real (wctx, cur, 0); + if (rc == RA_SUCCESS) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + break; // limit the walk offset after jmp reg instruction, got checkFP + } + } + + if (nctx == 0) + break; +// dump_targets (__LINE__, ntrg, targets); + while (cur->pc > targets[cur->tidx]) + cur->tidx += 1; + if (cur->pc == targets[cur->tidx]) + { + /* Stop analysis. Delete context. */ + if (jmp_reg_switch_mode == 0 || cur->pc != jmp_reg_switch_pc_old) + { + if (jmp_reg_switch_mode == 1 && nctx == 1 && jmp_reg_switch_pc == NULL) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d old target, cur->pc=%p, jmp_reg_switch_pc=%p, nctx=%d\n", + __LINE__, cur->pc, jmp_reg_switch_pc, nctx); + jmp_reg_switch_pc = cur->pc; // save cp before delete context, may be used as a start of switch case + jmp_reg_switch_pc_old = jmp_reg_switch_pc; + } + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, old target.\n", __LINE__); + DELETE_CURCTX (); + if (cur >= buf + nctx) + cur = buf; + continue; + } + if (jmp_reg_switch_mode == 1 && cur->pc == jmp_reg_switch_pc_old) + jmp_reg_switch_pc_old = NULL; // reset jmp_reg_switch_pc_old to delete the context later when cur->pc != jmp_reg_switch_pc_old + } + + /* let's walk the next x86 instruction */ + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d cur:%ld pc=0x%lx %02x %02x %02x %02x %02x %02x %02x sp=0x%lx\n", + __LINE__, (long) (cur - buf), (unsigned long) cur->pc, + (int) cur->pc[0], (int) cur->pc[1], (int) cur->pc[2], + (int) cur->pc[3], (int) cur->pc[4], (int) cur->pc[5], + (int) cur->pc[6], (unsigned long) cur->sp); + int v = 4; /* Operand size */ + int a = 4; /* Address size */ + /* int W = 0; REX.W bit */ +#if WSIZE(64) + int R = 0; /* REX.R bit */ +#endif + int X = 0; /* REX.X bit */ + int B = 0; /* REX.B bit */ + /* Check prefixes */ + int done = 0; + while (!done) + { + opcode = *cur->pc++; + switch (opcode) + { + case 0x66: /* opd size override */ + v = 2; + break; + case 0x67: /*addr size override */ + a = 2; + break; +#if WSIZE(64) + case 0x40: /* REX */ + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + B = (opcode & 0x1) ? 8 : 0; + X = (opcode & 0x2) ? 8 : 0; + R = (opcode & 0x4) ? 8 : 0; + if (opcode & 0x8) /* 64 bit operand size */ + v = 8; + opcode = *cur->pc++; + done = 1; + break; +#endif + default: + done = 1; + break; + } + } + int z = (v == 8) ? 4 : v; + switch (opcode) + { + case 0x0: /* add Eb,Gb */ + case 0x01: /* add Ev,Gv */ + case 0x02: /* add Gb,Eb */ + case 0x03: /* add Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x04: /* add %al,Ib */ + cur->pc += 1; + break; + case 0x05: /* add %eax,Iz */ + cur->pc += z; + break; + case 0x06: /* push es */ + cur->sp -= 1; + break; + case 0x07: /* pop es */ + cur->sp += 1; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + break; + case 0x08: /* or Eb,Gb */ + case 0x09: /* or Ev,Gv */ + case 0x0a: /* or Gb,Eb */ + case 0x0b: /* or Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x0c: /* or %al,Ib */ + cur->pc += 1; + break; + case 0x0d: /* or %eax,Iz */ + cur->pc += z; + break; + case 0x0e: /* push cs */ + cur->sp -= 1; + break; + case 0x0f: /* two-byte opcodes */ + extop = *cur->pc++; + switch (extop) + { /* RTM or HLE */ + case 0x01: + extop2 = *cur->pc; + switch (extop2) + { + case 0xd5: /* xend */ + case 0xd6: /* xtest */ + cur->pc++; + break; + default: + break; + } + break; + case 0x03: + cur->pc = check_modrm (cur->pc); + break; + case 0x0b: + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, undefined instruction. opcode=0x%02x\n", + __LINE__, (int) opcode); + DELETE_CURCTX (); + break; + case 0x05: /* syscall */ + case 0x34: /* sysenter */ + if (cur->rax == __NR_exit) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode=0x%02x\n", + __LINE__, (int) opcode); + DELETE_CURCTX (); + break; + } + else if (cur->rax == __NR_rt_sigreturn) + { + if (jmp_reg_switch_mode == 1) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d give up return address under jmp switch mode, opcode=0x%02x\n", + __LINE__, (int) opcode); + goto checkFP; + } + wctx->sp = (unsigned long) cur->sp; + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_RT_SIGRETURN); + return RA_RT_SIGRETURN; + } +#if WSIZE(32) + else if (cur->rax == __NR_sigreturn) + { + if (jmp_reg_switch_mode == 1) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: give up return address under jmp switch mode, opcode = 0x34\n"); + goto checkFP; + } + wctx->sp = (unsigned long) cur->sp; + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_SIGRETURN); + return RA_SIGRETURN; + } +#endif + /* Check for Linus' trick in the vsyscall page */ + while (*cur->pc == 0x90) /* nop */ + cur->pc++; + if (*cur->pc == 0xeb) /* jmp imm8 */ + cur->pc += 2; + break; + case 0x0d: /* nop Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x10: /* xmm Vq,Wq */ + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + cur->pc = check_modrm (cur->pc); + break; + case 0x18: /* prefetch */ + cur->pc = check_modrm (cur->pc); + break; + case 0x1E: /* endbr64/endbr32 (f3 0f 1e .. ) is parsing as repz nop edx */ + cur->pc += 2; + break; + case 0x1f: /* nop Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x28: /* xmm Vq,Wq */ + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + cur->pc = check_modrm (cur->pc); + break; + case 0x30: /* wrmsr */ + case 0x31: /* rdtsc */ + case 0x32: /* rdmsr */ + case 0x33: /* rdpmc */ + break; + /* case 0x34: sysenter (see above) */ + case 0x38: case 0x3a: + extop2 = *cur->pc++; + cur->pc = check_modrm (cur->pc); + // 21275311 Unwind failure in native stack for java application running on jdk8 + // Three-byte opcodes "66 0f 3a ??" should consume an additional "immediate" byte. + if (extop == 0x3a) + cur->pc++; + break; + case 0x40: case 0x41: case 0x42: case 0x43: /* CMOVcc Gv,Ev */ + case 0x44: case 0x45: case 0x46: case 0x47: + case 0x48: case 0x49: case 0x4a: case 0x4b: + case 0x4c: case 0x4d: case 0x4e: case 0x4f: + cur->pc = check_modrm (cur->pc); + break; + case 0x50: case 0x51: case 0x52: case 0x53: + case 0x54: case 0x55: case 0x56: case 0x57: + case 0x58: case 0x59: case 0x5a: case 0x5b: + case 0x5c: case 0x5d: case 0x5e: case 0x5f: + case 0x60: case 0x61: case 0x62: case 0x63: + case 0x64: case 0x65: case 0x66: case 0x67: + case 0x68: case 0x69: case 0x6a: case 0x6b: + case 0x6c: case 0x6d: case 0x6e: case 0x6f: + cur->pc = check_modrm (cur->pc); + break; + case 0x70: case 0x71: case 0x72: case 0x73: + cur->pc = check_modrm (cur->pc) + 1; + break; + case 0x74: case 0x75: case 0x76: + cur->pc = check_modrm (cur->pc); + break; + case 0x77: + break; + case 0x7c: case 0x7d: case 0x7e: case 0x7f: + cur->pc = check_modrm (cur->pc); + break; + case 0x80: case 0x81: case 0x82: case 0x83: /* Jcc Jz */ + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + immv = read_int (cur->pc, z); + cur->pc += z; + if (nctx < (jmp_reg_switch_mode ? MAXJMPREGCTX : MAXCTX)) + { + int tidx = 0; + unsigned char *npc = cur->pc + immv; + if ((unsigned long) npc < wctx->tbgn || (unsigned long) npc >= wctx->tend) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode=0x%02x\n", + __LINE__, (int) opcode); + DELETE_CURCTX (); + break; + } + if (is_after_ret (npc)) + break; + while (npc > targets[tidx]) + tidx += 1; + if (npc != targets[tidx]) + { + if (ntrg < MAXTRGTS) + { + for (int i = 0; i < nctx; i++) + if (buf[i].tidx >= tidx) + buf[i].tidx++; + + /* insert a new target */ + for (int i = ntrg; i > tidx; i--) + targets[i] = targets[i - 1]; + ntrg += 1; + targets[tidx++] = npc; + } + else + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d ntrg=max(%d)\n", + __LINE__, ntrg); + struct AdvWalkContext *new = buf + nctx; + nctx += 1; + __collector_memcpy (new, cur, sizeof (*new)); + new->pc = npc; + new->tidx = tidx; + cur = new; /* advance the new context first */ + continue; + } + } + else + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d nctx=max(%d)\n", + __LINE__, ntrg); + break; + case 0x90: case 0x91: case 0x92: case 0x93: /* setcc Eb */ + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + cur->pc = check_modrm (cur->pc); + break; + case 0xa0: /* push fs */ + cur->sp -= 1; + break; + case 0xa1: /* pop fs */ + cur->sp += 1; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + break; + case 0xa2: /* cpuid */ + break; + case 0xa3: /* bt Ev,Gv */ + cur->pc = check_modrm (cur->pc); + break; + case 0xa4: /* shld Ev,Gv,Ib */ + cur->pc = check_modrm (cur->pc); + cur->pc += 1; + break; + case 0xa5: /* shld Ev,Gv,%cl */ + cur->pc = check_modrm (cur->pc); + break; + case 0xa8: /* push gs */ + cur->sp -= 1; + break; + case 0xa9: /* pop gs */ + cur->sp += 1; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + break; + case 0xaa: /* rsm */ + break; + case 0xab: /* bts Ev,Gv */ + cur->pc = check_modrm (cur->pc); + break; + case 0xac: /* shrd Ev,Gv,Ib */ + cur->pc = check_modrm (cur->pc); + cur->pc += 1; + break; + case 0xad: /* shrd Ev,Gv,%cl */ + cur->pc = check_modrm (cur->pc); + break; + case 0xae: /* group15 */ + cur->pc = check_modrm (cur->pc); + break; + case 0xaf: /* imul Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0xb1: /* cmpxchg Ev,Gv */ + cur->pc = check_modrm (cur->pc); + break; + case 0xb3: + case 0xb6: /* movzx Gv,Eb */ + case 0xb7: /* movzx Gv,Ew */ + cur->pc = check_modrm (cur->pc); + break; + case 0xba: /* group8 Ev,Ib */ + cur->pc = check_modrm (cur->pc); + cur->pc += 1; + break; + case 0xbb: /* btc Ev,Gv */ + case 0xbc: /* bsf Gv,Ev */ + case 0xbd: /* bsr Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0xbe: /* movsx Gv,Eb */ + case 0xbf: /* movsx Gv,Ew */ + cur->pc = check_modrm (cur->pc); + break; + case 0xc0: /* xadd Eb,Gb */ + case 0xc1: /* xadd Ev,Gv */ + cur->pc = check_modrm (cur->pc); + break; + case 0xc2: /* cmpps V,W,Ib */ + cur->pc = check_modrm (cur->pc); + cur->pc += 1; + break; + case 0xc3: /* movnti M,G */ + cur->pc = check_modrm (cur->pc); + break; + case 0xc6: /* shufps V,W,Ib */ + cur->pc = check_modrm (cur->pc); + cur->pc += 1; + break; + case 0xc7: /* RDRAND */ + cur->pc = check_modrm (cur->pc); + break; + case 0xc8: case 0xc9: case 0xca: case 0xcb: /* bswap */ + case 0xcc: case 0xcd: case 0xce: case 0xcf: + break; + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + cur->pc = check_modrm (cur->pc); + break; + default: + if (jmp_reg_switch_mode == 1 && extop == 0x0b) + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d invalid opcode ub2: 0x0f %x jmp_reg_switch_mode=%d\n", + __LINE__, (int) extop, jmp_reg_switch_mode); + else + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d unknown opcode: 0x0f %x jmp_reg_switch_mode=%d\n", + __LINE__, (int) extop, jmp_reg_switch_mode); + DELETE_CURCTX (); + } + break; + } + break; + case 0x10: /* adc Eb,Gb */ + case 0x11: /* adc Ev,Gv */ + case 0x12: /* adc Gb,Eb */ + case 0x13: /* adc Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x14: /* adc %al,Ib */ + cur->pc += 1; + break; + case 0x15: /* adc %eax,Iz */ + cur->pc += z; + break; + case 0x16: /* push ss */ + cur->sp -= 1; + break; + case 0x17: /* pop ss */ + cur->sp += 1; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + break; + case 0x18: /* sbb Eb,Gb */ + case 0x19: /* sbb Ev,Gv */ + case 0x1a: /* sbb Gb,Eb */ + case 0x1b: /* sbb Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x1c: /* sbb %al,Ib */ + cur->pc += 1; + break; + case 0x1d: /* sbb %eax,Iz */ + cur->pc += z; + break; + case 0x1e: /* push ds */ + cur->sp -= 1; + break; + case 0x1f: /* pop ds */ + cur->sp += 1; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + break; + case 0x20: /* and Eb,Gb */ + case 0x21: /* and Ev,Gv */ + case 0x22: /* and Gb,Eb */ + case 0x23: /* and Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x24: /* and %al,Ib */ + cur->pc += 1; + break; + case 0x25: /* and %eax,Iz */ + cur->pc += z; + break; + case 0x26: /* seg=es prefix */ + break; + case 0x27: /* daa */ + break; + case 0x28: /* sub Eb,Gb */ + case 0x29: /* sub Ev,Gv */ + case 0x2a: /* sub Gb,Eb */ + case 0x2b: /* sub Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x2c: /* sub %al,Ib */ + cur->pc += 1; + break; + case 0x2d: /* sub %eax,Iz */ + cur->pc += z; + break; + case 0x2e: /* seg=cs prefix */ + break; + case 0x2f: /* das */ + break; + case 0x30: /* xor Eb,Gb */ + case 0x31: /* xor Ev,Gv */ + case 0x32: /* xor Gb,Eb */ + case 0x33: /* xor Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x34: /* xor %al,Ib */ + cur->pc += 1; + break; + case 0x35: /* xor %eax,Iz */ + cur->pc += z; + break; + case 0x36: /* seg=ss prefix */ + break; + case 0x37: /* aaa */ + break; + case 0x38: /* cmp Eb,Gb */ + case 0x39: /* cmp Ev,Gv */ + case 0x3a: /* cmp Gb,Eb */ + case 0x3b: /* cmp Gv,Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x3c: /* cmp %al,Ib */ + cur->pc += 1; + break; + case 0x3d: /* cmp %eax,Iz */ + cur->pc += z; + break; + case 0x3e: /* seg=ds prefix */ + break; + case 0x3f: /* aas */ + break; +#if WSIZE(32) + case 0x40: /* inc %eax */ + case 0x41: /* inc %ecx */ + case 0x42: /* inc %edx */ + case 0x43: /* inc %ebx */ + break; + case 0x44: /* inc %esp */ + /* Can't be a valid stack pointer - delete context */ + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode 0x44.\n", __LINE__); + DELETE_CURCTX (); + break; + case 0x45: /* inc %ebp */ + case 0x46: /* inc %esi */ + case 0x47: /* inc %edi */ + case 0x48: /* dec %eax */ + case 0x49: /* dec %ecx */ + case 0x4a: /* dec %edx */ + case 0x4b: /* dec %ebx */ + break; + case 0x4c: /* dec %esp */ + /* Can't be a valid stack pointer - delete context */ + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode 0x4c.\n", __LINE__); + DELETE_CURCTX (); + break; + case 0x4d: /* dec %ebp */ + case 0x4e: /* dec %esi */ + case 0x4f: /* dec %edi */ + break; +#endif + case 0x50: /* push %eax */ + case 0x51: /* push %ecx */ + case 0x52: /* push %edx */ + case 0x53: /* push %ebx */ + case 0x54: /* push %esp */ + case 0x55: /* push %ebp */ + case 0x56: /* push %esi */ + case 0x57: /* push %edi */ + cur->sp -= 1; + reg = OPC_REG (opcode); + if (reg == RBP) + { +#if 0 + /* Don't do this check yet. Affects tail calls. */ + /* avoid other function's prologue */ + if ((cur->pc[0] == 0x89 && cur->pc[1] == 0xe5) || + (cur->pc[0] == 0x8b && cur->pc[1] == 0xec)) + { + /* mov %esp,%ebp */ + DELETE_CURCTX (); + break; + } +#endif + if (cur->fp_loc == NULL) + { + cur->fp_loc = cur->sp; + cur->fp_sav = cur->fp; + } + } + break; + case 0x58: /* pop %eax */ + case 0x59: /* pop %ecx */ + case 0x5a: /* pop %edx */ + case 0x5b: /* pop %ebx */ + case 0x5c: /* pop %esp */ + case 0x5d: /* pop %ebp */ + case 0x5e: /* pop %esi */ + case 0x5f: /* pop %edi */ + reg = OPC_REG (opcode); + cur->regs[reg] = 0; + if (isInside ((unsigned long) cur->sp, (unsigned long) cur->sp_safe, wctx->sbase)) + cur->regs[reg] = *cur->sp; + DprintfT (SP_DUMP_UNWIND, "stack_unwind:%d cur->regs[%d]=0x%lx\n", + __LINE__, reg, (unsigned long) cur->regs[reg]); + if (reg == RDX) + { + if (cur->sp >= cur->sp_safe && + (unsigned long) cur->sp < wctx->sbase) + cur->rdx = *cur->sp; + } + else if (reg == RBP) + { + if (cur->fp_loc == cur->sp) + { + cur->fp = cur->fp_sav; + cur->fp_loc = NULL; + } + else if (cur->sp >= cur->sp_safe && + (unsigned long) cur->sp < wctx->sbase) + cur->fp = (unsigned long*) (*cur->sp); + } + else if (reg == RSP) + { + /* f.e. JVM I2CAdapter */ + if (cur->sp >= cur->sp_safe && (unsigned long) cur->sp < wctx->sbase) + { + unsigned long *nsp = (unsigned long*) (*cur->sp); + if (nsp >= cur->sp && nsp <= cur->fp) + { + cur->sp = nsp; + } + else + { + DprintfT (SP_DUMP_UNWIND, "stack_unwind%d give up return address, opcode=0x%02x\n", + __LINE__, opcode); + goto checkFP; + } + } + else + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d give up return address, opcode=0x%02x\n", + __LINE__, opcode); + goto checkFP; + } + break; + } + cur->sp += 1; + if (cur->sp - RED_ZONE > cur->sp_safe) + { + cur->sp_safe = cur->sp - RED_ZONE; + } + break; + case 0x60: /* pusha(d) */ + cur->sp -= 8; + break; + case 0x61: /* popa(d) */ + cur->sp += 8; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + break; + case 0x62: /* group AVX, 4-bytes EVEX prefix */ + { + unsigned char *pc = cur->pc - 1; // points to the beginning of the instruction + int len = parse_x86_AVX_instruction (pc); + if (len < 4) + { + DELETE_CURCTX (); + } + else + { + pc += len; + cur->pc = pc; + } + } + break; + case 0x63: /* arpl Ew,Gw (32) movsxd Gv,Ev (64)*/ + cur->pc = check_modrm (cur->pc); + break; + case 0x64: /* seg=fs prefix */ + case 0x65: /* seg=gs prefix */ + break; + case 0x66: /* opd size override */ + case 0x67: /* addr size override */ + break; + case 0x68: /* push Iz */ + cur->sp = (unsigned long*) ((long) cur->sp - z); + cur->pc += z; + break; + case 0x69: /* imul Gv,Ev,Iz */ + cur->pc = check_modrm (cur->pc); + cur->pc += z; + break; + case 0x6a: /* push Ib */ + cur->sp = (unsigned long*) ((long) cur->sp - v); + cur->pc += 1; + break; + case 0x6b: /* imul Gv,Ev,Ib */ + cur->pc = check_modrm (cur->pc); + cur->pc += 1; + break; + case 0x6c: case 0x6d: case 0x6e: case 0x6f: + cur->pc = check_modrm (cur->pc); + break; + case 0x70: /* jo Jb */ + case 0x71: /* jno Jb */ + case 0x72: /* jb Jb */ + case 0x73: /* jnb Jb */ + case 0x74: /* jz Jb */ + case 0x75: /* jnz Jb */ + case 0x76: /* jna Jb */ + case 0x77: /* ja Jb */ + case 0x78: /* js Jb */ + case 0x79: /* jns Jb */ + case 0x7a: /* jp Jb */ + case 0x7b: /* jnp Jb */ + case 0x7c: /* jl Jb */ + case 0x7d: /* jge Jb */ + case 0x7e: /* jle Jb */ + case 0x7f: /* jg Jb */ + imm8 = *(char*) cur->pc++; + if (nctx < (jmp_reg_switch_mode ? MAXJMPREGCTX : MAXCTX)) + { + int tidx = 0; + unsigned char *npc = cur->pc + imm8; + if (is_after_ret (npc)) + break; + while (npc > targets[tidx]) + tidx += 1; + if (npc != targets[tidx]) + { + if (ntrg < MAXTRGTS) + { + for (int i = 0; i < nctx; i++) + if (buf[i].tidx >= tidx) + buf[i].tidx++; + + /* insert a new target */ + for (int i = ntrg; i > tidx; i--) + targets[i] = targets[i - 1]; + ntrg += 1; + targets[tidx++] = npc; + } + else + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d ntrg(%d)=max\n", __LINE__, ntrg); + struct AdvWalkContext *new = buf + nctx; + nctx += 1; + __collector_memcpy (new, cur, sizeof (*new)); + new->pc = npc; + new->tidx = tidx; + cur = new; /* advance the new context first */ + continue; + } + } + else + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d nctx(%d)=max\n", __LINE__, nctx); + break; + case 0x80: /* group1 Eb,Ib */ + cur->pc = check_modrm (cur->pc); + cur->pc += 1; + break; + case 0x81: /* group1 Ev,Iz */ + modrm = *cur->pc; + if (MRM_MOD (modrm) == 0xc0 && MRM_REGS (modrm) == RSP) + { + int immz = read_int (cur->pc + 1, z); + extop = MRM_EXT (modrm); + if (extop == 0) /* add imm32,%esp */ + cur->sp = (unsigned long*) ((long) cur->sp + immz); + else if (extop == 4) /* and imm32,%esp */ + cur->sp = (unsigned long*) ((long) cur->sp & immz); + else if (extop == 5) /* sub imm32,%esp */ + cur->sp = (unsigned long*) ((long) cur->sp - immz); + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + } + cur->pc = check_modrm (cur->pc); + cur->pc += z; + break; + case 0x82: /* group1 Eb,Ib */ + cur->pc = check_modrm (cur->pc); + cur->pc += 1; + break; + case 0x83: /* group1 Ev,Ib */ + modrm = *cur->pc; + if (MRM_MOD (modrm) == 0xc0 && MRM_REGS (modrm) == RSP) + { + imm8 = (char) cur->pc[1]; /* sign extension */ + extop = MRM_EXT (modrm); + if (extop == 0) /* add imm8,%esp */ + cur->sp = (unsigned long*) ((long) cur->sp + imm8); + else if (extop == 4) /* and imm8,%esp */ + cur->sp = (unsigned long*) ((long) cur->sp & imm8); + else if (extop == 5) /* sub imm8,%esp */ + cur->sp = (unsigned long*) ((long) cur->sp - imm8); + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + } + cur->pc = check_modrm (cur->pc); + cur->pc += 1; + break; + case 0x84: /* test Eb,Gb */ + case 0x85: /* test Ev,Gv */ + case 0x86: /* xchg Eb,Gb */ + case 0x87: /* xchg Ev,Gv */ + cur->pc = check_modrm (cur->pc); + break; + case 0x88: /* mov Eb,Gb */ + cur->pc = check_modrm (cur->pc); + break; + case 0x89: /* mov Ev,Gv */ + modrm = *cur->pc; + if (MRM_MOD (modrm) == 0xc0) + { + if (MRM_REGS (modrm) == RBP && MRM_REGD (modrm) == RSP) + /* movl %esp,%ebp */ + cur->fp = cur->sp; + else if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RBP) + { /* mov %ebp,%esp */ + cur->sp = cur->fp; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + if (wctx->fp == (unsigned long) cur->sp) + cur->cval = RA_FROMFP; + } + } + else if (MRM_MOD (modrm) == 0x80) + { + if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RBP) + { + if (cur->pc[1] == 0x24) + { /* mov %ebp,disp32(%esp) - JVM */ + immv = read_int (cur->pc + 2, 4); + cur->fp_loc = (unsigned long*) ((char*) cur->sp + immv); + cur->fp_sav = cur->fp; + } + } + } + else if (MRM_MOD (modrm) == 0x40) + { + if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RDX) + { + if (cur->pc[1] == 0x24 && cur->pc[2] == 0x0) + { /* movl %edx,0(%esp) */ + cur->ra_loc = cur->sp; + cur->ra_sav = cur->rdx; + } + } + else if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RBP) + { + if (cur->pc[1] == 0x24) + { /* mov %ebp,disp8(%esp) - JVM */ + imm8 = ((char*) (cur->pc))[2]; + cur->fp_loc = (unsigned long*) ((char*) cur->sp + imm8); + cur->fp_sav = cur->fp; + } + } + } + else if (MRM_MOD (modrm) == 0x0) + { + if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RBP) + { + if (cur->pc[1] == 0x24) + { /* mov %ebp,(%esp) */ + cur->fp_loc = cur->sp; + cur->fp_sav = cur->fp; + } + } + else if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RDX) + { + if (cur->pc[1] == 0x24) + { /* movl %edx,(%esp) */ + cur->ra_loc = cur->sp; + cur->ra_sav = cur->rdx; + } + } + } + cur->pc = check_modrm (cur->pc); + break; + case 0x8a: /* mov Gb,Eb */ + cur->pc = check_modrm (cur->pc); + break; + case 0x8b: /* mov Gv,Ev */ + modrm = *cur->pc; + if (MRM_MOD (modrm) == 0xc0) + { + if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RBP) + /* mov %esp,%ebp */ + cur->fp = cur->sp; + else if (MRM_REGS (modrm) == RBP && MRM_REGD (modrm) == RSP) + { /* mov %ebp,%esp */ + cur->sp = cur->fp; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + if (wctx->fp == (unsigned long) cur->sp) + cur->cval = RA_FROMFP; + } + } + else if (MRM_MOD (modrm) == 0x80) + { + if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RBP) + { + if (cur->pc[1] == 0x24) + { /* mov disp32(%esp),%ebp */ + immv = read_int (cur->pc + 2, 4); + unsigned long *ptr = (unsigned long*) ((char*) cur->sp + immv); + if (cur->fp_loc == ptr) + { + cur->fp = cur->fp_sav; + cur->fp_loc = NULL; + } + else if (ptr >= cur->sp_safe && (unsigned long) ptr < wctx->sbase) + cur->fp = (unsigned long*) (*ptr); + } + } + } + else if (MRM_MOD (modrm) == 0x40) + { + if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RBP) + { + if (cur->pc[1] == 0x24) + { /* mov disp8(%esp),%ebp - JVM */ + imm8 = ((char*) (cur->pc))[2]; + unsigned long *ptr = (unsigned long*) ((char*) cur->sp + imm8); + if (cur->fp_loc == ptr) + { + cur->fp = cur->fp_sav; + cur->fp_loc = NULL; + } + else if (ptr >= cur->sp_safe && (unsigned long) ptr < wctx->sbase) + cur->fp = (unsigned long*) (*ptr); + } + } + } + else if (MRM_MOD (modrm) == 0x0) + { + if (MRM_REGS (modrm) == RSP && MRM_REGD (modrm) == RBP) + { + if (cur->pc[1] == 0x24) + { /* mov (%esp),%ebp */ + if (cur->fp_loc == cur->sp) + { + cur->fp = cur->fp_sav; + cur->fp_loc = NULL; + } + else if (cur->sp >= cur->sp_safe && + (unsigned long) cur->sp < wctx->sbase) + cur->fp = (unsigned long*) *cur->sp; + } + } + } + cur->pc = check_modrm (cur->pc); + break; + case 0x8c: /* mov Mw,Sw */ + cur->pc = check_modrm (cur->pc); + break; + case 0x8d: /* lea Gv,M */ + modrm = *cur->pc; + if (MRM_REGD (modrm) == RSP) + { + unsigned char *pc = cur->pc; + // Mez: need to use always regs[RSP/RBP] instead cur->sp(or fp): + cur->regs[RSP] = (unsigned long) cur->sp; + cur->regs[RBP] = (unsigned long) cur->fp; + cur->pc++; + int mod = (modrm >> 6) & 3; + int r_m = modrm & 7; + long val = 0; + int undefRez = 0; + if (mod == 0x3) + val = getRegVal (cur, MRM_REGS (modrm), &undefRez); + else if (r_m == 4) + { // SP or R12. Decode SIB-byte. + int sib = *cur->pc++; + int scale = 1 << (sib >> 6); + int index = X | ((sib >> 3) & 7); + int base = B | (sib & 7); + if (mod == 0) + { + if ((base & 7) == 5) + { // BP or R13 + if (index != 4) // SP + val += getRegVal (cur, index, &undefRez) * scale; + val += read_int (cur->pc, 4); + cur->pc += 4; + } + else + { + val += getRegVal (cur, base, &undefRez); + if (index != 4) // SP + val += getRegVal (cur, index, &undefRez) * scale; + } + } + else + { + val += getRegVal (cur, base, &undefRez); + if (index != 4) // SP + val += getRegVal (cur, index, &undefRez) * scale; + if (mod == 1) + { + val += read_int (cur->pc, 1); + cur->pc++; + } + else + { // mod == 2 + val += read_int (cur->pc, 4); + cur->pc += 4; + } + } + } + else if (mod == 0) + { + if (r_m == 5) + { // BP or R13 + val += read_int (cur->pc, 4); + cur->pc += 4; + } + else + val += getRegVal (cur, MRM_REGS (modrm), &undefRez); + } + else + { // mod == 1 || mod == 2 + val += getRegVal (cur, MRM_REGS (modrm), &undefRez); + if (mod == 1) + { + val += read_int (cur->pc, 1); + cur->pc++; + } + else + { // mod == 2 + val += read_int (cur->pc, 4); + cur->pc += 4; + } + } + if (undefRez) + { + DprintfT (SP_DUMP_UNWIND, "stack_unwind%d cannot calculate RSP. cur->pc=0x%lx val=0x%lx\n", + __LINE__, (unsigned long) cur->pc, (unsigned long) val); + goto checkFP; + } + cur->regs[MRM_REGD (modrm)] = val; + DprintfT (SP_DUMP_UNWIND, "stack_unwind%d cur->pc=0x%lx val=0x%lx wctx->sp=0x%lx wctx->sbase=0x%lx\n", + __LINE__, (unsigned long) cur->pc, (unsigned long) val, + (unsigned long) wctx->sp, (unsigned long) wctx->sbase); + if (cur->pc != check_modrm (pc)) + DprintfT (SP_DUMP_UNWIND, "stack_unwind%d ERROR: cur->pc=0x%lx != check_modrm(0x%lx)=0x%lx\n", + __LINE__, (unsigned long) cur->pc, (unsigned long) pc, + (unsigned long) check_modrm (pc)); + if (MRM_REGD (modrm) == RSP) + { + if (!isInside ((unsigned long) val, wctx->sp, wctx->sbase)) + { + DprintfT (SP_DUMP_UNWIND, "stack_unwind%d cannot calculate RSP. cur->pc=0x%lx opcode=0x%02x val=0x%lx wctx->sp=0x%lx wctx->sbase=0x%lx\n", + __LINE__, (unsigned long) cur->pc, opcode, (unsigned long) val, + (unsigned long) wctx->sp, (unsigned long) wctx->sbase); + goto checkFP; + } + cur->sp = (unsigned long *) val; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + } + } + else + cur->pc = check_modrm (cur->pc); + break; + case 0x8e: /* mov Sw,Ew */ + cur->pc = check_modrm (cur->pc); + break; + case 0x8f: /* pop Ev */ + cur->pc = check_modrm (cur->pc); + cur->sp += 1; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + break; + case 0x90: /* nop */ + break; + case 0x91: /* xchg %eax,%ecx */ + case 0x92: /* xchg %eax,%edx */ + case 0x93: /* xchg %eax,%ebx */ + case 0x94: /* xchg %eax,%esp XXXX */ + case 0x95: /* xchg %eax,%ebp XXXX */ + case 0x96: /* xchg %eax,%esi */ + case 0x97: /* xchg %eax,%edi */ + break; + case 0x98: /* cbw/cwde */ + case 0x99: /* cwd/cwq */ + break; + case 0x9a: /* callf Ap */ + if (jmp_reg_switch_mode == 1) + { + struct AdvWalkContext* tmpctx = (struct AdvWalkContext *) alloca (sizeof (*cur)); + __collector_memcpy (tmpctx, cur, sizeof (*cur)); + int rc = process_return (wctx, tmpctx); + if (rc != RA_FAILURE) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + } + cur->pc += 2 + a; + break; + case 0x9b: /* fwait */ + case 0x9c: /* pushf Fv */ + case 0x9d: /* popf Fv */ + case 0x9e: /* sahf */ + case 0x9f: /* lahf */ + break; + case 0xa0: /* mov al,Ob */ + case 0xa1: /* mov eax,Ov */ + case 0xa2: /* mov Ob,al */ + case 0xa3: /* mov Ov,eax */ + cur->pc += a; + break; + case 0xa4: /* movsb Yb,Xb */ + case 0xa5: /* movsd Yv,Xv */ + case 0xa6: /* cmpsb Yb,Xb */ + case 0xa7: /* cmpsd Xv,Yv */ + break; + case 0xa8: /* test al,Ib */ + cur->pc += 1; + break; + case 0xa9: /* test eax,Iz */ + cur->pc += z; + break; + case 0xaa: /* stosb Yb,%al */ + case 0xab: /* stosd Yv,%eax */ + case 0xac: /* lodsb %al,Xb */ + case 0xad: /* lodsd %eax,Xv */ + case 0xae: /* scasb %al,Yb */ + case 0xaf: /* scasd %eax,Yv */ + break; + case 0xb0: /* mov %al,Ib */ + case 0xb1: /* mov %cl,Ib */ + case 0xb2: /* mov %dl,Ib */ + case 0xb3: /* mov %bl,Ib */ + case 0xb4: /* mov %ah,Ib */ + case 0xb5: /* mov %ch,Ib */ + case 0xb6: /* mov %dh,Ib */ + case 0xb7: /* mov %bh,Ib */ + cur->pc += 1; + break; + case 0xb8: /* mov Iv,%eax */ + case 0xb9: /* mov Iv,%ecx */ + case 0xba: /* mov Iv,%edx */ + case 0xbb: /* mov Iv,%ebx */ + case 0xbc: /* mov Iv,%esp */ + case 0xbd: /* mov Iv,%rbp */ + case 0xbe: /* mov Iv,%esi */ + case 0xbf: /* mov Iv,%edi */ + reg = OPC_REG (opcode); + if (reg == RAX) + cur->rax = read_int (cur->pc, v); + cur->pc += v; + break; + case 0xc0: /* group2 Eb,Ib */ + case 0xc1: /* group2 Ev,Ib */ + cur->pc = check_modrm (cur->pc) + 1; + break; + case 0xc2: /* ret Iw */ + /* In the dynamic linker we may see that + * the actual return address is at sp+immv, + * while sp points to the resolved address. + */ + { + immv = read_int (cur->pc, 2); + int rc = process_return (wctx, cur); + if (rc != RA_FAILURE) + { + if (jmp_reg_switch_mode == 1) + { + DprintfT (SP_DUMP_UNWIND, "stack_unwind%d give up return address under jmp switch mode, opcode = 0xc2\n", __LINE__); + goto checkFP; + } + wctx->sp += immv; + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode 0xc2.\n", __LINE__); + DELETE_CURCTX (); + } + break; + case 0xc3: /* ret */ + { + int rc = process_return (wctx, cur); + if (rc != RA_FAILURE) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + if (jmp_reg_switch_mode == 1) + jmp_reg_switch_pc = cur->pc; + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode 0xc3.\n", __LINE__); + DELETE_CURCTX (); + } + break; + case 0xc4: /* group AVX, 3-bytes VEX prefix */ + { + unsigned char *pc = cur->pc - 1; // points to the beginning of the instruction + int len = parse_x86_AVX_instruction (pc); + if (len < 3) + DELETE_CURCTX (); + else + { + pc += len; + cur->pc = pc; + } + } + break; + case 0xc5: /* group AVX, 2-bytes VEX prefix */ + { + unsigned char *pc = cur->pc - 1; // points to the beginning of the instruction + int len = parse_x86_AVX_instruction (pc); + if (len < 2) + DELETE_CURCTX (); + else + { + pc += len; + cur->pc = pc; + } + } + break; + case 0xc6: + modrm = *cur->pc; + if (modrm == 0xf8) /* xabort */ + cur->pc += 2; + else /* mov Eb,Ib */ + cur->pc = check_modrm (cur->pc) + 1; + break; + case 0xc7: + modrm = *cur->pc; + if (modrm == 0xf8) /* xbegin */ + cur->pc += v + 1; + else + { /* mov Ev,Iz */ + extop = MRM_EXT (modrm); + if (extop != 0) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d give up return address, opcode = 0xc7\n", __LINE__); + goto checkFP; + } + if (MRM_MOD (modrm) == 0xc0 && MRM_REGS (modrm) == RAX) + cur->rax = read_int (cur->pc + 1, z); + cur->pc = check_modrm (cur->pc) + z; + } + break; + case 0xc8: /* enter Iw,Ib */ + cur->pc += 3; + break; + case 0xc9: /* leave */ + /* mov %ebp,%esp */ + cur->sp = cur->fp; + /* pop %ebp */ + if (cur->fp_loc == cur->sp) + { + cur->fp = cur->fp_sav; + cur->fp_loc = NULL; + } + else if (cur->sp >= cur->sp_safe && + (unsigned long) cur->sp < wctx->sbase) + { + cur->fp = (unsigned long*) (*cur->sp); + if (wctx->fp == (unsigned long) cur->sp) + cur->cval = RA_FROMFP; + } + cur->sp += 1; + if (cur->sp - RED_ZONE > cur->sp_safe) + cur->sp_safe = cur->sp - RED_ZONE; + break; + case 0xca: /* retf Iw */ + cur->pc += 2; /* XXXX process return */ + break; + case 0xcb: /* retf */ + break; /* XXXX process return */ + case 0xcc: /* int 3 */ + break; + case 0xcd: /* int Ib */ + if (*cur->pc == 0x80) + { + if (cur->rax == __NR_exit) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode 0xcd.\n", __LINE__); + DELETE_CURCTX (); + break; + } + else if (cur->rax == __NR_rt_sigreturn) + { + if (jmp_reg_switch_mode == 1) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d give up return address under jmp switch mode, opcode=0xcd\n", + __LINE__); + goto checkFP; + } + wctx->sp = (unsigned long) cur->sp; + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_RT_SIGRETURN); + return RA_RT_SIGRETURN; + } +#if WSIZE(32) + else if (cur->rax == __NR_sigreturn) + { + if (jmp_reg_switch_mode == 1) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d give up return address under jmp switch mode, opcode = 0xc2\n", + __LINE__); + goto checkFP; + } + wctx->sp = (unsigned long) cur->sp; + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_SIGRETURN); + return RA_SIGRETURN; + } +#endif + } + cur->pc += 1; + break; + case 0xce: /* into */ + case 0xcf: /* iret */ + break; + case 0xd0: /* shift group2 Eb,1 */ + case 0xd1: /* shift group2 Ev,1 */ + case 0xd2: /* shift group2 Eb,%cl */ + case 0xd3: /* shift group2 Ev,%cl */ + cur->pc = check_modrm (cur->pc); + break; + case 0xd4: /* aam Ib */ + cur->pc += 1; + break; + case 0xd5: /* aad Ib */ + cur->pc += 1; + break; + case 0xd6: /* falc? */ + break; + case 0xd7: + cur->pc = check_modrm (cur->pc); + cur->pc++; + break; + case 0xd8: /* esc instructions */ + case 0xd9: + case 0xda: + case 0xdb: + case 0xdc: + case 0xdd: + case 0xde: + case 0xdf: + cur->pc = check_modrm (cur->pc); + break; + case 0xe0: /* loopne Jb */ + case 0xe1: /* loope Jb */ + case 0xe2: /* loop Jb */ + case 0xe3: /* jcxz Jb */ + imm8 = *(char*) cur->pc++; + if (nctx < (jmp_reg_switch_mode ? MAXJMPREGCTX : MAXCTX)) + { + int tidx = 0; + unsigned char *npc = cur->pc + imm8; + if (is_after_ret (npc)) + break; + while (npc > targets[tidx]) + tidx += 1; + if (npc != targets[tidx]) + { + if (ntrg < MAXTRGTS) + { + for (int i = 0; i < nctx; i++) + if (buf[i].tidx >= tidx) + buf[i].tidx++; + /* insert a new target */ + for (int i = ntrg; i > tidx; i--) + targets[i] = targets[i - 1]; + ntrg += 1; + targets[tidx++] = npc; + } + else + DprintfT (SP_DUMP_UNWIND, "unwind.c: ntrg = max\n"); + struct AdvWalkContext *new = buf + nctx; + nctx += 1; + __collector_memcpy (new, cur, sizeof (*new)); + new->pc = npc; + new->tidx = tidx; + cur = new; /* advance the new context first */ + continue; + } + } + else + DprintfT (SP_DUMP_UNWIND, "unwind.c: nctx = max\n"); + break; + case 0xe4: case 0xe5: + cur->pc = check_modrm (cur->pc); + cur->pc++; + break; + case 0xe6: case 0xe7: + cur->pc++; + cur->pc = check_modrm (cur->pc); + break; + case 0xec: case 0xed: case 0xee: case 0xef: + cur->pc = check_modrm (cur->pc); + break; + case 0xe8: /* call Jz (f64) */ + { + if (jmp_reg_switch_mode == 1) + { + struct AdvWalkContext* tmpctx = (struct AdvWalkContext *) alloca (sizeof (*cur)); + __collector_memcpy (tmpctx, cur, sizeof (*cur)); + int rc = process_return (wctx, tmpctx); + if (rc != RA_FAILURE) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + } + int immz = read_int (cur->pc, z); + if (immz == 0) + /* special case in PIC code */ + cur->sp -= 1; + cur->pc += z; + } + break; + case 0xe9: /* jump Jz */ + { + int immz = read_int (cur->pc, z); + unsigned char *npc = cur->pc + z + immz; + if ((unsigned long) npc < wctx->tbgn || (unsigned long) npc >= wctx->tend) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode 0xe9.\n", __LINE__); + DELETE_CURCTX (); + break; + } + int tidx = 0; + while (npc > targets[tidx]) + tidx += 1; + if (npc != targets[tidx]) + { + if (ntrg < MAXTRGTS) + { + for (int i = 0; i < nctx; i++) + if (buf[i].tidx >= tidx) + buf[i].tidx++; + /* insert a new target */ + for (int i = ntrg; i > tidx; i--) + targets[i] = targets[i - 1]; + ntrg += 1; + targets[tidx++] = npc; + } + else + DprintfT (SP_DUMP_UNWIND, "unwind.c: ntrg = max\n"); + cur->pc = npc; + cur->tidx = tidx; + continue; /* advance this context first */ + } + else + { + /* Delete context */ + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode 0xe9.\n", __LINE__); + DELETE_CURCTX (); + } + } + break; + case 0xeb: /* jump imm8 */ + { + imm8 = *(char*) cur->pc++; + int tidx = 0; + unsigned char *npc = cur->pc + imm8; + while (npc > targets[tidx]) + tidx += 1; + if (npc != targets[tidx]) + { + if (ntrg < MAXTRGTS) + { + for (int i = 0; i < nctx; i++) + if (buf[i].tidx >= tidx) + buf[i].tidx++; + /* insert a new target */ + for (int i = ntrg; i > tidx; i--) + targets[i] = targets[i - 1]; + ntrg += 1; + targets[tidx++] = npc; + } + else + DprintfT (SP_DUMP_UNWIND, "unwind.c: ntrg = max\n"); + cur->pc = npc; + cur->tidx = tidx; + continue; /* advance this context first */ + } + else + { + /* Delete context */ + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode 0xeb.\n", __LINE__); + DELETE_CURCTX (); + } + } + break; + case 0xf0: /* lock prefix */ + case 0xf2: /* repne prefix */ + case 0xf3: /* repz prefix */ + break; + case 0xf4: /* hlt */ + extop2 = *(cur->pc - 3); + if (extop2 == 0x90) + { + // 17851712 occasional SEGV in find_i386_ret_addr in unwind.c during attach + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_END_OF_STACK); + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d returns RA_END_OF_STACK\n", __LINE__); + return RA_END_OF_STACK; + } + /* We see 'hlt' in _start. Stop analysis, revert to FP */ + /* A workaround for the Linux main stack */ + if (nctx > 1) + { + DELETE_CURCTX (); + break; + } + if (cur->fp == 0) + { + if (jmp_reg_switch_mode == 1) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: give up return address under jmp switch mode, opcode = 0xf4\n"); + goto checkFP; + } + cache_put (wctx, RA_EOSTCK); + wctx->pc = 0; + wctx->sp = 0; + wctx->fp = 0; + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_END_OF_STACK); + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d returns RA_END_OF_STACK\n", __LINE__); + return RA_END_OF_STACK; + } + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d give up return address, opcode = 0xf4\n", __LINE__); + goto checkFP; + case 0xf5: /* cmc */ + break; + case 0xf6: /* group3 Eb */ + modrm = *cur->pc; + extop = MRM_EXT (modrm); + cur->pc = check_modrm (cur->pc); + if (extop == 0x0) /* test Ib */ + cur->pc += 1; + break; + case 0xf7: /* group3 Ev */ + modrm = *cur->pc; + extop = MRM_EXT (modrm); + cur->pc = check_modrm (cur->pc); + if (extop == 0x0) /* test Iz */ + cur->pc += z; + break; + case 0xf8: /* clc */ + case 0xf9: /* stc */ + case 0xfa: /* cli */ + case 0xfb: /* sti */ + case 0xfc: /* cld */ + case 0xfd: /* std */ + break; + case 0xfe: /* group4 */ + modrm = *cur->pc; + extop = MRM_EXT (modrm); + switch (extop) + { + case 0x0: /* inc Eb */ + case 0x1: /* dec Eb */ + cur->pc = check_modrm (cur->pc); + break; + case 0x7: + cur->pc = check_modrm (cur->pc); + break; + default: + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d unknown opcode: 0xfe %x\n", + __LINE__, extop); + DELETE_CURCTX (); + break; + } + break; + case 0xff: /* group5 */ + modrm = *cur->pc; + extop = MRM_EXT (modrm); + switch (extop) + { + case 0x0: /* inc Ev */ + case 0x1: /* dec Ev */ + cur->pc = check_modrm (cur->pc); + break; + case 0x2: /* calln Ev */ + if (jmp_reg_switch_mode == 1) + { + struct AdvWalkContext* tmpctx = (struct AdvWalkContext *) alloca (sizeof (*cur)); + __collector_memcpy (tmpctx, cur, sizeof (*cur)); + int rc = process_return (wctx, tmpctx); + if (rc != RA_FAILURE) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + } + cur->pc = check_modrm (cur->pc); + break; + case 0x3: /* callf Ep */ + if (jmp_reg_switch_mode == 1) + { + struct AdvWalkContext* tmpctx = (struct AdvWalkContext *) alloca (sizeof (*cur)); + __collector_memcpy (tmpctx, cur, sizeof (*cur)); + int rc = process_return (wctx, tmpctx); + if (rc != RA_FAILURE) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + } + cur->pc = check_modrm (cur->pc); /* XXXX */ + break; + case 0x4: /* jumpn Ev */ + /* This instruction appears in PLT or + * in tail call optimization. + * In both cases treat it as return. + * Save jump *(reg) - switch, etc, for later use when no ctx left + */ + if (modrm == 0x25 || /* jumpn *disp32 */ + MRM_MOD (modrm) == 0x40 || /* jumpn byte(reg) */ + MRM_MOD (modrm) == 0x80) /* jumpn word(reg) */ + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: PLT or tail call: %p\n", cur->pc - 1); + int rc = process_return (wctx, cur); + if (rc != RA_FAILURE) + { + if (jmp_reg_switch_mode == 1 && total_num_jmp_reg < max_num_jmp_reg_seen) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: give up return address under jmp switch mode, opcode = 0xff\n"); + goto checkFP; + } + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + } + else if (modrm != 0x24 /*ignore SIB*/) /* jumpn *(reg) or jumpn reg */ + { + // 22846120 stack unwind does not find caller of __memcpy_ssse3_back with B64 intel-Linux + /* + * For now, let's deal rather narrowly with this scenario. If: + * - we are in the middle of an "ff e2" instruction, and + * - the next instruction is undefined ( 0f 0b == ud2 ) + * then test return. (Might eventually have to broaden the scope + * of this fix to other registers/etc.) + */ + if (cur->pc[0] == 0xe2 && cur->pc[1] == 0x0f && cur->pc[2] == 0x0b) + { + int rc = process_return_real (wctx, cur, 0); + if (rc == RA_SUCCESS) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + } + + // 22691241 shjsynprog, jsynprog core dump from find_i386_ret_addr + /* + * Here is another oddity. Java 9 seems to emit dynamically generated + * code where a code block ends with a "jmp *reg" and then padding to a + * multiple-of-16 boundary and then a bunch of 0s. In this case, let's + * not continue to walk bytes since we would be walking off the end of + * the instructions into ... something. Treating them as instructions + * can lead to unexpected results, including SEGV. + */ + /* + * While the general problem deserves a better solution, let's look + * here only for one particular case: + * 0xff 0xe7 jmp *reg + * nop to bring us to a multiple-of-16 boundary + * 0x0000000000000a00 something that does not look like an instruction + * + * A different nop might be used depending on how much padding is needed + * to reach that multiple-of-16 boundary. We've seen two: + * 0x90 one byte + * 0x0f 0x1f 0x40 0x00 four bytes + */ + // confirm the instruction is 0xff 0xe7 + if (cur->pc[0] == 0xe7) + { + // check for correct-length nop and find next 16-byte boundary + int found_nop = 0; + unsigned long long *boundary = 0; + switch ((((unsigned long) (cur->pc)) & 0xf)) + { + case 0xb: // look for 4-byte nop + if (*((unsigned *) (cur->pc + 1)) == 0x00401f0f) + found_nop = 1; + boundary = (unsigned long long *) (cur->pc + 5); + break; + case 0xe: // look for 1-byte nop + if (cur->pc[1] == 0x90) + found_nop = 1; + boundary = (unsigned long long *) (cur->pc + 2); + break; + default: + break; + } + + // if nop is found, check what's at the boundary + if (found_nop && *boundary == 0x000000000a00) + { + DELETE_CURCTX (); + break; + } + } + + DprintfT (SP_DUMP_UNWIND, "unwind.c: probably PLT or tail call or switch table: %p\n", + cur->pc - 1); + if (num_jmp_reg < expected_num_jmp_reg) + { + if (jmp_reg_ctx[num_jmp_reg] == NULL) + jmp_reg_ctx[num_jmp_reg] = (struct AdvWalkContext *) alloca (sizeof (*cur)); + if (jmp_reg_ctx[num_jmp_reg] != NULL) + __collector_memcpy (jmp_reg_ctx[num_jmp_reg], cur, sizeof (*cur)); + } + if (num_jmp_reg < expected_num_jmp_reg || + (num_jmp_reg >= expected_num_jmp_reg && + jmp_reg_ctx[expected_num_jmp_reg - 1] != NULL && + cur->pc != jmp_reg_ctx[expected_num_jmp_reg - 1]->pc)) + { + num_jmp_reg++; + total_num_jmp_reg++; + } + if (jmp_reg_switch_mode == 1 && total_num_jmp_reg >= max_num_jmp_reg_seen) + { + int rc = process_return_real (wctx, cur, 0); + if (rc == RA_SUCCESS) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + } + } + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d delete context, opcode 0xff.\n", __LINE__); + DELETE_CURCTX (); + break; + case 0x5: /* jmpf Ep */ + cur->pc = check_modrm (cur->pc); /* XXXX */ + break; + case 0x6: /* push Ev */ + cur->pc = check_modrm (cur->pc); + cur->sp -= 1; + break; + case 0x7: + cur->pc = check_modrm (cur->pc); /* XXXX */ + if (jmp_reg_switch_mode == 1) + { + int rc = process_return_real (wctx, cur, 0); + if (rc == RA_SUCCESS) + { + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, rc); + return rc; + } + } + break; + default: + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d unknown opcode: 0xff %x\n", + __LINE__, (int) extop); + DELETE_CURCTX (); + break; + } + break; + default: + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d unknown opcode: 0x%x\n", + __LINE__, (int) opcode); + DELETE_CURCTX (); + break; + } + + /* switch to next context */ + if (++cur >= buf + nctx) + cur = buf; + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d switch context: cur=0x%lx(%ld) nctx=%d cnt=%d\n", + __LINE__, (unsigned long) cur, (long) (cur - buf), (int) nctx, (int) cnt); + } + +checkFP: + Tprintf (DBG_LT3, "find_i386_ret_addr:%d checkFP: wctx=0x%lx fp=0x%lx ln=0x%lx pc=0x%lx sbase=0x%lx sp=0x%lx tbgn=0x%lx tend=0x%lx\n", + __LINE__, (unsigned long) wctx, (unsigned long) wctx->fp, + (unsigned long) wctx->ln, (unsigned long) wctx->pc, (unsigned long) wctx->sbase, + (unsigned long) wctx->sp, (unsigned long) wctx->tbgn, (unsigned long) wctx->tend); + + if (jmp_reg_switch_mode == 1) + { // not deal with switch cases not ending with ret + if (jmp_reg_switch_backup_ctx != NULL) + __collector_memcpy (cur, jmp_reg_switch_backup_ctx, sizeof (*cur)); + DprintfT (SP_DUMP_UNWIND, "stack_unwind jmp reg mode on: pc = 0x%lx cnt = %d, nctx = %d\n", wctx->pc, cnt, nctx); + } + + unsigned long *cur_fp = cur->fp; + unsigned long *cur_sp = cur->sp; + if (do_walk == 0) + __collector_memcpy (&wctx_pc_save, wctx, sizeof (struct WalkContext)); + + /* Resort to the frame pointer */ + if (cur->fp_loc) + cur->fp = cur->fp_sav; + cur->sp = cur->fp; + if ((unsigned long) cur->sp >= wctx->sbase || + (unsigned long) cur->sp < wctx->sp) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d do_walk=%d cur->sp=0x%p out of range. wctx->sbase=0x%lx wctx->sp=0x%lx wctx->pc=0x%lx\n", + __LINE__, (int) do_walk, cur->sp, (unsigned long) wctx->sbase, + (unsigned long) wctx->sp, (unsigned long) wctx->pc); + if (do_walk == 0) + { + cur->sp = cur_sp; + cur->fp = cur_fp; + do_walk = 1; + save_ctx = 1; + goto startWalk; + } + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_FAILURE); + return RA_FAILURE; + } + + unsigned long fp = *cur->sp++; + if (fp <= (unsigned long) cur->sp || fp >= wctx->sbase) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d fp=0x%016llx out of range. cur->sp=%p wctx->sbase=0x%lx wctx->pc=0x%lx\n", + __LINE__, (unsigned long long) fp, cur->sp, + (unsigned long) wctx->sbase, (unsigned long) wctx->pc); + if (do_walk == 0) + { + cur->sp = cur_sp; + cur->fp = cur_fp; + do_walk = 1; + save_ctx = 1; + goto startWalk; + } + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_FAILURE); + return RA_FAILURE; + } + + unsigned long ra = *cur->sp++; + if (ra == 0) + { + cache_put (wctx, RA_EOSTCK); + DprintfT (SP_DUMP_UNWIND, "unwind.c:%d returns RA_END_OF_STACK wctx->pc = 0x%lx\n", __LINE__, wctx->pc); + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_END_OF_STACK); + return RA_END_OF_STACK; + } + + unsigned long tbgn = wctx->tbgn; + unsigned long tend = wctx->tend; + if (ra < tbgn || ra >= tend) + { + // We do not know yet if update_map_segments is really needed + if (!__collector_check_segment (ra, &tbgn, &tend, 0)) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: __collector_check_segment fail. wctx->pc = 0x%lx\n", wctx->pc); + if (do_walk == 0) + { + cur->sp = cur_sp; + cur->fp = cur_fp; + do_walk = 1; + save_ctx = 1; + goto startWalk; + } + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_FAILURE); + return RA_FAILURE; + } + } + + unsigned long npc = adjust_ret_addr (ra, ra - tbgn, tend); + if (npc == 0) + { + DprintfT (SP_DUMP_UNWIND, "unwind.c: adjust_ret_addr fail. wctx->pc = 0x%lx\n", wctx->pc); + if (do_walk == 0) + { + cur->sp = cur_sp; + cur->fp = cur_fp; + do_walk = 1; + save_ctx = 1; + goto startWalk; + } + if (save_ctx) + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_FAILURE); + return RA_FAILURE; + } + wctx->pc = npc; + wctx->sp = (unsigned long) cur->sp; + wctx->fp = fp; + wctx->tbgn = tbgn; + wctx->tend = tend; + + if (save_ctx) + { + omp_cache_put (cur->sp_safe, &wctx_pc_save, wctx, RA_SUCCESS); + DprintfT (SP_DUMP_UNWIND, "unwind.c: cache walk context. wctx_pc_save->pc = 0x%lx\n", wctx_pc_save.pc); + } + return RA_SUCCESS; +} + +/* + * We have the return address, but we would like to report to the user + * the calling PC, which is the instruction immediately preceding the + * return address. Unfortunately, x86 instructions can have variable + * length. So we back up 8 bytes and try to figure out where the + * calling PC starts. (FWIW, call instructions are often 5-bytes long.) + */ +unsigned long +adjust_ret_addr (unsigned long ra, unsigned long segoff, unsigned long tend) +{ + unsigned long npc = 0; + int i = segoff < 8 ? segoff : 8; + for (; i > 1; i--) + { + unsigned char *ptr = (unsigned char*) ra - i; + int z = 4; + int a = 4; + int done = 0; + int bVal; + while (!done) + { + bVal = getByteInstruction (ptr); + if (bVal < 0) + return 0; + switch (bVal) + { + case 0x26: + case 0x36: +#if WSIZE(64) + ptr += 1; + break; +#endif + case 0x64: + case 0x65: + bVal = getByteInstruction (ptr + 1); + if (bVal < 0) + return 0; + if (bVal == 0xe8) + // a workaround for bug 16193041, assuming "call Jz" has no segment override prefix + done = 1; + else + ptr += 1; + break; + case 0x66: + z = 2; + ptr += 1; + break; + case 0x67: + a = 2; + ptr += 1; + break; + default: + done = 1; + break; + } + } +#if WSIZE(64) + bVal = getByteInstruction (ptr); + if (bVal < 0) + return 0; + if (bVal >= 0x40 && bVal <= 0x4f) + { /* XXXX not all REX codes applicable */ + if (bVal & 0x8) + z = 4; + ptr += 1; + } +#endif + int opcode = getByteInstruction (ptr); + if (opcode < 0) + return 0; + ptr++; + switch (opcode) + { + case 0xe8: /* call Jz (f64) */ + ptr += z; + break; + case 0x9a: /* callf Ap */ + ptr += 2 + a; + break; + case 0xff: /* calln Ev , callf Ep */ + { + int extop = MRM_EXT (*ptr); + if (extop == 2 || extop == 3) + ptr = check_modrm (ptr); + } + break; + default: + continue; + } + if ((unsigned long) ptr == ra) + { + npc = ra - i; + break; + } + } + if (npc == 0) + { + unsigned char * ptr = (unsigned char *) ra; +#if WSIZE(32) + // test __kernel_sigreturn or __kernel_rt_sigreturn + if ((ra + 7 < tend && getByteInstruction (ptr) == 0x58 + && getByteInstruction (ptr + 1) == 0xb8 + && getByteInstruction (ptr + 6) == 0xcd + && getByteInstruction (ptr + 7) == 0x80) /* pop %eax; mov $NNNN, %eax; int */ + || (ra + 7 < tend && getByteInstruction (ptr) == 0x58 + && getByteInstruction (ptr + 1) == 0xb8 + && getByteInstruction (ptr + 6) == 0x0f + && getByteInstruction (ptr + 7) == 0x05) /* pop %eax; mov $NNNN, %eax; syscall */ + || (ra + 6 < tend && getByteInstruction (ptr) == 0xb8 + && getByteInstruction (ptr + 5) == 0xcd + && getByteInstruction (ptr + 6) == 0x80) /* mov $NNNN, %eax; int */ + || (ra + 6 < tend && getByteInstruction (ptr) == 0xb8 + && getByteInstruction (ptr + 5) == 0x0f + && getByteInstruction (ptr + 6) == 0x05)) /* mov $NNNN, %eax; syscall */ +#else //WSIZE(64) + // test __restore_rt + if (ra + 8 < tend && getByteInstruction (ptr) == 0x48 + && getByteInstruction (ptr + 7) == 0x0f + && getByteInstruction (ptr + 8) == 0x05) /* mov $NNNNNNNN, %rax; syscall */ +#endif + { + npc = ra; + } + } + if (npc == 0 && __collector_java_mode + && __collector_java_asyncgetcalltrace_loaded) + { // detect jvm interpreter code for java user threads + unsigned char * ptr = (unsigned char *) ra; +#if WSIZE(32) + // up to J170 + /* + * ff 24 9d e0 64 02 f5 jmp *-0xafd9b20(,%ebx,4) + * 8b 4e 01 movl 1(%esi),%ecx + * f7 d1 notl %ecx + * 8b 5d ec movl -0x14(%ebp),%ebx + * c1 e1 02 shll $2,%ecx + * eb d8 jmp .-0x26 [ 0x92a ] + * 83 ec 08 subl $8,%esp || 8b 65 f8 movl -8(%ebp),%esp + * */ + if (ra - 20 >= (ra - segoff) && ((*ptr == 0x83 && *(ptr + 1) == 0xec) || (*ptr == 0x8b && *(ptr + 1) == 0x65)) + && *(ptr - 2) == 0xeb + && *(ptr - 5) == 0xc1 && *(ptr - 4) == 0xe1 + && *(ptr - 8) == 0x8b && *(ptr - 7) == 0x5d + && *(ptr - 10) == 0xf7 && *(ptr - 9) == 0xd1 + && *(ptr - 13) == 0x8b && *(ptr - 12) == 0x4e + && *(ptr - 20) == 0xff && *(ptr - 19) == 0x24 && *(ptr - 18) == 0x9d) + { + npc = ra - 20; + } + // J180 J190 + // ff 24 9d ** ** ** ** jmp *-0x*******(,%ebx,4) + if (npc == 0 + && ra - 7 >= (ra - segoff) + && *(ptr - 7) == 0xff + && *(ptr - 6) == 0x24 + && *(ptr - 5) == 0x9d) + { + npc = ra - 7; + } +#else //WSIZE(64) + // up to J170 + /* + * 41 ff 24 da jmp *(%r10,%rbx,8) + * 41 8b 4d 01 movl 1(%r13),%ecx + * f7 d1 notl %ecx + * 48 8b 5d d8 movq -0x28(%rbp),%rbx + * c1 e1 02 shll $2,%ecx + * eb cc jmp .-0x32 [ 0xd23 ] + * 48 8b 65 f0 movq -0x10(%rbp),%rsp + */ + if (ra - 19 >= (ra - segoff) && *ptr == 0x48 && ((*(ptr + 1) == 0x8b && *(ptr + 2) == 0x65) || (*(ptr + 1) == 0x83 && *(ptr + 2) == 0xec)) + && *(ptr - 2) == 0xeb + && *(ptr - 5) == 0xc1 && *(ptr - 4) == 0xe1 + && *(ptr - 9) == 0x48 && *(ptr - 8) == 0x8b && *(ptr - 7) == 0x5d + && *(ptr - 11) == 0xf7 && *(ptr - 10) == 0xd1 + && *(ptr - 15) == 0x41 && *(ptr - 14) == 0x8b && *(ptr - 13) == 0x4d + && *(ptr - 19) == 0x41 && *(ptr - 18) == 0xff) + npc = ra - 19; + // J180 J190 + // 41 ff 24 da jmp *(%r10,%rbx,8) + if (npc == 0 + && ra - 4 >= (ra - segoff) + && *(ptr - 4) == 0x41 + && *(ptr - 3) == 0xff + && *(ptr - 2) == 0x24 + && *(ptr - 1) == 0xda) + npc = ra - 4; +#endif + } + + return npc; +} + +/* + * Parses AVX instruction and returns its length. + * Returns 0 if parsing failed. + * https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf + */ +static int +parse_x86_AVX_instruction (unsigned char *pc) +{ + /* + * VEX prefix has a two-byte form (0xc5) and a three byte form (0xc4). + * If an instruction syntax can be encoded using the two-byte form, + * it can also be encoded using the three byte form of VEX. + * The latter increases the length of the instruction by one byte. + * This may be helpful in some situations for code alignment. + * + Byte 0 Byte 1 Byte 2 Byte 3 + (Bit Position) 7 0 7 6 5 4 0 7 6 3 2 10 + 3-byte VEX [ 11000100 ] [ R X B | m-mmmm ] [ W | vvvv | L | pp ] + 7 0 7 6 3 2 10 + 2-byte VEX [ 11000101 ] [ R | vvvv | L | pp ] + 7 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + 4-byte EVEX [ 01100010 ] [ R X B R1 0 0 m m ] [ W v v v v 1 p p ] [ z L1 L B1 V1 a a a ] + + R: REX.R in 1's complement (inverted) form + 0: Same as REX.R=1 (64-bit mode only) + 1: Same as REX.R=0 (must be 1 in 32-bit mode) + + X: REX.X in 1's complement (inverted) form + 0: Same as REX.X=1 (64-bit mode only) + 1: Same as REX.X=0 (must be 1 in 32-bit mode) + + B: REX.B in 1's complement (inverted) form + 0: Same as REX.B=1 (64-bit mode only) + 1: Same as REX.B=0 (Ignored in 32-bit mode). + + W: opcode specific (use like REX.W, or used for opcode + extension, or ignored, depending on the opcode byte) + + m-mmmm: + 00000: Reserved for future use (will #UD) + 00001: implied 0F leading opcode byte + 00010: implied 0F 38 leading opcode bytes + 00011: implied 0F 3A leading opcode bytes + 00100-11111: Reserved for future use (will #UD) + + vvvv: a register specifier (in 1's complement form) or 1111 if unused. + + L: Vector Length + 0: scalar or 128-bit vector + 1: 256-bit vector + + pp: opcode extension providing equivalent functionality of a SIMD prefix + 00: None + 01: 66 + 10: F3 + 11: F2 + * + * Example: 0xc5f877L vzeroupper + * VEX prefix: 0xc5 0x77 + * Opcode: 0xf8 + * + */ + int len = 0; + disassemble_info dis_info; + dis_info.arch = bfd_arch_i386; + dis_info.mach = bfd_mach_x86_64; + dis_info.flavour = bfd_target_unknown_flavour; + dis_info.endian = BFD_ENDIAN_UNKNOWN; + dis_info.endian_code = dis_info.endian; + dis_info.octets_per_byte = 1; + dis_info.disassembler_needs_relocs = FALSE; + dis_info.fprintf_func = fprintf_func; + dis_info.stream = NULL; + dis_info.disassembler_options = NULL; + dis_info.read_memory_func = read_memory_func; + dis_info.memory_error_func = memory_error_func; + dis_info.print_address_func = print_address_func; + dis_info.symbol_at_address_func = symbol_at_address_func; + dis_info.symbol_is_valid = symbol_is_valid; + dis_info.display_endian = BFD_ENDIAN_UNKNOWN; + dis_info.symtab = NULL; + dis_info.symtab_size = 0; + dis_info.buffer_vma = 0; + dis_info.buffer = pc; + dis_info.buffer_length = 8; + + disassembler_ftype disassemble = print_insn_i386; + if (disassemble == NULL) + { + DprintfT (SP_DUMP_UNWIND, "parse_x86_AVX_instruction ERROR: unsupported disassemble\n"); + return 0; + } + len = disassemble (0, &dis_info); + DprintfT (SP_DUMP_UNWIND, "parse_x86_AVX_instruction: returned %d pc: %p\n", len, pc); + return len; +} + +/* + * In the Intel world, a stack frame looks like this: + * + * %fp0->| | + * |-------------------------------| + * | Args to next subroutine | + * |-------------------------------|-\ + * %sp0->| One word struct-ret address | | + * |-------------------------------| > minimum stack frame (8 bytes) + * | Previous frame pointer (%fp0)| | + * %fp1->|-------------------------------|-/ + * | Local variables | + * %sp1->|-------------------------------| + * + */ + +int +stack_unwind (char *buf, int size, void *bptr, void *eptr, ucontext_t *context, int mode) +{ + long *lbuf = (long*) buf; + int lsize = size / sizeof (long); + int ind = 0; + int do_walk = 1; + int extra_frame = 0; + if (mode & FRINFO_NO_WALK) + do_walk = 0; + if ((mode & 0xffff) == FRINFO_FROM_STACK) + extra_frame = 1; + + /* + * trace the stack frames from user stack. + * We are assuming that the frame pointer and return address + * are null when we are at the top level. + */ + struct WalkContext wctx; + wctx.pc = GET_PC (context); + wctx.sp = GET_SP (context); + wctx.fp = GET_FP (context); + wctx.ln = (unsigned long) context->uc_link; + unsigned long *sbase = (unsigned long*) __collector_tsd_get_by_key (unwind_key); + if (sbase && *sbase > wctx.sp) + wctx.sbase = *sbase; + else + { + wctx.sbase = wctx.sp + 0x100000; + if (wctx.sbase < wctx.sp) /* overflow */ + wctx.sbase = (unsigned long) - 1; + } + // We do not know yet if update_map_segments is really needed + __collector_check_segment (wctx.pc, &wctx.tbgn, &wctx.tend, 0); + + for (;;) + { + if (ind >= lsize || wctx.pc == 0) + break; + if (bptr != NULL && extra_frame && wctx.sp <= (unsigned long) bptr && ind < 2) + { + lbuf[0] = wctx.pc; + if (ind == 0) + { + ind++; + if (ind >= lsize) + break; + } + } + if (bptr == NULL || wctx.sp > (unsigned long) bptr) + { + lbuf[ind++] = wctx.pc; + if (ind >= lsize) + break; + } + + for (;;) + { + if (eptr != NULL && wctx.sp >= (unsigned long) eptr) + { + ind = ind >= 2 ? ind - 2 : 0; + goto exit; + } + int ret = find_i386_ret_addr (&wctx, do_walk); + DprintfT (SP_DUMP_UNWIND, "stack_unwind (x86 walk):%d find_i386_ret_addr returns %d\n", __LINE__, ret); + if (ret == RA_FAILURE) + { + /* lbuf[ind++] = SP_FAILED_UNWIND_MARKER; */ + goto exit; + } + + if (ret == RA_END_OF_STACK) + goto exit; +#if WSIZE(32) + if (ret == RA_RT_SIGRETURN) + { + struct SigFrame + { + unsigned long arg0; + unsigned long arg1; + unsigned long arg2; + } *sframe = (struct SigFrame*) wctx.sp; + ucontext_t *ncontext = (ucontext_t*) sframe->arg2; + wctx.pc = GET_PC (ncontext); + if (!__collector_check_segment (wctx.pc, &wctx.tbgn, &wctx.tend, 0)) + { + /* lbuf[ind++] = SP_FAILED_UNWIND_MARKER; */ + goto exit; + } + unsigned long nsp = GET_SP (ncontext); + /* Check the new stack pointer */ + if (nsp <= sframe->arg2 || nsp > sframe->arg2 + sizeof (ucontext_t) + 1024) + { + /* lbuf[ind++] = SP_FAILED_UNWIND_MARKER; */ + goto exit; + } + wctx.sp = nsp; + wctx.fp = GET_FP (ncontext); + break; + } + else if (ret == RA_SIGRETURN) + { + struct sigcontext *sctx = (struct sigcontext*) wctx.sp; + wctx.pc = sctx->eip; + if (!__collector_check_segment (wctx.pc, &wctx.tbgn, &wctx.tend, 0)) + { + /* lbuf[ind++] = SP_FAILED_UNWIND_MARKER; */ + goto exit; + } + wctx.sp = sctx->esp; + wctx.fp = sctx->ebp; + break; + } +#elif WSIZE(64) + if (ret == RA_RT_SIGRETURN) + { + ucontext_t *ncontext = (ucontext_t*) wctx.sp; + wctx.pc = GET_PC (ncontext); + if (!__collector_check_segment (wctx.pc, &wctx.tbgn, &wctx.tend, 0)) + { + /* lbuf[ind++] = SP_FAILED_UNWIND_MARKER; */ + goto exit; + } + unsigned long nsp = GET_SP (ncontext); + /* Check the new stack pointer */ + if (nsp <= wctx.sp || nsp > wctx.sp + sizeof (ucontext_t) + 1024) + { + /* lbuf[ind++] = SP_FAILED_UNWIND_MARKER; */ + goto exit; + } + wctx.sp = nsp; + wctx.fp = GET_FP (ncontext); + break; + } +#endif /* WSIZE() */ + if (bptr != NULL && extra_frame && wctx.sp <= (unsigned long) bptr && ind < 2) + { + lbuf[0] = wctx.pc; + if (ind == 0) + { + ind++; + if (ind >= lsize) + break; + } + } + if (bptr == NULL || wctx.sp > (unsigned long) bptr) + { + lbuf[ind++] = wctx.pc; + if (ind >= lsize) + goto exit; + } + } + } + +exit: +#if defined(DEBUG) + if ((SP_DUMP_UNWIND & __collector_tracelevel) != 0) + { + DprintfT (SP_DUMP_UNWIND, "stack_unwind (x86 walk):%d found %d frames\n\n", __LINE__, ind); + for (int i = 0; i < ind; i++) + DprintfT (SP_DUMP_UNWIND, " %3d: 0x%lx\n", i, (unsigned long) lbuf[i]); + } +#endif + dump_stack (__LINE__); + if (ind >= lsize) + { + ind = lsize - 1; + lbuf[ind++] = (unsigned long) SP_TRUNC_STACK_MARKER; + } + return ind * sizeof (long); +} + +#elif ARCH(Aarch64) + +static int +stack_unwind (char *buf, int size, void *bptr, void *eptr, ucontext_t *context, int mode) +{ + if (buf && bptr && eptr && context && size + mode > 0) + getByteInstruction ((unsigned char *) eptr); + int ind = 0; + __u64 *lbuf = (void *) buf; + int lsize = size / sizeof (__u64); + __u64 pc = context->uc_mcontext.pc; + __u64 sp = context->uc_mcontext.sp; + __u64 stack_base; + unsigned long tbgn = 0; + unsigned long tend = 0; + + unsigned long *sbase = (unsigned long*) __collector_tsd_get_by_key (unwind_key); + if (sbase && *sbase > sp) + stack_base = *sbase; + else + { + stack_base = sp + 0x100000; + if (stack_base < sp) // overflow + stack_base = (__u64) -1; + } + DprintfT (SP_DUMP_UNWIND, + "unwind.c:%d stack_unwind %2d pc=0x%llx sp=0x%llx stack_base=0x%llx\n", + __LINE__, ind, (unsigned long long) pc, (unsigned long long) sp, + (unsigned long long) stack_base); + + while (sp && pc) + { + DprintfT (SP_DUMP_UNWIND, + "unwind.c:%d stack_unwind %2d pc=0x%llx sp=0x%llx\n", + __LINE__, ind, (unsigned long long) pc, (unsigned long long) sp); +// Dl_info dlinfo; +// if (!dladdr ((void *) pc, &dlinfo)) +// break; +// DprintfT (SP_DUMP_UNWIND, "%2d: %llx <%s+%llu> (%s)\n", +// ind, (unsigned long long) pc, +// dlinfo.dli_sname ? dlinfo.dli_sname : "(?)", +// (unsigned long long) pc - (unsigned long long) dlinfo.dli_saddr, +// dlinfo.dli_fname); + lbuf[ind++] = pc; + if (ind >= lsize || sp >= stack_base || (sp & 15) != 0) + break; + if (pc < tbgn || pc >= tend) + if (!__collector_check_segment ((unsigned long) pc, &tbgn, &tend, 0)) + { + DprintfT (SP_DUMP_UNWIND, + "unwind.c:%d __collector_check_segment failed. sp=0x%lx\n", + __LINE__, (unsigned long) sp); + break; + } + pc = ((__u64 *) sp)[1]; + __u64 old_sp = sp; + sp = ((__u64 *) sp)[0]; + if (sp < old_sp) + break; + } + if (ind >= lsize) + { + ind = lsize - 1; + lbuf[ind++] = (__u64) SP_TRUNC_STACK_MARKER; + } + return ind * sizeof (__u64); +} +#endif /* ARCH() */ diff --git a/gprofng/src/ABS.h b/gprofng/src/ABS.h new file mode 100644 index 0000000..a5bcbea --- /dev/null +++ b/gprofng/src/ABS.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _ABS_H +#define _ABS_H + +/* + * Apropos Backtracking Scheme definitions. + * Class: com_sun_forte_st_mpmt_timeline_HWCEvent + */ + +/* ABS failure codes */ +typedef enum +{ + ABS_NULL = 0x00, /* undefined/disabled/inactive */ + ABS_UNSUPPORTED = 0x01, /* inappropriate HWC event type */ + ABS_BLOCKED = 0x02, /* runtime backtrack blocker reached */ + ABS_INCOMPLETE = 0x03, /* runtime backtrack limit reached */ + ABS_REG_LOSS = 0x04, /* address register contaminated */ + ABS_INVALID_EA = 0x05, /* invalid effective address value */ + ABS_NO_CTI_INFO = 0x10, /* no AnalyzerInfo for validation */ + ABS_INFO_FAILED = 0x20, /* info failed to validate backtrack */ + ABS_CTI_TARGET = 0x30, /* CTI target invalidated backtrack */ + ABS_CODE_RANGE = 0xFF /* reserved ABS code range in Vaddr */ +} ABS_code; + +enum { + NUM_ABS_RT_CODES = 7, + NUM_ABS_PP_CODES = 5 +}; + +extern const char *ABS_RT_CODES[NUM_ABS_RT_CODES]; +extern char *ABS_PP_CODES[NUM_ABS_PP_CODES]; + +/* libcollector will mark HWC overflow values that appear to be invalid */ +/* dbe should check HWC values for errors */ +#define HWCVAL_ERR_FLAG (1ULL<<63) +#define HWCVAL_SET_ERR(ctr) ((ctr) | HWCVAL_ERR_FLAG) +#define HWCVAL_HAS_ERR(ctr) ((ctr) & HWCVAL_ERR_FLAG ? 1 : 0) +#define HWCVAL_CLR_ERR(ctr) ((ctr) & ~HWCVAL_ERR_FLAG) + +#define ABS_GET_RT_CODE(EA) ((EA) & 0x0FLL) +#define ABS_GET_PP_CODE(EA) (((EA) & 0xF0LL) / 0xF) + +#endif /* _ABS_H */ diff --git a/gprofng/src/Application.cc b/gprofng/src/Application.cc new file mode 100644 index 0000000..e28956c --- /dev/null +++ b/gprofng/src/Application.cc @@ -0,0 +1,259 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdlib.h> +#include <strings.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "Application.h" +#include "Settings.h" +#include "i18n.h" +#include "util.h" + +Application::ProgressFunc Application::progress_func = NULL; +Application *theApplication; + +Application::Application (int argc, char *argv[], char *fdhome) +{ + theApplication = this; + cur_dir = NULL; + prog_version = dbe_strdup (VERSION); + set_name (strchr (argv[0], '/') ? argv[0] : NULL); + whoami = get_basename (get_name ()); + + // set up a queue for comments + commentq = new Emsgqueue (NTXT ("app_commentq")); + + // Locate where the binaries are installed + set_run_dir (fdhome); + + // Initialize I18N + init_locale (run_dir); + + // Initialize licensing data + lic_found = 0; + lic_err = NULL; + + // Initialize worker threads + number_of_worker_threads = 1; +#if DEBUG + char *use_worker_threads = getenv (NTXT ("SP_USE_WORKER_THREADS")); + if ((NULL != use_worker_threads) && (0 == strcasecmp (use_worker_threads, NTXT ("no")))) + { + number_of_worker_threads = 0; + } +#endif /* DEBUG */ + settings = new Settings (this); +} + +Application::~Application () +{ + delete commentq; + delete settings; + free (prog_version); + free (cur_dir); + free (prog_name); + free (run_dir); +} + +// Set the name of the application (for messages) +void +Application::set_name (const char *_name) +{ + prog_name = get_realpath (_name); +} + +char * +Application::get_realpath (const char *_name) +{ + if (_name == NULL) + _name = "/proc/self/exe"; + char *exe_name = realpath (_name, NULL); + if (exe_name) + return exe_name; + if (strchr (_name, '/') == NULL) + { + char *path = getenv ("PATH"); + if (path) + for (char *s = path;; s++) + if (*s == ':' || *s == 0) + { + if (path != s) + { + char *nm = dbe_sprintf (NTXT ("%.*s/%s"), (int) (path - s - 1), path, _name); + exe_name = realpath (nm, NULL); + free (nm); + if (exe_name) + return exe_name; + } + if (*s == 0) + break; + path = s + 1; + } + } + return strdup (_name); +} + +// Set the directory where all binaries are found +void +Application::set_run_dir (char *fdhome) +{ + run_dir_with_spaces = NULL; + if (fdhome) + { + char *path = dbe_sprintf ("%s/bin", fdhome); + struct stat sbuf; + if (stat (path, &sbuf) != -1) + run_dir = path; + else + { + free (path); + run_dir = dbe_strdup (fdhome); + } + } + else + { + run_dir = realpath (prog_name, NULL); + if (run_dir == NULL) + { + fprintf (stderr, // I18N won't work here -- not catopen yet. + GTXT ("Can't find location of %s\n"), prog_name); + run_dir = dbe_strdup (get_cur_dir ()); + } + else + { + char *d = strrchr (run_dir, '/'); + if (d) + *d = 0; + // Check if the installation path contains spaces + if (strchr (run_dir, ' ') != NULL) + { + // Create a symbolic link without spaces + const char *dir = NTXT ("/tmp/.gprofngLinks"); + char *symbolic_link = dbe_create_symlink_to_path (run_dir, dir); + if (NULL != symbolic_link) + { + // Save old path to avoid memory leak + run_dir_with_spaces = run_dir; + // Use the path through symbolic link + run_dir = symbolic_link; + } + } + } + } +} + +char * +Application::get_cur_dir () +{ + if (cur_dir == NULL) + { + char cwd[MAXPATHLEN]; + if (getcwd (cwd, sizeof (cwd)) == NULL) + { + perror (prog_name); + exit (1); + } + cur_dir = dbe_strdup (canonical_path (cwd)); + } + return cur_dir; +} + +/** + * Get number of worker threads + * This is used to decide if it is ok to use worker threads for stat() + * and other actions that can hang for a long time + * @return number_of_worker_threads + */ +int +Application::get_number_of_worker_threads () +{ + return number_of_worker_threads; +} + +int +Application::check_args (int argc, char *argv[]) +{ + int opt; + // Parsing the command line + opterr = 0; + while ((opt = getopt (argc, argv, "V")) != EOF) + switch (opt) + { + case 'V': +// Ruud + Application::print_version_info (); +/* + printf (NTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION); +*/ + exit (0); + default: + usage (); + } + return optind; +} + +Emsg * +Application::fetch_comments () +{ + if (commentq == NULL) + return NULL; + return commentq->fetch (); +} + +void +Application::queue_comment (Emsg *m) +{ + commentq->append (m); +} + +void +Application::delete_comments () +{ + if (commentq != NULL) + { + delete commentq; + commentq = new Emsgqueue (NTXT ("app_commentq")); + } +} + +int +Application::set_progress (int percentage, const char *proc_str) +{ + if (progress_func != NULL) + return progress_func (percentage, proc_str); + return 0; +} + +// Ruud +void +Application::print_version_info () +{ + printf ( GTXT ( + "GNU %s binutils version %s\n" + "Copyright (C) 2021 Free Software Foundation, Inc.\n" + "License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"), + get_basename (prog_name), VERSION); +} diff --git a/gprofng/src/Application.h b/gprofng/src/Application.h new file mode 100644 index 0000000..404383b --- /dev/null +++ b/gprofng/src/Application.h @@ -0,0 +1,108 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * The Application class is the base class for all C++ executables + * in the Performance Tools Suite + * + * It determines the directory from which the running binary came, + * sets up the I18N catalog, the program name, and initializes + * an instance of the Settings class to manage all user preferences + * and settings. It also manages usage tracking. + * + * Applications which read experiments are derived from a subclass + * named DbeApplication (q.v.) + */ + +#ifndef _APPLICATION_H +#define _APPLICATION_H + +#include "dbe_types.h" + +class Settings; +class Emsg; +class Emsgqueue; + +// Application object +class Application +{ +public: + Application (int argc, char *argv[], char *_run_dir = NULL); + virtual ~Application (); + void set_name (const char *_name); + char *get_cur_dir (); + + // Control the settings of a progress bar, used for GUI applications + // this function also detects cancel requests and returns 1 + // if yes, 0 otherwise + static int set_progress (int percentage, const char *proc_str); + static char *get_realpath (const char *_name); + + // queue for messages (from reading er.rc files, ...) + void queue_comment (Emsg *m); // queue for messages + Emsg *fetch_comments (void); // fetch the queue of comment messages + void delete_comments (void); // delete the queue of comment messages + + // worker threads (currently used in dbe_stat() for stat() calls) + int get_number_of_worker_threads (); + + char *get_version () { return prog_version; } + char *get_name () { return prog_name; } + char *get_run_dir () { return run_dir; } + Emsgqueue *get_comments_queue () { return commentq; }; + +protected: // methods + void set_run_dir (char *fdhome = NULL); + typedef int (*ProgressFunc)(int, const char *); + + // Write a usage message; to be defined in derived class + virtual void usage () = 0; + +// Ruud + // Write a version message; to be defined in derived class + void print_version_info (); + + // Can be overridden in derived class + virtual int check_args (int argc, char *argv[]); + + void read_rc (); + static void set_progress_func (ProgressFunc func) { progress_func = func; } + +protected: + Emsgqueue *commentq; + Settings *settings; + char *prog_version; + char *prog_name; + char *whoami; + char *run_dir; + char *run_dir_with_spaces; // used in case there are spaces + char *cur_dir; + int lic_found; + char *lic_err; + +private: + void set_ut_email (int argc, char *argv[]); + int number_of_worker_threads; + static ProgressFunc progress_func; +}; + +extern Application *theApplication; + +#endif /* _APPLICATION_H */ diff --git a/gprofng/src/ArchiveExp.cc b/gprofng/src/ArchiveExp.cc new file mode 100644 index 0000000..e7ebfa9 --- /dev/null +++ b/gprofng/src/ArchiveExp.cc @@ -0,0 +1,149 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <unistd.h> + +#include "util.h" +#include "DbeSession.h" +#include "LoadObject.h" +#include "ArchiveExp.h" +#include "DbeFile.h" +#include "CallStack.h" +#include "gp-archive.h" +#include "Function.h" +#include "Module.h" + +ArchiveExp::ArchiveExp (char *path) : Experiment () +{ + force_flag = false; + copyso_flag = false; + use_fndr_archives = true; + status = find_expdir (path); + if (status == SUCCESS) + read_log_file (); +} + +ArchiveExp::~ArchiveExp () { } + +void +ArchiveExp::read_data (int s_option) +{ + read_archives (); + read_map_file (); + if (read_java_classes_file () == SUCCESS) + { + for (int i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d loadObjs[%d]=%-25s %s\n"), + get_basename (__FILE__), (int) __LINE__, i, + STR (lo->get_name ()), STR (lo->get_pathname ())); + if ((lo->dbeFile->filetype & DbeFile::F_JAVACLASS) == 0) + continue; + lo->isUsed = true; + if ((s_option & ARCH_EXE_ONLY) != 0) + continue; + lo->sync_read_stabs (); + } + } + if ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0) + { + read_frameinfo_file (); + resolveFrameInfo = true; + Vector<DataDescriptor*> *ddscr = getDataDescriptors (); + delete ddscr; // getDataDescriptors() forces reading of experiment data + CallStack *callStack = callTree (); + if (callStack) + { + if (DEBUG_ARCHIVE) + { + Dprintf (DEBUG_ARCHIVE, NTXT ("stacks=%p\n"), callStack); + callStack->print (NULL); + } + for (int n = 0;; n++) + { + CallStackNode *node = callStack->get_node (n); + if (node == NULL) + break; + do + { + Histable *h = node->get_instr (); + Histable::Type t = h->get_type (); + if (t == Histable::INSTR) + { + DbeInstr *dbeInstr = (DbeInstr *) h; + if (!dbeInstr->isUsed) + { + Function *func = (Function *) dbeInstr->convertto (Histable::FUNCTION); + if (!func->isUsed) + { + func->isUsed = true; + func->module->isUsed = true; + func->module->loadobject->isUsed = true; + } + DbeLine *dbeLine = (DbeLine *) dbeInstr->convertto (Histable::LINE); + if (dbeLine) + dbeLine->sourceFile->isUsed = true; + } + } + else if (t == Histable::LINE) + { + DbeLine * dbeLine = (DbeLine *) h; + dbeLine->sourceFile->isUsed = true; + } + node = node->ancestor; + } + while (node); + } + } + } +} + +char * +ArchiveExp::createLinkToFndrArchive (LoadObject *lo, int /* hide_msg */) +{ + // For example, archives of libc.so will be: + // <exp>/archives/<libc.so_check_sum> + // <exp>/M_r0.er/archives/libc.so_<hash> -> ../../archives/<libc.so_check_sum> + if (!create_dir (get_fndr_arch_name ())) + { + fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), get_fndr_arch_name ()); + return NULL; + } + uint32_t checksum = lo->get_checksum (); + char *linkName = dbe_sprintf (NTXT ("../../%s/%u"), SP_ARCHIVES_DIR, checksum); + char *nm = lo->get_pathname (); + char *symLinkName = getNameInArchive (nm, false); + if (symlink (linkName, symLinkName) != 0) + { + fprintf (stderr, GTXT ("Unable to create link `%s' -> `%s'\n"), + symLinkName, linkName); + free (linkName); + free (symLinkName); + return NULL; + } + free (linkName); + free (symLinkName); + + // Return a full path inside founder archive: + return dbe_sprintf (NTXT ("%s/%u"), get_fndr_arch_name (), checksum); +} diff --git a/gprofng/src/ArchiveExp.h b/gprofng/src/ArchiveExp.h new file mode 100644 index 0000000..809ed58 --- /dev/null +++ b/gprofng/src/ArchiveExp.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _ARCHIVE_EXP_H +#define _ARCHIVE_EXP_H + +#include "Experiment.h" +class LoadObject; + +class ArchiveExp : public Experiment +{ +public: + ArchiveExp (char *path); + ~ArchiveExp (); + char *createLinkToFndrArchive (LoadObject *lo, int hide_msg); + void read_data (int s_option); + +private: + bool force_flag; + bool copyso_flag; + bool use_fndr_archives; +}; + +#endif /* _ARCHIVE_EXP_H */ diff --git a/gprofng/src/BaseMetric.cc b/gprofng/src/BaseMetric.cc new file mode 100644 index 0000000..bb51ddf --- /dev/null +++ b/gprofng/src/BaseMetric.cc @@ -0,0 +1,975 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <strings.h> +#include <stdlib.h> + +#include "util.h" +#include "BaseMetric.h" +#include "DbeSession.h" +#include "Expression.h" + +int BaseMetric::last_id = 0; + +void +BaseMetric::init (Type t) +{ + id = last_id++; + type = t; + aux = NULL; + cmd = NULL; + username = NULL; + hw_ctr = NULL; + cond = NULL; + val = NULL; + expr = NULL; + cond_spec = NULL; + val_spec = NULL; + expr_spec = NULL; + legend = NULL; + definition = NULL; + dependent_bm = NULL; + zeroThreshold = 0; + clock_unit = (Presentation_clock_unit) 0; + for (int ii = 0; ii < NSUBTYPES; ii++) + default_visbits[ii] = VAL_NA; + valtype = VT_DOUBLE; + precision = METRIC_HR_PRECISION; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_TIMEVAL | VAL_PERCENT; +} + +BaseMetric::BaseMetric (Type t) +{ + init (t); + switch (t) + { + case CP_LMS_USER: + case CP_LMS_SYSTEM: + case CP_LMS_WAIT_CPU: + case CP_LMS_USER_LOCK: + case CP_LMS_TFAULT: + case CP_LMS_DFAULT: + case OMP_MASTER_THREAD: + case CP_TOTAL: + case CP_TOTAL_CPU: + case CP_LMS_TRAP: + case CP_LMS_KFAULT: + case CP_LMS_SLEEP: + case CP_LMS_STOPPED: + case OMP_NONE: + case OMP_OVHD: + case OMP_WORK: + case OMP_IBAR: + case OMP_EBAR: + case OMP_WAIT: + case OMP_SERL: + case OMP_RDUC: + case OMP_LKWT: + case OMP_CTWT: + case OMP_ODWT: + case OMP_MSTR: + case OMP_SNGL: + case OMP_ORDD: + case CP_KERNEL_CPU: + // all of these are floating point, precision = clock profile tick + valtype = VT_DOUBLE; + precision = METRIC_SIG_PRECISION; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_TIMEVAL | VAL_PERCENT; + break; + case SYNC_WAIT_TIME: + case IO_READ_TIME: + case IO_WRITE_TIME: + case IO_OTHER_TIME: + case IO_ERROR_TIME: + // all of these are floating point, precision = hrtime tick + valtype = VT_DOUBLE; + precision = METRIC_HR_PRECISION; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_TIMEVAL | VAL_PERCENT; + break; + case SYNC_WAIT_COUNT: + case HEAP_ALLOC_CNT: + case HEAP_LEAK_CNT: + case IO_READ_CNT: + case IO_WRITE_CNT: + case IO_OTHER_CNT: + case IO_ERROR_CNT: + valtype = VT_LLONG; + precision = 1; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_VALUE | VAL_PERCENT; + break; + case RACCESS: + case DEADLOCKS: + // all of these are integer + valtype = VT_LLONG; + precision = 1; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_VALUE | VAL_PERCENT; + zeroThreshold = 1; + break; + case HEAP_ALLOC_BYTES: + case HEAP_LEAK_BYTES: + case IO_READ_BYTES: + case IO_WRITE_BYTES: + // all of these are longlong + valtype = VT_ULLONG; + precision = 1; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_VALUE | VAL_PERCENT; + break; + case SIZES: + valtype = VT_LLONG; + precision = 1; + flavors = STATIC; + value_styles = VAL_VALUE; + break; + case ADDRESS: + valtype = VT_ADDRESS; + precision = 1; + flavors = STATIC; + value_styles = VAL_VALUE; + break; + case ONAME: + valtype = VT_LABEL; + precision = 0; + flavors = STATIC; + value_styles = VAL_VALUE; + break; + case HWCNTR: // We should call the other constructor for hwc metric + default: + abort (); + } + specify (); +} + +// Constructor for linked HW counters (base counter) +BaseMetric::BaseMetric (Hwcentry *ctr, const char* _aux, const char* _username, + int v_styles, BaseMetric* _dependent_bm) +{ + hwc_init (ctr, _aux, _aux, _username, v_styles); + dependent_bm = _dependent_bm; +} + +// Constructor for linked HW counters (derived counter) + +BaseMetric::BaseMetric (Hwcentry *ctr, const char *_aux, const char *_cmdname, + const char *_username, int v_styles) +{ + hwc_init (ctr, _aux, _cmdname, _username, v_styles); +} + +void +BaseMetric::hwc_init (Hwcentry *ctr, const char* _aux, const char* _cmdname, + const char* _username, int v_styles) +{ + init (HWCNTR); + aux = dbe_strdup (_aux); // HWC identifier + cmd = dbe_strdup (_cmdname); // may differ from _aux for cycles->time hwcs + username = dbe_strdup (_username); + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = v_styles | VAL_PERCENT; + if ((value_styles & (VAL_TIMEVAL | VAL_VALUE)) == VAL_TIMEVAL) + valtype = VT_DOUBLE; + else + valtype = VT_ULLONG; + if (ABST_MEMSPACE_ENABLED (ctr->memop)) + flavors |= DATASPACE; // only for ctrs with memop definitions + hw_ctr = ctr; + specify (); +} + +// Constructor for derived metrics +BaseMetric::BaseMetric (const char *_cmd, const char *_username, + Definition *def) +{ + init (DERIVED); + cmd = dbe_strdup (_cmd); + username = dbe_strdup (_username); + aux = dbe_strdup (_cmd); + definition = def; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + clock_unit = CUNIT_NULL; // should it be CUNIT_TIME or 0 or something? + + /* we're not going to process packets for derived metrics */ + packet_type = (ProfData_type) (-1); + value_styles = VAL_VALUE; + valtype = VT_DOUBLE; + precision = 1000; +} + +// Copy constructor +BaseMetric::BaseMetric (const BaseMetric& m) +{ + id = m.id; + type = m.type; + aux = dbe_strdup (m.aux); + cmd = dbe_strdup (m.cmd); + username = dbe_strdup (m.username); + flavors = m.flavors; + value_styles = m.value_styles; + valtype = m.valtype; + precision = m.precision; + hw_ctr = m.hw_ctr; + packet_type = m.packet_type; + zeroThreshold = m.zeroThreshold; + clock_unit = m.clock_unit; + for (int ii = 0; ii < NSUBTYPES; ii++) + default_visbits[ii] = m.default_visbits[ii]; + if (m.cond_spec) + { + cond_spec = strdup (m.cond_spec); + cond = m.cond->copy (); + } + else + { + cond = NULL; + cond_spec = NULL; + } + if (m.val_spec) + { + val_spec = strdup (m.val_spec); + val = m.val->copy (); + } + else + { + val = NULL; + val_spec = NULL; + } + if (m.expr_spec) + { + expr_spec = strdup (m.expr_spec); + expr = m.expr->copy (); + } + else + { + expr = NULL; + expr_spec = NULL; + } + legend = dbe_strdup (m.legend); + definition = NULL; + if (m.definition) + definition = Definition::add_definition (m.definition->def); + dependent_bm = m.dependent_bm; +} + +BaseMetric::~BaseMetric () +{ + free (aux); + free (cmd); + free (cond_spec); + free (val_spec); + free (expr_spec); + free (legend); + free (username); + delete cond; + delete val; + delete expr; + delete definition; +} + +bool +BaseMetric::is_internal () +{ + return (get_value_styles () & VAL_INTERNAL) != 0; +} + +int +BaseMetric::get_default_visbits (SubType subtype) +{ + int rc = VAL_NA; + switch (subtype) + { + case STATIC: + case EXCLUSIVE: + rc = default_visbits[0]; + break; + case INCLUSIVE: + rc = default_visbits[1]; + break; + default: + break; + } + return rc; +} + +void +BaseMetric::set_default_visbits (SubType subtype, int _visbits) +{ + switch (subtype) + { + case STATIC: + case EXCLUSIVE: + default_visbits[0] = _visbits; + break; + case INCLUSIVE: + default_visbits[1] = _visbits; + break; + default: + break; + } +} + +void +BaseMetric::set_cond_spec (char *_cond_spec) +{ + if (cond_spec) + { + free (cond_spec); + delete cond; + cond_spec = NULL; + cond = NULL; + } + if (_cond_spec) + { + cond = dbeSession->ql_parse (_cond_spec); + if (cond == NULL) + { + fprintf (stderr, GTXT ("Invalid expression in metric specification `%s'\n"), _cond_spec); + abort (); + } + cond_spec = dbe_strdup (_cond_spec); + } +} + +void +BaseMetric::set_val_spec (char *_val_spec) +{ + if (val_spec) + { + free (val_spec); + delete val; + val_spec = NULL; + val = NULL; + } + if (_val_spec) + { + val = dbeSession->ql_parse (_val_spec); + if (val == NULL) + { + fprintf (stderr, GTXT ("Invalid expression in metric specification `%s'\n"), _val_spec); + abort (); + } + val_spec = dbe_strdup (_val_spec); + } +} + +void +BaseMetric::set_expr_spec (char *_expr_spec) +{ + id = last_id++; + if (expr_spec) + { + free (expr_spec); + delete expr; + expr_spec = NULL; + expr = NULL; + } + if (_expr_spec) + { + expr = dbeSession->ql_parse (_expr_spec); + if (expr == NULL) + { + fprintf (stderr, GTXT ("Invalid expression in metric specification `%s'\n"), _expr_spec); + return; + } + expr_spec = dbe_strdup (_expr_spec); + } +} + +void +BaseMetric::specify_mstate_metric (int st) +{ + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("MSTATE==%d"), st); + specify_prof_metric (buf); +} + +void +BaseMetric::specify_ompstate_metric (int st) +{ + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("OMPSTATE==%d"), st); + specify_prof_metric (buf); +} + +void +BaseMetric::specify_prof_metric (char *_cond_spec) +{ + packet_type = DATA_CLOCK; + specify_metric (_cond_spec, NTXT ("NTICK_USEC")); // microseconds +} + +void +BaseMetric::specify_metric (char *_cond_spec, char *_val_spec) +{ + set_cond_spec (_cond_spec); + set_val_spec (_val_spec); +} + +void +BaseMetric::specify () +{ + enum + { + IDLE_STATE_BITS = + (1 << OMP_IDLE_STATE) | (1 << OMP_IBAR_STATE) | (1 << OMP_EBAR_STATE) | + (1 << OMP_LKWT_STATE) | (1 << OMP_CTWT_STATE) | (1 << OMP_ODWT_STATE) | + (1 << OMP_ATWT_STATE) | (1 << OMP_TSKWT_STATE), + LMS_USER_BITS = + (1 << OMP_NO_STATE) | (1 << OMP_WORK_STATE) | (1 << OMP_SERL_STATE) | + (1 << OMP_RDUC_STATE) + }; + + char buf[256]; + char buf2[256]; + packet_type = (ProfData_type) - 1; // illegal value + clock_unit = CUNIT_TIME; + switch (type) + { + case SIZES: + username = dbe_strdup (GTXT ("Size")); + clock_unit = CUNIT_BYTES; + cmd = dbe_strdup (NTXT ("size")); + break; + case ADDRESS: + username = dbe_strdup (GTXT ("PC Address")); + cmd = dbe_strdup (NTXT ("address")); + break; + case ONAME: + username = dbe_strdup (GTXT ("Name")); + cmd = dbe_strdup (NTXT ("name")); + break; + case CP_LMS_SYSTEM: + username = dbe_strdup (GTXT ("System CPU Time")); + specify_mstate_metric (LMS_SYSTEM); + cmd = dbe_strdup (NTXT ("system")); + break; + case CP_TOTAL_CPU: + username = dbe_strdup (GTXT ("Total CPU Time")); + snprintf (buf, sizeof (buf), + "(MSTATE==%d)||(MSTATE==%d)||(MSTATE==%d)||(MSTATE==%d)", + LMS_USER, LMS_SYSTEM, LMS_TRAP, LMS_LINUX_CPU); + specify_prof_metric (buf); + cmd = dbe_strdup (NTXT ("totalcpu")); + break; + case CP_TOTAL: + username = dbe_strdup (GTXT ("Total Thread Time")); + snprintf (buf, sizeof (buf), NTXT ("(MSTATE!=%d)&&(MSTATE!=%d)"), + LMS_KERNEL_CPU, LMS_LINUX_CPU); + specify_prof_metric (buf); + cmd = dbe_strdup (NTXT ("total")); + break; + case CP_KERNEL_CPU: + username = dbe_strdup (GTXT ("Kernel CPU Time")); + specify_mstate_metric (LMS_KERNEL_CPU); + cmd = dbe_strdup (NTXT ("kcpu")); + break; + case OMP_MASTER_THREAD: + username = dbe_strdup (GTXT ("Master Thread Time")); + specify_prof_metric (NTXT ("LWPID==1")); + cmd = dbe_strdup (NTXT ("masterthread")); + break; + case CP_LMS_USER: + username = dbe_strdup (GTXT ("User CPU Time")); + specify_mstate_metric (LMS_USER); + cmd = dbe_strdup (NTXT ("user")); + break; + case CP_LMS_WAIT_CPU: + username = dbe_strdup (GTXT ("Wait CPU Time")); + specify_mstate_metric (LMS_WAIT_CPU); + cmd = dbe_strdup (NTXT ("wait")); + break; + case CP_LMS_USER_LOCK: + username = dbe_strdup (GTXT ("User Lock Time")); + specify_mstate_metric (LMS_USER_LOCK); + cmd = dbe_strdup (NTXT ("lock")); + break; + case CP_LMS_TFAULT: + username = dbe_strdup (GTXT ("Text Page Fault Time")); + specify_mstate_metric (LMS_TFAULT); + cmd = dbe_strdup (NTXT ("textpfault")); + break; + case CP_LMS_DFAULT: + username = dbe_strdup (GTXT ("Data Page Fault Time")); + specify_mstate_metric (LMS_DFAULT); + cmd = dbe_strdup (NTXT ("datapfault")); + break; + case CP_LMS_TRAP: + username = dbe_strdup (GTXT ("Trap CPU Time")); + specify_mstate_metric (LMS_TRAP); + cmd = dbe_strdup (NTXT ("trap")); + break; + case CP_LMS_KFAULT: + username = dbe_strdup (GTXT ("Kernel Page Fault Time")); + specify_mstate_metric (LMS_KFAULT); + cmd = dbe_strdup (NTXT ("kernelpfault")); + break; + case CP_LMS_SLEEP: + username = dbe_strdup (GTXT ("Sleep Time")); + specify_mstate_metric (LMS_SLEEP); + cmd = dbe_strdup (NTXT ("sleep")); + break; + case CP_LMS_STOPPED: + username = dbe_strdup (GTXT ("Stopped Time")); + specify_mstate_metric (LMS_STOPPED); + cmd = dbe_strdup (NTXT ("stop")); + break; + case OMP_OVHD: + username = dbe_strdup (GTXT ("OpenMP Overhead Time")); + specify_ompstate_metric (OMP_OVHD_STATE); + cmd = dbe_strdup (NTXT ("ompovhd")); + break; + case OMP_WORK: + username = dbe_strdup (GTXT ("OpenMP Work Time")); + snprintf (buf, sizeof (buf), + NTXT ("(OMPSTATE>=0) && (MSTATE==%d) && ((1<<OMPSTATE) & %d)"), + LMS_USER, LMS_USER_BITS); + specify_prof_metric (buf); + cmd = dbe_strdup (NTXT ("ompwork")); + break; + case OMP_WAIT: + username = dbe_strdup (GTXT ("OpenMP Wait Time")); + snprintf (buf, sizeof (buf), + "OMPSTATE>=0 && ((1<<OMPSTATE) & ((MSTATE!=%d) ? %d : %d))", + LMS_USER, (LMS_USER_BITS | IDLE_STATE_BITS), IDLE_STATE_BITS); + specify_prof_metric (buf); + cmd = dbe_strdup (NTXT ("ompwait")); + break; + case OMP_IBAR: + username = dbe_strdup (GTXT ("OpenMP Implicit Barrier Time")); + specify_ompstate_metric (OMP_IBAR_STATE); + cmd = dbe_strdup (NTXT ("ompibar")); + break; + case OMP_EBAR: + username = dbe_strdup (GTXT ("OpenMP Explicit Barrier Time")); + specify_ompstate_metric (OMP_EBAR_STATE); + cmd = dbe_strdup (NTXT ("ompebar")); + break; + case OMP_SERL: + username = dbe_strdup (GTXT ("OpenMP Serial Time")); + specify_ompstate_metric (OMP_SERL_STATE); + cmd = dbe_strdup (NTXT ("ompserl")); + break; + case OMP_RDUC: + username = dbe_strdup (GTXT ("OpenMP Reduction Time")); + specify_ompstate_metric (OMP_RDUC_STATE); + cmd = dbe_strdup (NTXT ("omprduc")); + break; + case OMP_LKWT: + username = dbe_strdup (GTXT ("OpenMP Lock Wait Time")); + specify_ompstate_metric (OMP_LKWT_STATE); + cmd = dbe_strdup (NTXT ("omplkwt")); + break; + case OMP_CTWT: + username = dbe_strdup (GTXT ("OpenMP Critical Section Wait Time")); + specify_ompstate_metric (OMP_CTWT_STATE); + cmd = dbe_strdup (NTXT ("ompctwt")); + break; + case OMP_ODWT: + username = dbe_strdup (GTXT ("OpenMP Ordered Section Wait Time")); + specify_ompstate_metric (OMP_ODWT_STATE); + cmd = dbe_strdup (NTXT ("ompodwt")); + break; + case SYNC_WAIT_TIME: + packet_type = DATA_SYNCH; + username = dbe_strdup (GTXT ("Sync Wait Time")); + snprintf (buf, sizeof (buf), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (NULL, buf); + cmd = dbe_strdup (NTXT ("sync")); + break; + case SYNC_WAIT_COUNT: + packet_type = DATA_SYNCH; + username = dbe_strdup (GTXT ("Sync Wait Count")); + specify_metric (NULL, NTXT ("1")); + cmd = dbe_strdup (NTXT ("syncn")); + break; + case HEAP_ALLOC_CNT: + packet_type = DATA_HEAP; + username = dbe_strdup (GTXT ("Allocations")); + snprintf (buf, sizeof (buf), NTXT ("(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR"), + FREE_TRACE, MUNMAP_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("heapalloccnt")); + break; + case HEAP_ALLOC_BYTES: + packet_type = DATA_HEAP; + username = dbe_strdup (GTXT ("Bytes Allocated")); + snprintf (buf, sizeof (buf), NTXT ("(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR"), + FREE_TRACE, MUNMAP_TRACE); + specify_metric (buf, NTXT ("HSIZE")); + cmd = dbe_strdup (NTXT ("heapallocbytes")); + break; + case HEAP_LEAK_CNT: + packet_type = DATA_HEAP; + username = dbe_strdup (GTXT ("Leaks")); + snprintf (buf, sizeof (buf), "(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR&&HLEAKED", + FREE_TRACE, MUNMAP_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("heapleakcnt")); + break; + case HEAP_LEAK_BYTES: + packet_type = DATA_HEAP; + username = dbe_strdup (GTXT ("Bytes Leaked")); + snprintf (buf, sizeof (buf), NTXT ("(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR"), + FREE_TRACE, MUNMAP_TRACE); + specify_metric (buf, NTXT ("HLEAKED")); + cmd = dbe_strdup (NTXT ("heapleakbytes")); + break; + + case IO_READ_CNT: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Read Count")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)", READ_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("ioreadcnt")); + break; + case IO_WRITE_CNT: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Write Count")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)", WRITE_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("iowritecnt")); + break; + case IO_OTHER_CNT: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Other I/O Count")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)", + OPEN_TRACE, CLOSE_TRACE, OTHERIO_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("ioothercnt")); + break; + case IO_ERROR_CNT: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("I/O Error Count")); + snprintf (buf, sizeof (buf), + "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)", + READ_TRACE_ERROR, WRITE_TRACE_ERROR, OPEN_TRACE_ERROR, + CLOSE_TRACE_ERROR, OTHERIO_TRACE_ERROR); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("ioerrorcnt")); + break; + case IO_READ_BYTES: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Read Bytes")); + snprintf (buf, sizeof (buf), NTXT ("(IOTYPE==%d)&&IONBYTE"), + READ_TRACE); + specify_metric (buf, NTXT ("IONBYTE")); + cmd = dbe_strdup (NTXT ("ioreadbytes")); + break; + case IO_WRITE_BYTES: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Write Bytes")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)&&IONBYTE", WRITE_TRACE); + specify_metric (buf, NTXT ("IONBYTE")); + cmd = dbe_strdup (NTXT ("iowritebytes")); + break; + case IO_READ_TIME: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Read Time")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)&&EVT_TIME", READ_TRACE); + snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (buf, buf2); + cmd = dbe_strdup (NTXT ("ioreadtime")); + break; + case IO_WRITE_TIME: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Write Time")); + snprintf (buf, sizeof (buf), NTXT ("(IOTYPE==%d)&&EVT_TIME"), + WRITE_TRACE); + snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (buf, buf2); + cmd = dbe_strdup (NTXT ("iowritetime")); + break; + case IO_OTHER_TIME: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Other I/O Time")); + snprintf (buf, sizeof (buf), + "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)&&EVT_TIME", + OPEN_TRACE, CLOSE_TRACE, OTHERIO_TRACE); + snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (buf, buf2); + cmd = dbe_strdup (NTXT ("ioothertime")); + break; + case IO_ERROR_TIME: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("I/O Error Time")); + snprintf (buf, sizeof (buf), + "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)&&EVT_TIME", + READ_TRACE_ERROR, WRITE_TRACE_ERROR, OPEN_TRACE_ERROR, + CLOSE_TRACE_ERROR, OTHERIO_TRACE_ERROR); + snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (buf, buf2); + cmd = dbe_strdup (NTXT ("ioerrortime")); + break; + case RACCESS: + packet_type = DATA_RACE; + username = dbe_strdup (GTXT ("Race Accesses")); + specify_metric (NULL, NTXT ("RCNT")); + cmd = dbe_strdup (NTXT ("raccess")); + break; + case DEADLOCKS: + packet_type = DATA_DLCK; + username = dbe_strdup (GTXT ("Deadlocks")); + specify_metric (NULL, NTXT ("1")); + cmd = dbe_strdup (NTXT ("deadlocks")); + break; + case HWCNTR: + packet_type = DATA_HWC; + // username, cmd, and aux set by hwc constructor + if (valtype == VT_DOUBLE) + { + if (hw_ctr->timecvt > 0) // CPU cycles + specify_metric (NULL, NTXT ("((HWCINT*1000000)/FREQ_MHZ)")); + else if (hw_ctr->timecvt < 0) + { // reference clock (frequency is -timecvt MHz) + snprintf (buf, sizeof (buf), NTXT ("((HWCINT*1000000)/%d)"), -hw_ctr->timecvt); + specify_metric (NULL, buf); + } + else // shouldn't happen + specify_metric (NULL, NTXT ("0")); + // resulting unit: seconds * 1e12 + precision = 1000000LL * 1000000LL; // Seconds * 1e12 + } + else + { + specify_metric (NULL, NTXT ("HWCINT")); + precision = 1; + } + break; + case OMP_MSTR: + case OMP_SNGL: + case OMP_ORDD: + case OMP_NONE: + default: + username = dbe_strdup (GTXT ("****")); + fprintf (stderr, "BaseMetric::init Undefined basemetric %s\n", + get_basetype_name ()); + } +} + +#define CASE_S(x) case x: s = (char *) #x; break +char * +BaseMetric::get_basetype_name () +{ + static char buf[128]; + char *s; + switch (type) + { + CASE_S (CP_LMS_SYSTEM); + CASE_S (CP_TOTAL_CPU); + CASE_S (CP_TOTAL); + CASE_S (OMP_MASTER_THREAD); + CASE_S (CP_LMS_USER); + CASE_S (CP_LMS_WAIT_CPU); + CASE_S (CP_LMS_USER_LOCK); + CASE_S (CP_LMS_TFAULT); + CASE_S (CP_LMS_DFAULT); + CASE_S (CP_LMS_TRAP); + CASE_S (CP_LMS_KFAULT); + CASE_S (CP_LMS_SLEEP); + CASE_S (CP_LMS_STOPPED); + CASE_S (OMP_NONE); + CASE_S (OMP_OVHD); + CASE_S (OMP_WORK); + CASE_S (OMP_IBAR); + CASE_S (OMP_EBAR); + CASE_S (OMP_WAIT); + CASE_S (OMP_SERL); + CASE_S (OMP_RDUC); + CASE_S (OMP_LKWT); + CASE_S (OMP_CTWT); + CASE_S (OMP_ODWT); + CASE_S (OMP_MSTR); + CASE_S (OMP_SNGL); + CASE_S (OMP_ORDD); + CASE_S (CP_KERNEL_CPU); + CASE_S (SYNC_WAIT_TIME); + CASE_S (IO_READ_TIME); + CASE_S (IO_WRITE_TIME); + CASE_S (IO_OTHER_TIME); + CASE_S (IO_ERROR_TIME); + CASE_S (HWCNTR); + CASE_S (SYNC_WAIT_COUNT); + CASE_S (HEAP_ALLOC_CNT); + CASE_S (HEAP_LEAK_CNT); + CASE_S (IO_READ_CNT); + CASE_S (IO_WRITE_CNT); + CASE_S (IO_OTHER_CNT); + CASE_S (IO_ERROR_CNT); + CASE_S (RACCESS); + CASE_S (DEADLOCKS); + CASE_S (HEAP_ALLOC_BYTES); + CASE_S (HEAP_LEAK_BYTES); + CASE_S (IO_READ_BYTES); + CASE_S (IO_WRITE_BYTES); + CASE_S (SIZES); + CASE_S (ADDRESS); + CASE_S (ONAME); + CASE_S (DERIVED); + default: + s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, type); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +char * +BaseMetric::dump () +{ + int len = 4; + char *msg = dbe_sprintf (NTXT ("id=%d %s aux='%s' cmd='%s' user_name='%s' expr_spec='%s'\n" + "%*c cond_spec='%s' val_spec='%s'"), + id, get_basetype_name (), STR (aux), STR (cmd), + STR (username), STR (expr_spec), + len, ' ', STR (cond_spec), STR (val_spec)); + return msg; +} + +Histable * +BaseMetric::get_comparable_obj (Histable *obj) +{ + if (obj == NULL || expr == NULL) + return obj; + if (strncmp (expr_spec, NTXT ("EXPGRID=="), 9) == 0) + { + int n = atoi (expr_spec + 9); + Vector<Histable *> *cmpObjs = obj->get_comparable_objs (); + if (cmpObjs && cmpObjs->size () >= n) + return cmpObjs->get (n - 1); + return NULL; + } + return obj; +} + +Definition::Definition (opType _op) +{ + op = _op; + bm = NULL; + arg1 = NULL; + arg2 = NULL; + def = NULL; + dependencies = NULL; + map = NULL; + index = 0; +} + +Definition::~Definition () +{ + delete arg1; + delete arg2; + delete dependencies; + delete[] map; +} + +Vector<BaseMetric *> * +Definition::get_dependencies () +{ + if (dependencies == NULL) + { + if (arg1 && arg1->bm && arg2 && arg2->bm) + { + dependencies = new Vector<BaseMetric *>(2); + arg1->index = dependencies->size (); + dependencies->append (arg1->bm); + arg2->index = dependencies->size (); + dependencies->append (arg2->bm); + map = new long[2]; + } + } + return dependencies; +} + +long * +Definition::get_map () +{ + get_dependencies (); + return map; +} + +Definition * +Definition::add_definition (char *_def) +{ + // parse the definition + char *op_ptr = strchr (_def, '/'); + if (op_ptr == NULL) + { + // it's a primitive metric + BaseMetric *bm = dbeSession->find_base_reg_metric (_def); + if (bm) + { + Definition *p = new Definition (opPrimitive); + p->bm = bm; + return p; + } + return NULL; // BaseMetric is not yet specified + } + Definition *p2 = add_definition (op_ptr + 1); + if (p2 == NULL) + return NULL; + _def = dbe_strdup (_def); + op_ptr = strchr (_def, '/'); + *op_ptr = 0; + Definition *p1 = add_definition (_def); + if (p1) + { + *op_ptr = '/'; + Definition *p = new Definition (opDivide); + p->arg1 = p1; + p->arg2 = p2; + p->def = _def; + return p; + } + free (_def); + delete p1; + delete p2; + return NULL; +} + +double +Definition::eval (long *indexes, TValue *values) +{ + switch (op) + { + case opPrimitive: + return values[indexes[index]].to_double (); + case opDivide: + { + double x2 = arg2->eval (indexes, values); + if (x2 == 0) + return 0.; + double x1 = arg1->eval (indexes, values); + return x1 / x2; + } + default: + fprintf (stderr, GTXT ("unknown expression\n")); + return 0.; + } +} diff --git a/gprofng/src/BaseMetric.h b/gprofng/src/BaseMetric.h new file mode 100644 index 0000000..e056993 --- /dev/null +++ b/gprofng/src/BaseMetric.h @@ -0,0 +1,246 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _BASEMETRIC_H +#define _BASEMETRIC_H + +#include "dbe_structs.h" +#include "hwcentry.h" +#include "Table.h" + +// METRIC_*_PRECISION determine the least threshold value +// for time measured metrics. Any event that counts for less +// than 1sec/METRIC_PRECISION is discarded. +#define METRIC_SIG_PRECISION MICROSEC +#define METRIC_HR_PRECISION MICROSEC + +class Expression; +class Definition; +class Histable; +template <class ITEM> class Vector; + +class BaseMetric +{ +public: + // sync enum changes with AnMetric.java + enum Type + { // Subtype==STATIC metrics: + ONAME = 1, //ONAME must be 1 + SIZES, + ADDRESS, + // Clock Profiling, Derived Metrics: + CP_TOTAL, + CP_TOTAL_CPU, + // Clock profiling, Solaris Microstates (LMS_* defines) + CP_LMS_USER, + CP_LMS_SYSTEM, + CP_LMS_TRAP, + CP_LMS_TFAULT, + CP_LMS_DFAULT, + CP_LMS_KFAULT, + CP_LMS_USER_LOCK, + CP_LMS_SLEEP, + CP_LMS_WAIT_CPU, + CP_LMS_STOPPED, + // Kernel clock profiling + CP_KERNEL_CPU, + // Sync Tracing + SYNC_WAIT_TIME, + SYNC_WAIT_COUNT, + // HWC + HWCNTR, + // Heap Tracing: + HEAP_ALLOC_CNT, + HEAP_ALLOC_BYTES, + HEAP_LEAK_CNT, + HEAP_LEAK_BYTES, + // I/O Tracing: + IO_READ_BYTES, + IO_READ_CNT, + IO_READ_TIME, + IO_WRITE_BYTES, + IO_WRITE_CNT, + IO_WRITE_TIME, + IO_OTHER_CNT, + IO_OTHER_TIME, + IO_ERROR_CNT, + IO_ERROR_TIME, + // MPI Tracing: + MPI_TIME, + MPI_SEND, + MPI_BYTES_SENT, + MPI_RCV, + MPI_BYTES_RCVD, + MPI_OTHER, + // OMP states: + OMP_NONE, + OMP_OVHD, + OMP_WORK, + OMP_IBAR, + OMP_EBAR, + OMP_WAIT, + OMP_SERL, + OMP_RDUC, + OMP_LKWT, + OMP_CTWT, + OMP_ODWT, + OMP_MSTR, + OMP_SNGL, + OMP_ORDD, + OMP_MASTER_THREAD, + // MPI states: + MPI_WORK, + MPI_WAIT, + // Races and Deadlocks + RACCESS, + DEADLOCKS, + // Derived Metrics + DERIVED + }; + + // sync enum changes with AnMetric.java + enum SubType + { + STATIC = 1, // Type==SIZES, ADDRESS, ONAME + EXCLUSIVE = 2, + INCLUSIVE = 4, + ATTRIBUTED = 8, + DATASPACE = 16 // Can be accessed in dataspace views + }; + + BaseMetric (Type t); + BaseMetric (Hwcentry *ctr, const char* _aux, const char* _cmdname, + const char* _username, int v_styles); // depended bm + BaseMetric (Hwcentry *ctr, const char* _aux, const char* _username, + int v_styles, BaseMetric* _depended_bm = NULL); // master bm + BaseMetric (const char *_cmd, const char *_username, Definition *def); // derived metrics + BaseMetric (const BaseMetric& m); + virtual ~BaseMetric (); + + int get_id () { return id; } + Type get_type () { return type; } + Hwcentry *get_hw_ctr () { return hw_ctr; } + char *get_aux () { return aux; } + char *get_username () { return username; } + char *get_cmd () { return cmd; } + int get_flavors () { return flavors; } + int get_clock_unit () { return clock_unit; } + long long get_precision () { return precision; } + ValueTag get_vtype () { return valtype; } + int get_value_styles () { return value_styles; } + bool is_zeroThreshold () { return zeroThreshold; } + ProfData_type get_packet_type () { return packet_type; } + Expression *get_cond () { return cond; } + Expression *get_val () { return val; } + Expression *get_expr () { return expr; } + char *get_expr_spec () { return expr_spec; } + Definition *get_definition () { return definition; }; + BaseMetric *get_dependent_bm () { return dependent_bm; }; + + bool + comparable () + { + return val_spec != NULL || type == DERIVED || type == SIZES || type == ADDRESS; + } + + // setters.. + void set_default_visbits (SubType subtype, int _visbits); + void set_id (int _id) { id = _id; } //TBR, if possible + // For comparison, set which packets to eval: + void set_expr_spec (char *_expr_spec); + void set_cond_spec (char *_cond_spec); + int get_default_visbits (SubType subtype); + char *dump (); + Histable *get_comparable_obj (Histable *obj); + bool is_internal (); // Invisible for users + + char *legend; // for comparison: add'l column text + +private: + BaseMetric *dependent_bm; // for HWCs only: a link to the timecvt metric + Expression *cond; // determines which packets to evaluate + char *cond_spec; // used to generate "cond" + Expression *val; // determines the numeric value for packet + char *val_spec; // used to generate "val" + Expression *expr; // for comparison: an additional expression to determine + // which packets to eval. Should be NULL otherwise. + char *expr_spec; // used to generate "expr" + int id; // unique id (assigned to last_id @ "new") + Type type; // e.g. HWCNTR + char *aux; // for HWCs only: Hwcentry ctr->name + char *cmd; // the .rc metric command, e.g. "total" + char *username; // e.g. "GTXT("Total Wait Time")" + int flavors; // bitmask of SubType capabilities + int value_styles; // bitmask of ValueType capabilities + static const int NSUBTYPES = 2; // STATIC/EXCLUSIVE, INCLUSIVE + int default_visbits[NSUBTYPES]; // ValueType, e.g. VAL_VALUE|VAL_TIMEVAL + ValueTag valtype; // e.g. VT_LLONG + long long precision; // e.g. METRIC_SIG_PRECISION, 1, etc. + Hwcentry *hw_ctr; // HWC definition + ProfData_type packet_type; // e.g. DATA_HWC, or -1 for N/A + bool zeroThreshold; // deadlock stuff + Presentation_clock_unit clock_unit; + + static int last_id; // incremented by 1 w/ every "new". Not MT-safe + Definition *definition; + + void hwc_init (Hwcentry *ctr, const char* _aux, const char* _cmdname, const char* _username, int v_styles); + void init (Type t); + char *get_basetype_name (); + void specify (); + void specify_metric (char *_cond_spec, char *_val_spec); + void set_val_spec (char *_val_spec); + void specify_mstate_metric (int st); + void specify_ompstate_metric (int st); + void specify_prof_metric (char *_cond_spec); +}; + +class Definition +{ +public: + + enum opType + { + opNULL, + opPrimitive, + opDivide + }; + + Definition (opType _op); + ~Definition (); + static Definition *add_definition (char *_def); + Vector<BaseMetric *> *get_dependencies (); + long *get_map (); + double eval (long *indexes, TValue *values); + + opType op; + Definition *arg1; + Definition *arg2; + char *def; + +private: + BaseMetric *bm; + long *map; + Vector<BaseMetric *> *dependencies; + long index; +}; + +#endif /* _BASEMETRIC_H */ + diff --git a/gprofng/src/BaseMetricTreeNode.cc b/gprofng/src/BaseMetricTreeNode.cc new file mode 100644 index 0000000..2d1db99 --- /dev/null +++ b/gprofng/src/BaseMetricTreeNode.cc @@ -0,0 +1,329 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <strings.h> +#include <limits.h> +#include <sys/param.h> + +#include "hwcentry.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Expression.h" +#include "Metric.h" +#include "Table.h" +#include "i18n.h" +#include "debug.h" + +BaseMetricTreeNode::BaseMetricTreeNode () +{ + init_vars (); + build_basic_tree (); +} + +BaseMetricTreeNode::BaseMetricTreeNode (BaseMetric *item) +{ + init_vars (); + bm = item; + name = dbe_strdup (bm->get_cmd ()); + uname = dbe_strdup (bm->get_username ()); + unit = NULL; //YXXX populate from base_metric (requires updating base_metric) + unit_uname = NULL; +} + +BaseMetricTreeNode::BaseMetricTreeNode (const char *_name, const char *_uname, + const char *_unit, const char *_unit_uname) +{ + init_vars (); + name = dbe_strdup (_name); + uname = dbe_strdup (_uname); + unit = dbe_strdup (_unit); + unit_uname = dbe_strdup (_unit_uname); +} + +void +BaseMetricTreeNode::init_vars () +{ + name = NULL; + uname = NULL; + unit = NULL; + unit_uname = NULL; + root = this; + parent = NULL; + children = new Vector<BaseMetricTreeNode*>; + isCompositeMetric = false; + bm = NULL; + registered = false; + num_registered_descendents = 0; +} + +BaseMetricTreeNode::~BaseMetricTreeNode () +{ + children->destroy (); + delete children; + free (name); + free (uname); + free (unit); + free (unit_uname); +} + +BaseMetricTreeNode * +BaseMetricTreeNode::register_metric (BaseMetric *item) +{ + BaseMetricTreeNode *found = root->find (item->get_cmd ()); + if (!found) + { + switch (item->get_type ()) + { + case BaseMetric::CP_TOTAL: + found = root->find (L_CP_TOTAL); + break; + case BaseMetric::CP_TOTAL_CPU: + found = root->find (L_CP_TOTAL_CPU); + break; + } + if (found && found->bm == NULL) + found->bm = item; + } + if (!found) + { + switch (item->get_type ()) + { + case BaseMetric::HEAP_ALLOC_BYTES: + case BaseMetric::HEAP_ALLOC_CNT: + case BaseMetric::HEAP_LEAK_BYTES: + case BaseMetric::HEAP_LEAK_CNT: + found = root->find (get_prof_data_type_name (DATA_HEAP)); + break; + case BaseMetric::CP_KERNEL_CPU: + case BaseMetric::CP_TOTAL: + found = root->find (get_prof_data_type_name (DATA_CLOCK)); + break; + case BaseMetric::CP_LMS_DFAULT: + case BaseMetric::CP_LMS_TFAULT: + case BaseMetric::CP_LMS_KFAULT: + case BaseMetric::CP_LMS_STOPPED: + case BaseMetric::CP_LMS_WAIT_CPU: + case BaseMetric::CP_LMS_SLEEP: + case BaseMetric::CP_LMS_USER_LOCK: + case BaseMetric::CP_TOTAL_CPU: + found = root->find (L_CP_TOTAL); + break; + case BaseMetric::CP_LMS_USER: + case BaseMetric::CP_LMS_SYSTEM: + case BaseMetric::CP_LMS_TRAP: + found = root->find (L_CP_TOTAL_CPU); + break; + case BaseMetric::HWCNTR: + found = root->find ((item->get_flavors () & BaseMetric::DATASPACE) != 0 ? + L2_HWC_DSPACE : L2_HWC_GENERAL); + break; + case BaseMetric::SYNC_WAIT_TIME: + case BaseMetric::SYNC_WAIT_COUNT: + found = root->find (get_prof_data_type_name (DATA_SYNCH)); + break; + case BaseMetric::OMP_WORK: + case BaseMetric::OMP_WAIT: + case BaseMetric::OMP_OVHD: + found = root->find (get_prof_data_type_name (DATA_OMP)); + break; + case BaseMetric::IO_READ_TIME: + case BaseMetric::IO_READ_BYTES: + case BaseMetric::IO_READ_CNT: + case BaseMetric::IO_WRITE_TIME: + case BaseMetric::IO_WRITE_BYTES: + case BaseMetric::IO_WRITE_CNT: + case BaseMetric::IO_OTHER_TIME: + case BaseMetric::IO_OTHER_CNT: + case BaseMetric::IO_ERROR_TIME: + case BaseMetric::IO_ERROR_CNT: + found = root->find (get_prof_data_type_name (DATA_IOTRACE)); + break; + case BaseMetric::ONAME: + case BaseMetric::SIZES: + case BaseMetric::ADDRESS: + found = root->find (L1_STATIC); + break; + default: + found = root->find (L1_OTHER); + break; + } + assert (found != NULL); + switch (item->get_type ()) + { + case BaseMetric::CP_TOTAL: + case BaseMetric::CP_TOTAL_CPU: + found->isCompositeMetric = true; + break; + } + found = found->add_child (item); + } + register_node (found); + return found; +} + +void +BaseMetricTreeNode::register_node (BaseMetricTreeNode *node) +{ + if (!node->registered) + { + node->registered = true; + BaseMetricTreeNode *tmp = node->parent; + while (tmp) + { + tmp->num_registered_descendents++; + tmp = tmp->parent; + } + } +} + +BaseMetricTreeNode * +BaseMetricTreeNode::find (const char *_name) +{ + BaseMetricTreeNode *found = NULL; + if (dbe_strcmp (get_name (), _name) == 0) + return this; + if (bm && dbe_strcmp (bm->get_cmd (), _name) == 0) + return this; + BaseMetricTreeNode *child; + int index; + + Vec_loop (BaseMetricTreeNode*, children, index, child) + { + found = child->find (_name); + if (found) + return found; + } + return NULL; +} + +static void +int_get_registered_descendents (BaseMetricTreeNode* curr, + Vector<BaseMetricTreeNode*> *dest, bool nearest_only) +{ + if (!curr) + return; + if (curr->is_registered ()) + { + dest->append (curr); + if (nearest_only) + return; // soon as we hit a live node, stop following branch + } + int index; + BaseMetricTreeNode *child; + + Vec_loop (BaseMetricTreeNode*, curr->get_children (), index, child) + { + int_get_registered_descendents (child, dest, nearest_only); + } +} + +void +BaseMetricTreeNode::get_nearest_registered_descendents (Vector<BaseMetricTreeNode*> *dest) +{ + if (!dest || dest->size () != 0) + abort (); + bool nearest_only = true; + int_get_registered_descendents (this, dest, nearest_only); +} + +void +BaseMetricTreeNode::get_all_registered_descendents (Vector<BaseMetricTreeNode*> *dest) +{ + if (!dest || dest->size () != 0) + abort (); + bool nearest_only = false; + int_get_registered_descendents (this, dest, nearest_only); +} + +char * +BaseMetricTreeNode::get_description () +{ + if (bm) + { + Hwcentry* hw_ctr = bm->get_hw_ctr (); + if (hw_ctr) + return hw_ctr->short_desc; + } + return NULL; +} + +void +BaseMetricTreeNode::build_basic_tree () +{ +#define TREE_INSERT_DATA_TYPE(t) add_child(get_prof_data_type_name (t), get_prof_data_type_uname (t)) + BaseMetricTreeNode *level1, *level2; + // register L1_DURATION here because it has a value but is not a true metric + register_node (add_child (L1_DURATION, L1_DURATION_UNAME, UNIT_SECONDS, + UNIT_SECONDS_UNAME)); + register_node (add_child (L1_GCDURATION, L1_GCDURATION_UNAME, UNIT_SECONDS, + UNIT_SECONDS_UNAME)); + TREE_INSERT_DATA_TYPE (DATA_HEAP); + level1 = TREE_INSERT_DATA_TYPE (DATA_CLOCK); + level1 = level1->add_child (L_CP_TOTAL, GTXT ("XXX Total Thread Time")); + level1->isCompositeMetric = true; + level2 = level1->add_child (L_CP_TOTAL_CPU, GTXT ("XXX Total CPU Time")); + level2->isCompositeMetric = true; + + add_child (L1_OTHER, L1_OTHER_UNAME); + level1 = TREE_INSERT_DATA_TYPE (DATA_HWC); + level1->add_child (L2_HWC_DSPACE, L2_HWC_DSPACE_UNAME); + level1->add_child (L2_HWC_GENERAL, L2_HWC_GENERAL_UNAME); + TREE_INSERT_DATA_TYPE (DATA_SYNCH); + TREE_INSERT_DATA_TYPE (DATA_OMP); + TREE_INSERT_DATA_TYPE (DATA_IOTRACE); + add_child (L1_STATIC, L1_STATIC_UNAME); +} + +BaseMetricTreeNode * +BaseMetricTreeNode::add_child (BaseMetric *item) +{ + return add_child (new BaseMetricTreeNode (item)); +} + +BaseMetricTreeNode * +BaseMetricTreeNode::add_child (const char * _name, const char *_uname, + const char * _unit, const char * _unit_uname) +{ + return add_child (new BaseMetricTreeNode (_name, _uname, _unit, _unit_uname)); +} + +BaseMetricTreeNode * +BaseMetricTreeNode::add_child (BaseMetricTreeNode *new_node) +{ + new_node->parent = this; + new_node->root = root; + children->append (new_node); + return new_node; +} + +char * +BaseMetricTreeNode::dump () +{ + int len = 4; + char *s = bm ? bm->dump () : dbe_strdup ("<no base metric>"); + char *msg = dbe_sprintf ("%s\n%*c %*c unit='%s' unit_uname='%s' uname='%s' name='%s'\n", + STR (s), len, ' ', len, ' ', + STR (get_unit_uname ()), STR (get_unit ()), + STR (get_user_name ()), STR (get_name ())); + free (s); + return msg; +} diff --git a/gprofng/src/BaseMetricTreeNode.h b/gprofng/src/BaseMetricTreeNode.h new file mode 100644 index 0000000..0076dff --- /dev/null +++ b/gprofng/src/BaseMetricTreeNode.h @@ -0,0 +1,100 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _BASEMETRICTREENODE_H +#define _BASEMETRICTREENODE_H + +#include "BaseMetric.h" + +// Unit values +#define UNIT_SECONDS "SECONDS" +#define UNIT_SECONDS_UNAME GTXT("secs.") +#define UNIT_BYTES "BYTES" +#define UNIT_BYTES_UNAME GTXT("bytes") + +// Name values for intermediate parent nodes that aren't defined elsewhere +#define L1_DURATION "PROFDATA_TYPE_DURATION" +#define L1_DURATION_UNAME GTXT("Experiment Duration") +#define L1_GCDURATION "PROFDATA_TYPE_GCDURATION" +#define L1_GCDURATION_UNAME GTXT("Java Garbage Collection Duration") +#define L2_HWC_DSPACE "PROFDATA_TYPE_HWC_DSPACE" +#define L2_HWC_DSPACE_UNAME GTXT("Memoryspace Hardware Counters") +#define L2_HWC_GENERAL "PROFDATA_TYPE_HWC_GENERAL" +#define L2_HWC_GENERAL_UNAME GTXT("General Hardware Counters") +#define L1_MPI_STATES "PROFDATA_TYPE_MPI_STATES" +#define L1_MPI_STATES_UNAME GTXT("MPI States") +#define L1_OTHER "PROFDATA_TYPE_OTHER" +#define L1_OTHER_UNAME GTXT("Derived and Other Metrics") +#define L1_STATIC "PROFDATA_TYPE_STATIC" +#define L1_STATIC_UNAME GTXT("Static") +#define L_CP_TOTAL "L_CP_TOTAL" +#define L_CP_TOTAL_CPU "L_CP_TOTAL_CPU" + +class BaseMetricTreeNode +{ +public: + BaseMetricTreeNode (); // builds basic metric tree (not including HWCs) + virtual ~BaseMetricTreeNode (); + BaseMetricTreeNode *register_metric (BaseMetric *item); + BaseMetricTreeNode *find (const char *name); + void get_nearest_registered_descendents (Vector<BaseMetricTreeNode*> *new_vec); + void get_all_registered_descendents (Vector<BaseMetricTreeNode*> *new_vec); + char *get_description(); + char *dump(); + + BaseMetricTreeNode *get_root () { return root; } + BaseMetricTreeNode *get_parent () { return parent; } + Vector<BaseMetricTreeNode*> *get_children () { return children; } + bool is_registered () { return registered; } + int get_num_registered_descendents () { return num_registered_descendents; } + bool is_composite_metric () { return isCompositeMetric; } + BaseMetric *get_BaseMetric () { return bm; } + char *get_name () { return name; } + char *get_user_name () { return uname; } + char *get_unit () { return unit; } + char *get_unit_uname () { return unit_uname; } + +private: + BaseMetricTreeNode (BaseMetric *item); + BaseMetricTreeNode (const char *name, const char *uname, + const char *_unit, const char *_unit_uname); + void init_vars (); + void build_basic_tree (); + BaseMetricTreeNode *add_child (BaseMetric *item); + BaseMetricTreeNode *add_child (const char *name, const char *uname, + const char *unit = NULL, const char *unit_uname = NULL); + BaseMetricTreeNode *add_child (BaseMetricTreeNode *new_node); + void register_node (BaseMetricTreeNode *); + + BaseMetricTreeNode *root; // root of tree + BaseMetricTreeNode *parent; // my parent + bool aggregation; // value is based on children's values + char *name; // bm->get_cmd() for metrics, unique string otherwise + char *uname; // user-visible text + char *unit; // see UNIT_* defines + char *unit_uname; // see UNIT_*_UNAME defines + Vector<BaseMetricTreeNode*> *children; // my children + bool isCompositeMetric; // value is sum of children + BaseMetric *bm; // metric for this node, or null + bool registered; // metric has been officially registered + int num_registered_descendents; // does not include self +}; + +#endif /* _BASEMETRICTREENODE_H */ diff --git a/gprofng/src/CacheMap.h b/gprofng/src/CacheMap.h new file mode 100644 index 0000000..a575749 --- /dev/null +++ b/gprofng/src/CacheMap.h @@ -0,0 +1,186 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Cache Map implementation. + * + * Cache Map makes the following assumptions: + * - Cache Map is used for very fast but not guaranteed mapping; + * - only REL_EQ Relation can be used; + * - all objects used as keys or values has to be managed + * outside CacheMap (f.e. deletion); + * - (Key_t)0 is invalid key; + * - (Value_t)0 is invalid value; + * - <TBC> + */ + +#ifndef _DBE_CACHEMAP_H +#define _DBE_CACHEMAP_H + +#include <assert.h> +#include <vec.h> +#include <Map.h> + +template <typename Key_t, typename Value_t> +class CacheMap : public Map<Key_t, Value_t> +{ +public: + + CacheMap (); + ~CacheMap (); + void put (Key_t key, Value_t val); + Value_t get (Key_t key); + Value_t get (Key_t key, typename Map<Key_t, Value_t>::Relation rel); + Value_t + remove (Key_t key); + +private: + + struct Entry + { + Key_t key; + Value_t val; + + Entry () + { + key = (Key_t) 0; + } + }; + + static const int INIT_SIZE; + static const int MAX_SIZE; + + static unsigned hash (Key_t key); + Entry *getEntry (Key_t key); + + int cursize; + int nputs; + int nchunks; + Entry **chunks; +}; + +template <typename Key_t, typename Value_t> +const int CacheMap<Key_t, Value_t>::INIT_SIZE = 1 << 14; +template <typename Key_t, typename Value_t> +const int CacheMap<Key_t, Value_t>::MAX_SIZE = 1 << 20; + +template <typename Key_t, typename Value_t>CacheMap<Key_t, Value_t> +::CacheMap () +{ + cursize = INIT_SIZE; + chunks = new Entry*[32]; + nchunks = 0; + chunks[nchunks++] = new Entry[cursize]; + nputs = 0; +} + +template <typename Key_t, typename Value_t> +CacheMap<Key_t, Value_t>::~CacheMap () +{ + for (int i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; +} + +template <typename Key_t, typename Value_t> +unsigned +CacheMap<Key_t, Value_t>::hash (Key_t key) +{ + unsigned h = (unsigned) key ^ (unsigned) (key >> 32); + h ^= (h >> 20) ^ (h >> 12); + return h ^ (h >> 7) ^ (h >> 4); +} + +template <typename Key_t, typename Value_t> +void +CacheMap<Key_t, Value_t>::put (Key_t key, Value_t val) +{ + if (nputs >= cursize && cursize < MAX_SIZE) + { + // Allocate new chunk for entries. + chunks[nchunks++] = new Entry[cursize]; + cursize *= 2; + + // Copy all old entries to the newly allocated chunk + Entry *newchunk = chunks[nchunks - 1]; + int prevsz = 0; + int nextsz = INIT_SIZE; + for (int i = 0; i < nchunks - 1; i++) + { + Entry *oldchunk = chunks[i]; + for (int j = prevsz; j < nextsz; j++) + newchunk[j] = oldchunk[j - prevsz]; + prevsz = nextsz; + nextsz *= 2; + } + } + Entry *entry = getEntry (key); + entry->key = key; + entry->val = val; + nputs++; +} + +template <typename Key_t, typename Value_t> +typename CacheMap<Key_t, Value_t>::Entry * +CacheMap<Key_t, Value_t>::getEntry (Key_t key) +{ + unsigned idx = hash (key); + int i = nchunks - 1; + int j = cursize / 2; + for (; i > 0; i -= 1, j /= 2) + if (idx & j) + break; + if (i == 0) + j *= 2; + return &chunks[i][idx & (j - 1)]; +} + +template <typename Key_t, typename Value_t> +Value_t +CacheMap<Key_t, Value_t>::get (Key_t key) +{ + Entry *entry = getEntry (key); + return entry->key == key ? entry->val : (Value_t) 0; +} + +template <typename Key_t, typename Value_t> +Value_t +CacheMap<Key_t, Value_t>::get (Key_t key, typename Map<Key_t, Value_t>::Relation rel) +{ + if (rel != Map<Key_t, Value_t>::REL_EQ) + return (Value_t) 0; + return get (key); +} + +template <typename Key_t, typename Value_t> +Value_t +CacheMap<Key_t, Value_t>::remove (Key_t key) +{ + Entry *entry = getEntry (key); + Value_t res = (Value_t) 0; + if (entry->key == key) + { + res = entry->val; + entry->val = (Value_t) 0; + } + return res; +} + +#endif diff --git a/gprofng/src/CallStack.cc b/gprofng/src/CallStack.cc new file mode 100644 index 0000000..7671f9f --- /dev/null +++ b/gprofng/src/CallStack.cc @@ -0,0 +1,1250 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <new> + +#include "util.h" +#include "CacheMap.h" +#include "CallStack.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "DbeLinkList.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "Function.h" +#include "LoadObject.h" +#include "Module.h" + +Descendants::Descendants () +{ + count = 0; + limit = sizeof (first_data) / sizeof (CallStackNode *); + data = first_data; +} + +Descendants::~Descendants () +{ + if (data != first_data) + free (data); +} + +CallStackNode * +Descendants::find (Histable *hi, int *index) +{ + int cnt = count; + int left = 0; + for (int right = cnt - 1; left <= right;) + { + int ind = (left + right) / 2; + CallStackNode *node = data[ind]; + Histable *instr = node->get_instr (); + if (instr == hi) + { + if (index) + *index = ind; + return node; + } + if (instr->id < hi->id) + right = ind - 1; + else + left = ind + 1; + } + if (index) + *index = left; + return NULL; +} + +void +Descendants::append (CallStackNode* item) +{ + if (count < limit) + data[count++] = item; + else + insert (count, item); +} + +void +Descendants::insert (int ind, CallStackNode* item) +{ + CallStackNode **old_data = data; + int old_cnt = count; + if (old_cnt + 1 >= limit) + { + int new_limit = (limit == 0) ? DELTA : limit * 2; + CallStackNode **new_data = (CallStackNode **) malloc (new_limit * sizeof (CallStackNode *)); + for (int i = 0; i < ind; i++) + new_data[i] = old_data[i]; + new_data[ind] = item; + for (int i = ind; i < old_cnt; i++) + new_data[i + 1] = old_data[i]; + limit = new_limit; + data = new_data; + if (old_data != first_data) + free (old_data); + } + else + { + for (int i = ind; i < old_cnt; i++) + old_data[i + 1] = old_data[i]; + old_data[ind] = item; + } + count++; +} + +/* + * Private implementation of CallStack interface + */ + +// When performing pipeline optimization on resolve_frame_info + add_stack +// cstk_ctx structure contains the state (or context) for one iteration to pass on +// from Phase 2 to Phase 3 (More details in Experiment.cc) +class CallStackP : public CallStack +{ +public: + CallStackP (Experiment *exp); + + virtual ~CallStackP (); + + virtual void add_stack (DataDescriptor *dDscr, long idx, FramePacket *frp, cstk_ctx_chunk *cstCtxChunk); + virtual void *add_stack (Vector<Histable*> *objs); + virtual CallStackNode *get_node (int n); + virtual void print (FILE *); + +private: + + static const int CHUNKSZ = 16384; + + Experiment *experiment; + CallStackNode *root; + CallStackNode *jvm_node; + int nodes; + int nchunks; + CallStackNode **chunks; + Map<uint64_t, CallStackNode *> *cstackMap; + DbeLock *cstackLock; + + CallStackNode *add_stack (long start, long end, Vector<Histable*> *objs, CallStackNode *myRoot); + CallStackNode *new_Node (CallStackNode*, Histable*); + CallStackNode *find_preg_stack (uint64_t); + // objs are in the root..leaf order + void *add_stack_d (Vector<Histable*> *objs); + void add_stack_java (DataDescriptor *dDscr, long idx, FramePacket *frp, hrtime_t tstamp, uint32_t thrid, Vector<DbeInstr*>* natpcs, bool natpc_added, cstk_ctx_chunk *cstCtxChunk); + void add_stack_java_epilogue (DataDescriptor *dDscr, long idx, FramePacket *frp, hrtime_t tstamp, uint32_t thrid, Vector<DbeInstr*>* natpcs, Vector<Histable*>* jpcs, bool natpc_added); + + // Adjust HW counter event to find better trigger PC, etc. + DbeInstr *adjustEvent (DbeInstr *leafPC, DbeInstr * candPC, + Vaddr &eventEA, int abst_type); + Vector<DbeInstr*> *natpcsP; + Vector<Histable*> *jpcsP; +}; + +CallStackP::CallStackP (Experiment *exp) +{ + experiment = exp; + nchunks = 0; + chunks = NULL; + nodes = 0; + cstackMap = new CacheMap<uint64_t, CallStackNode *>; + cstackLock = new DbeLock (); + Function *total = dbeSession->get_Total_Function (); + root = new_Node (0, total->find_dbeinstr (0, 0)); + jvm_node = NULL; + natpcsP = NULL; + jpcsP = NULL; +} + +CallStackP::~CallStackP () +{ + delete cstackLock; + if (chunks) + { + for (int i = 0; i < nodes; i++) + { + CallStackNode *node = get_node (i); + node->~CallStackNode (); + } + for (int i = 0; i < nchunks; i++) + free (chunks[i]); + free (chunks); + } + delete natpcsP; + delete jpcsP; + destroy_map (CallStackNode *, cstackMap); +} + +CallStackNode * +CallStackP::new_Node (CallStackNode *anc, Histable *pcval) +{ + // cstackLock->aquireLock(); // Caller already locked it + if (nodes >= nchunks * CHUNKSZ) + { + CallStackNode **old_chunks = chunks; + nchunks++; + + // Reallocate Node chunk array + chunks = (CallStackNode **) malloc (nchunks * sizeof (CallStackNode *)); + for (int i = 0; i < nchunks - 1; i++) + chunks[i] = old_chunks[i]; + free (old_chunks); + // Allocate new chunk for nodes. + chunks[nchunks - 1] = (CallStackNode *) malloc (CHUNKSZ * sizeof (CallStackNode)); + } + nodes++; + CallStackNode *node = get_node (nodes - 1); + new (node) CallStackNode (anc, pcval); + // cstackLock->releaseLock(); + return node; +} + +CallStackNode * +CallStackP::find_preg_stack (uint64_t prid) +{ + DataView *dview = experiment->getOpenMPdata (); + dview->sort (PROP_CPRID); + Datum tval; + tval.setUINT64 (prid); + long idx = dview->getIdxByVals (&tval, DataView::REL_EQ); + if (idx < 0) + return root; + CallStackNode *node = (CallStackNode*) dview->getObjValue (PROP_USTACK, idx); + if (node != NULL) + return node; + uint64_t pprid = dview->getLongValue (PROP_PPRID, idx); + if (pprid == prid) + return root; + void *nat_stack = dview->getObjValue (PROP_MSTACK, idx); + Vector<Histable*> *pcs = getStackPCs (nat_stack); + + // Find the bottom frame + int btm; + bool inOMP = false; + DbeInstr *instr; + Histable *hist; + for (btm = 0; btm < pcs->size (); btm++) + { + hist = pcs->fetch (btm); + if (hist->get_type () == Histable::INSTR) + instr = (DbeInstr *) hist; + else // DBELINE + instr = (DbeInstr *) hist->convertto (Histable::INSTR); + LoadObject *lo = instr->func->module->loadobject; + if (!inOMP) + { + if (lo->flags & SEG_FLAG_OMP) + inOMP = true; + } + else if (!(lo->flags & SEG_FLAG_OMP)) + break; + } + + // Find the top frame + dview->sort (PROP_CPRID); + int top; + tval.setUINT64 (pprid); + long pidx = dview->getIdxByVals (&tval, DataView::REL_EQ); + if (pidx < 0) // No parent. Process the entire nat_stack + top = pcs->size () - 1; + else + { + uint32_t thrid = (uint32_t) dview->getIntValue (PROP_THRID, idx); + uint32_t pthrid = (uint32_t) dview->getIntValue (PROP_THRID, pidx); + if (thrid != pthrid) + { + // Parent is on a different stack. + // Process the entire nat_stack. Skip libthread. + for (top = pcs->size () - 1; top >= 0; top--) + { + hist = pcs->fetch (top); + if (hist->get_type () == Histable::INSTR) + instr = (DbeInstr *) hist; + else // DBELINE + instr = (DbeInstr *) hist->convertto (Histable::INSTR); + if (instr->func->module->loadobject->flags & SEG_FLAG_OMP) + break; + } + if (top < 0) // None found. May be incomplete call stack (x86) + top = pcs->size () - 1; + } + else + { + // Parent is on the same stack. Find match. + top = pcs->size () - 1; + void *pnat_stack = dview->getObjValue (PROP_MSTACK, pidx); + Vector<Histable*> *ppcs = getStackPCs (pnat_stack); + for (int ptop = ppcs->size () - 1; top >= 0 && ptop >= 0; + top--, ptop--) + { + if (pcs->fetch (top) != ppcs->fetch (ptop)) + break; + } + delete ppcs; + } + } + + // Process the found range + Vector<Histable*> *upcs = new Vector<Histable*>(128); + for (int i = btm; i <= top; ++i) + { + hist = (DbeInstr*) pcs->fetch (i); + if (hist->get_type () == Histable::INSTR) + instr = (DbeInstr *) hist; + else // DBELINE + instr = (DbeInstr *) hist->convertto (Histable::INSTR); + + if (instr->func->module->loadobject->flags & SEG_FLAG_OMP) + // Skip all frames from libmtsk + continue; + upcs->append (instr); + } + delete pcs; + node = find_preg_stack (pprid); + while (node != root) + { + upcs->append (node->instr); + node = node->ancestor; + } + node = (CallStackNode *) add_stack (upcs); + dview->setObjValue (PROP_USTACK, idx, node); + delete upcs; + return node; +} + +#define JNI_MARKER -3 + +// This is one iteration if the third stage of +// resolve_frame_info + add_stack pipeline. Works on building the java +// stacks +void +CallStackP::add_stack_java (DataDescriptor *dDscr, long idx, FramePacket *frp, + hrtime_t tstamp, uint32_t thrid, + Vector<DbeInstr*>* natpcs, bool natpc_added, + cstk_ctx_chunk *cstCtxChunk) +{ + Vector<Histable*> *jpcs = NULL; + cstk_ctx *cstctx = NULL; + if (cstCtxChunk != NULL) + { + cstctx = cstCtxChunk->cstCtxAr[idx % CSTCTX_CHUNK_SZ]; + jpcs = cstctx->jpcs; + jpcs->reset (); + } + if (jpcs == NULL) + { + // this is when we are not doing the pipeline optimization + // Temporary array for resolved addresses + // [leaf_pc .. root_pc] == [0..stack_size-1] + // Leave room for a possible "truncated" frame + if (jpcsP == NULL) + jpcsP = new Vector<Histable*>; + jpcs = jpcsP; + jpcs->reset (); + } + + // + // Construct the user stack + // + // Construct Java user stack + int jstack_size = frp->stackSize (true); + if (jstack_size) + { + // jpcs = new Vector<Histable*>( jstack_size ); + if (frp->isTruncatedStack (true)) + { + Function *truncf = dbeSession->getSpecialFunction (DbeSession::TruncatedStackFunc); + jpcs->append (truncf->find_dbeinstr (0, 0)); + } + + int nind = natpcs->size () - 1; // first native frame + for (int jind = jstack_size - 1; jind >= 0; jind--) + { + bool jleaf = (jind == 0); // is current java frame a leaf? + Vaddr mid = frp->getMthdFromStack (jind); + int bci = frp->getBciFromStack (jind); + DbeInstr *cur_instr = experiment->map_jmid_to_PC (mid, bci, tstamp); + jpcs->append (cur_instr); + if (bci == JNI_MARKER) + { + JMethod *j_method = (JMethod*) cur_instr->func; + // Find matching native function on the native stack + bool found = false; + for (; nind >= 0; nind--) + { + DbeInstr *nat_addr = natpcs->fetch (nind); + if (0 == nat_addr) + continue; + Function *nat_func = nat_addr->func; + if (!found && j_method->jni_match (nat_func)) + found = true; + if (found) + { + // XXX omazur: the following will skip JNI native method + // implemented in JVM itself. + // If we are back in JVM switch to processing Java + // frames if there are any. + if ((nat_func->module->loadobject->flags & SEG_FLAG_JVM) && !jleaf) + break; + jpcs->append (nat_addr); + } + } + } + } + } + add_stack_java_epilogue (dDscr, idx, frp, tstamp, thrid, natpcs, jpcs, natpc_added); +} + +// This is one iteration if the fourth stage of +// resolve_frame_info + add_stack pipeline. +// It adds the native and java stacks to the stackmap + +void +CallStackP::add_stack_java_epilogue (DataDescriptor *dDscr, long idx, FramePacket *frp, hrtime_t tstamp, uint32_t thrid, Vector<DbeInstr*>* natpcs, Vector<Histable*> *jpcs, bool natpc_added) +{ + CallStackNode *node = NULL; + if (!natpc_added) + { + node = (CallStackNode *) add_stack ((Vector<Histable*>*)natpcs); + dDscr->setObjValue (PROP_MSTACK, idx, node); + dDscr->setObjValue (PROP_XSTACK, idx, node); + dDscr->setObjValue (PROP_USTACK, idx, node); + } + + int jstack_size = frp->stackSize (true); + if (jstack_size) + { + if (jpcs != NULL) + node = (CallStackNode *) add_stack_d (jpcs); + if (node == NULL) + node = (CallStackNode*) dDscr->getObjValue (PROP_USTACK, idx); + dDscr->setObjValue (PROP_USTACK, idx, node); + Function *func = (Function*) node->instr->convertto (Histable::FUNCTION); + if (func != dbeSession->get_JUnknown_Function ()) + dDscr->setObjValue (PROP_XSTACK, idx, node); + } + + JThread *jthread = experiment->map_pckt_to_Jthread (thrid, tstamp); + if (jthread == JTHREAD_NONE && jstack_size != 0 && node != NULL) + { + Function *func = (Function*) node->instr->convertto (Histable::FUNCTION); + if (func != dbeSession->get_JUnknown_Function ()) + jthread = JTHREAD_DEFAULT; + } + dDscr->setObjValue (PROP_JTHREAD, idx, jthread); + if (jthread == JTHREAD_NONE || (jthread != JTHREAD_DEFAULT && jthread->is_system ())) + { + if (jvm_node == NULL) + { + Function *jvm = dbeSession->get_jvm_Function (); + if (jvm) + { + jvm_node = new_Node (root, jvm->find_dbeinstr (0, 0)); + CommonPacket::jvm_overhead = jvm_node; + } + } + dDscr->setObjValue (PROP_USTACK, idx, jvm_node); + } +} + +// This is one iteration of the 2nd stage of +// resolve_frame_info + add_stack() pipeline. Builds the stack for a given framepacket. +// When pipeline optimization is turnd off, cstctxchunk passed is NULL +void +CallStackP::add_stack (DataDescriptor *dDscr, long idx, FramePacket *frp, + cstk_ctx_chunk* cstCtxChunk) +{ + Vector<DbeInstr*> *natpcs = NULL; + cstk_ctx *cstctx = NULL; + int stack_size = frp->stackSize (); + if (cstCtxChunk != NULL) + { + cstctx = cstCtxChunk->cstCtxAr[idx % CSTCTX_CHUNK_SZ]; + natpcs = cstctx->natpcs; + natpcs->reset (); + } + if (natpcs == NULL) + { + // this is when we are not doing the pipeline optimization + // Temporary array for resolved addresses + // [leaf_pc .. root_pc] == [0..stack_size-1] + // Leave room for a possible "truncated" frame + if (natpcsP == NULL) + natpcsP = new Vector<DbeInstr*>; + natpcs = natpcsP; + natpcs->reset (); + } + + bool leaf = true; + hrtime_t tstamp = (hrtime_t) dDscr->getLongValue (PROP_TSTAMP, idx); + uint32_t thrid = (uint32_t) dDscr->getIntValue (PROP_THRID, idx); + + enum + { + NONE, + CHECK_O7, + USE_O7, + SKIP_O7 + } state = NONE; + + Vaddr o7_to_skip = 0; + for (int index = 0; index < stack_size; index++) + { + if (frp->isLeafMark (index)) + { + state = CHECK_O7; + continue; + } + + if (state == SKIP_O7) + { + // remember this bad o7 value since OMP might not recognize it + o7_to_skip = frp->getFromStack (index); + state = NONE; + continue; + } + + Vaddr va = frp->getFromStack (index); + DbeInstr *cur_instr = experiment->map_Vaddr_to_PC (va, tstamp); +#if ARCH(Intel)// TBR? FIXUP_XXX_SPARC_LINUX: switch should be on experiment ARCH, not dbe ARCH + // We need to adjust return addresses on intel + // in order to attribute inclusive metrics to + // proper call instructions. + if (experiment->exp_maj_version <= 9) + if (!leaf && cur_instr->addr != 0) + cur_instr = cur_instr->func->find_dbeinstr (0, cur_instr->addr - 1); +#endif + + // Skip PC's from PLT, update leaf and state accordingly + if ((cur_instr->func->flags & FUNC_FLAG_PLT) + && (leaf || state == CHECK_O7)) + { + if (state == CHECK_O7) + state = USE_O7; + leaf = false; + continue; + } + if (state == CHECK_O7) + { + state = USE_O7; + uint64_t saddr = cur_instr->func->save_addr; + if (cur_instr->func->isOutlineFunction) + // outline functions assume 'save' instruction + // Note: they accidentally have saddr == FUNC_ROOT + state = SKIP_O7; + else if (saddr == FUNC_ROOT) + { + // If a function is statically determined as a root + // but dynamically appears not, don't discard o7. + // One such case is __misalign_trap_handler on sparcv9. + if (stack_size == 3) + state = SKIP_O7; + } + else if (saddr != FUNC_NO_SAVE && cur_instr->addr > saddr) + state = SKIP_O7; + } + else if (state == USE_O7) + { + state = NONE; + if (cur_instr->flags & PCInvlFlag) + continue; + } + if (leaf) + { + Vaddr evpc = (Vaddr) dDscr->getLongValue (PROP_VIRTPC, idx); + if (evpc != 0 + && !(index > 0 && frp->isLeafMark (index - 1) + && evpc == (Vaddr) (-1))) + { + /* contains hwcprof info */ + cur_instr->func->module->read_hwcprof_info (); + + // complete ABS validation of candidate eventPC/eventEA + // and correction/adjustment of collected callstack leaf PC + DbeInstr *candPC = experiment->map_Vaddr_to_PC (evpc, tstamp); + Vaddr vaddr = (Vaddr) dDscr->getLongValue (PROP_VADDR, idx); + Vaddr tmp_vaddr = vaddr; + int abst_type; + uint32_t tag = dDscr->getIntValue (PROP_HWCTAG, idx); + if (tag < 0 || tag >= MAX_HWCOUNT) + abst_type = ABST_NOPC; + else + abst_type = experiment->coll_params.hw_tpc[tag]; + + // We need to adjust addresses for ABST_EXACT_PEBS_PLUS1 + // (Nehalem/SandyBridge PEBS identifies PC+1, not PC) + if (abst_type == ABST_EXACT_PEBS_PLUS1 && candPC->addr != 0) + candPC = candPC->func->find_dbeinstr (0, candPC->func->find_previous_addr (candPC->addr)); + + cur_instr = adjustEvent (cur_instr, candPC, tmp_vaddr, abst_type); + if (vaddr != tmp_vaddr) + { + if (tmp_vaddr < ABS_CODE_RANGE) + { + /* post processing backtrack failed */ + dDscr->setValue (PROP_VADDR, idx, tmp_vaddr); + dDscr->setValue (PROP_PADDR, idx, ABS_NULL); + /* hwcp->eventVPC = xxxxx leave eventPC alone, + * or can we set it to leafpc? */ + dDscr->setValue (PROP_PHYSPC, idx, ABS_NULL); + } + else + { + /* internal error: why would post-processing modify vaddr? */ + dDscr->setValue (PROP_PADDR, idx, (Vaddr) (-1)); + dDscr->setValue (PROP_PHYSPC, idx, (Vaddr) (-1)); + } + } + } + } + natpcs->append (cur_instr); + leaf = false; + + // A hack to deceive the user into believing that outlined code + // is called from the base function + DbeInstr *drvd = cur_instr->func->derivedNode; + if (drvd != NULL) + natpcs->append (drvd); + } + if (frp->isTruncatedStack ()) + { + Function *truncf = dbeSession->getSpecialFunction (DbeSession::TruncatedStackFunc); + natpcs->append (truncf->find_dbeinstr (0, 0)); + } + else if (frp->isFailedUnwindStack ()) + { + Function *funwf = dbeSession->getSpecialFunction (DbeSession::FailedUnwindFunc); + natpcs->append (funwf->find_dbeinstr (0, 0)); + } + + CallStackNode *node = (CallStackNode*) add_stack ((Vector<Histable*>*)natpcs); + dDscr->setObjValue (PROP_MSTACK, idx, node); + dDscr->setObjValue (PROP_XSTACK, idx, node); + dDscr->setObjValue (PROP_USTACK, idx, node); + + // OpenMP 3.0 stacks + stack_size = frp->ompstack->size (); + if (stack_size > 0 || frp->omp_state == OMP_IDLE_STATE) + { + Function *func; + Vector<Histable*> *omppcs = new Vector<Histable*>(stack_size); + Vector<Histable*> *ompxpcs = new Vector<Histable*>(stack_size); + switch (frp->omp_state) + { + case OMP_IDLE_STATE: + case OMP_RDUC_STATE: + case OMP_IBAR_STATE: + case OMP_EBAR_STATE: + case OMP_LKWT_STATE: + case OMP_CTWT_STATE: + case OMP_ODWT_STATE: + case OMP_ATWT_STATE: + { + func = dbeSession->get_OMP_Function (frp->omp_state); + DbeInstr *instr = func->find_dbeinstr (0, 0); + omppcs->append (instr); + ompxpcs->append (instr); + break; + } + } + Vector<Vaddr> *stck = frp->ompstack; + leaf = true; + for (int index = 0; index < stack_size; index++) + { + if (stck->fetch (index) == SP_LEAF_CHECK_MARKER) + { + state = CHECK_O7; + continue; + } + if (state == SKIP_O7) + { + state = NONE; + continue; + } + + // The OMP stack might not have enough information to know to discard a bad o7. + // So just remember what the native stack skipped. + if (o7_to_skip == stck->fetch (index)) + { + state = NONE; + continue; + } + Vaddr va = stck->fetch (index); + DbeInstr *cur_instr = experiment->map_Vaddr_to_PC (va, tstamp); + + // Skip PC's from PLT, update leaf and state accordingly + if ((cur_instr->func->flags & FUNC_FLAG_PLT) && + (leaf || state == CHECK_O7)) + { + if (state == CHECK_O7) + state = USE_O7; + leaf = false; + continue; + } + if (state == CHECK_O7) + { + state = USE_O7; + uint64_t saddr = cur_instr->func->save_addr; + if (cur_instr->func->isOutlineFunction) + // outline functions assume 'save' instruction + // Note: they accidentally have saddr == FUNC_ROOT + state = SKIP_O7; + else if (saddr == FUNC_ROOT) + { + // If a function is statically determined as a root + // but dynamically appears not, don't discard o7. + // One such case is __misalign_trap_handler on sparcv9. + if (stack_size == 3) + state = SKIP_O7; + } + else if (saddr != FUNC_NO_SAVE && cur_instr->addr > saddr) + state = SKIP_O7; + } + else if (state == USE_O7) + { + state = NONE; + if (cur_instr->flags & PCInvlFlag) + continue; + } + + DbeLine *dbeline = (DbeLine*) cur_instr->convertto (Histable::LINE); + if (cur_instr->func->usrfunc) + { + dbeline = dbeline->sourceFile->find_dbeline (cur_instr->func->usrfunc, dbeline->lineno); + omppcs->append (dbeline); + } + else if (dbeline->lineno > 0) + omppcs->append (dbeline); + else + omppcs->append (cur_instr); + if (dbeline->is_set (DbeLine::OMPPRAGMA) && + frp->omp_state == OMP_WORK_STATE) + dDscr->setValue (PROP_OMPSTATE, idx, OMP_OVHD_STATE); + ompxpcs->append (cur_instr); + leaf = false; + } + if (frp->omptruncated == SP_TRUNC_STACK_MARKER) + { + func = dbeSession->getSpecialFunction (DbeSession::TruncatedStackFunc); + DbeInstr *instr = func->find_dbeinstr (0, 0); + omppcs->append (instr); + ompxpcs->append (instr); + } + else if (frp->omptruncated == SP_FAILED_UNWIND_MARKER) + { + func = dbeSession->getSpecialFunction (DbeSession::FailedUnwindFunc); + DbeInstr *instr = func->find_dbeinstr (0, 0); + omppcs->append (instr); + ompxpcs->append (instr); + } + + // User model call stack + node = (CallStackNode*) add_stack (omppcs); + dDscr->setObjValue (PROP_USTACK, idx, node); + delete omppcs; + + // Expert call stack + node = (CallStackNode*) add_stack (ompxpcs); + dDscr->setObjValue (PROP_XSTACK, idx, node); + delete ompxpcs; + dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT); + return; + } + + // OpenMP 2.5 stacks + if (frp->omp_cprid || frp->omp_state) + { + DataView *dview = experiment->getOpenMPdata (); + if (dview == NULL) + { + // It appears we may get OMP_SERL_STATE from a passive libmtsk + dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT); + return; + } + if (dview->getDataDescriptor () == dDscr) + { + // Don't process the user stack for OpenMP fork events yet + dDscr->setObjValue (PROP_USTACK, idx, (void*) NULL); + dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT); + return; + } + Vector<Histable*> *omppcs = new Vector<Histable*>(stack_size); + + // Construct OMP user stack + // Find the bottom frame + int btm = 0; + switch (frp->omp_state) + { + case OMP_IDLE_STATE: + { + Function *func = dbeSession->get_OMP_Function (frp->omp_state); + omppcs->append (func->find_dbeinstr (0, 0)); + // XXX: workaround for inconsistency between OMP_IDLE_STATE + // and omp_cprid != 0 + frp->omp_cprid = 0; + btm = natpcs->size (); + break; + } + case OMP_RDUC_STATE: + case OMP_IBAR_STATE: + case OMP_EBAR_STATE: + case OMP_LKWT_STATE: + case OMP_CTWT_STATE: + case OMP_ODWT_STATE: + case OMP_ATWT_STATE: + { + Function *func = dbeSession->get_OMP_Function (frp->omp_state); + omppcs->append (func->find_dbeinstr (0, 0)); + bool inOMP = false; + for (btm = 0; btm < natpcs->size (); btm++) + { + LoadObject *lo = natpcs->fetch (btm)->func->module->loadobject; + if (!inOMP) + { + if (lo->flags & SEG_FLAG_OMP) + inOMP = true; + } + else if (!(lo->flags & SEG_FLAG_OMP)) + break; + } + break; + } + case OMP_NO_STATE: + case OMP_WORK_STATE: + case OMP_SERL_STATE: + default: + break; + } + + // Find the top frame + int top = -1; + switch (frp->omp_state) + { + case OMP_IDLE_STATE: + break; + default: + { + dview->sort (PROP_CPRID); + Datum tval; + tval.setUINT64 (frp->omp_cprid); + long pidx = dview->getIdxByVals (&tval, DataView::REL_EQ); + if (pidx < 0) // No parent. Process the entire nat_stack + top = natpcs->size () - 1; + else + { + uint32_t pthrid = (uint32_t) dview->getIntValue (PROP_THRID, pidx); + if (thrid != pthrid) + { + // Parent is on a different stack. + // Process the entire nat_stack. Skip libthread. + for (top = natpcs->size () - 1; top >= 0; top--) + { + DbeInstr *instr = natpcs->fetch (top); + if (instr->func->module->loadobject->flags & SEG_FLAG_OMP) + break; + } + if (top < 0) // None found. May be incomplete call stack + top = natpcs->size () - 1; + } + else + { + // Parent is on the same stack. Find match. + top = natpcs->size () - 1; + void *pnat_stack = dview->getObjValue (PROP_MSTACK, pidx); + Vector<Histable*> *ppcs = getStackPCs (pnat_stack); + for (int ptop = ppcs->size () - 1; top >= 0 && ptop >= 0; + top--, ptop--) + { + if (natpcs->fetch (top) != ppcs->fetch (ptop)) + break; + } + delete ppcs; + } + } + // If no frames are found for Barrier/Reduction save at least one + if ((frp->omp_state == OMP_RDUC_STATE + || frp->omp_state == OMP_IBAR_STATE + || frp->omp_state == OMP_EBAR_STATE) + && top < btm && btm < natpcs->size ()) + top = btm; + } + } + for (int i = btm; i <= top; ++i) + { + DbeInstr *instr = natpcs->fetch (i); + if (instr->func->module->loadobject->flags & SEG_FLAG_OMP) + continue; // Skip all frames from libmtsk + omppcs->append (instr); + } + node = find_preg_stack (frp->omp_cprid); + while (node != root) + { + omppcs->append (node->instr); + node = node->ancestor; + } + node = (CallStackNode *) add_stack (omppcs); + dDscr->setObjValue (PROP_USTACK, idx, node); + delete omppcs; + dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT); + return; + } + + // Construct Java user stack + add_stack_java (dDscr, idx, frp, tstamp, thrid, natpcs, true, NULL); +} + +// adjustment of leafPC/eventVA for XHWC packets with candidate eventPC +// Called from CallStack during initial processing of the events +DbeInstr * +CallStackP::adjustEvent (DbeInstr *leafPC, DbeInstr *candPC, Vaddr &eventVA, + int abst_type) +{ + // increment counter of dataspace events + experiment->dsevents++; + bool isPrecise; + if (abst_type == ABST_EXACT_PEBS_PLUS1) + isPrecise = true; + else if (abst_type == ABST_EXACT) + isPrecise = true; + else + isPrecise = false; + + if (isPrecise) + /* precise backtracking */ + /* assume within 1 instruction of leaf (this could be checked here) */ + // no change to eventVA or candPC + return candPC; + + Function *func = leafPC->func; + unsigned int bt_entries = func->module->bTargets.size (); + DbeInstr *bestPC = NULL; + + // bt == branch target (potential destination of a branch + if (bt_entries == 0) + { // no XHWCprof info for this module + // increment counter + experiment->dsnoxhwcevents++; + + // see if event is to be processed anyway + if (!dbeSession->check_ignore_no_xhwcprof ()) + { + // Don't ignore error + // XXX -- set error code in event VA -- replace with other mechanism + if (eventVA > ABS_CODE_RANGE) + eventVA = ABS_NULL; + eventVA |= ABS_NO_CTI_INFO; // => effective address can't be validated + bestPC = leafPC; // => no PC correction possible + } + else + bestPC = candPC; // assume the event valid + } + else + { + // we have the info to verify the backtracking + target_info_t *bt; + int bt_entry = bt_entries; + uint64_t leafPC_offset = func->img_offset + leafPC->addr; + uint64_t candPC_offset = candPC->func->img_offset + candPC->addr; + do + { + bt_entry--; + bt = func->module->bTargets.fetch (bt_entry); + /* bts seem to be sorted by offset, smallest to largest */ + } + while (bt_entry > 0 && bt->offset > leafPC_offset); + /* if bt_entry == 0, all items have been checked */ + + if (bt->offset > leafPC_offset) + { /* XXXX isn't is possible that all bt's are after leafPC_offset? */ + bestPC = leafPC; // actual event PC can't be determined + if (eventVA > ABS_CODE_RANGE) + eventVA = ABS_NULL; + eventVA |= ABS_INFO_FAILED; // effective address can't be validated + } + else if (bt->offset > candPC_offset) + { + // use synthetic PC corresponding to bTarget + bestPC = func->find_dbeinstr (PCTrgtFlag, bt->offset - func->img_offset); + if (eventVA > ABS_CODE_RANGE) + eventVA = ABS_NULL; + eventVA |= ABS_CTI_TARGET; // effective address can't be validated + } + else + bestPC = candPC; // accept provided virtual address as valid + } + return bestPC; +} + +void * +CallStackP::add_stack_d (Vector<Histable*> *objs) +{ + // objs: root..leaf + // Reverse objs + for (int i = 0, j = objs->size () - 1; i < j; ++i, --j) + objs->swap (i, j); + return add_stack (objs); +} + +CallStackNode::CallStackNode (CallStackNode *_ancestor, Histable *_instr) +{ + ancestor = _ancestor; + instr = _instr; + alt_node = NULL; +} + +CallStackNode::~CallStackNode () { } + +bool +CallStackNode::compare (long start, long end, Vector<Histable*> *objs, CallStackNode *mRoot) +{ + CallStackNode *p = this; + for (long i = start; i < end; i++, p = p->get_ancestor ()) + if (p == NULL || p->get_instr () != objs->get (i)) + return false; + return p == mRoot; +} + +void +CallStackNode::dump () +{ + const char *s = ""; + int sz = 0; + for (CallStackNode *p = this; p; p = p->get_ancestor ()) + { + fprintf (stderr, NTXT ("%.*s 0x%08llx id=0x%08llx %s\n"), sz, s, + (long long) p, (long long) p->get_instr ()->id, + STR (p->get_instr ()->get_name ())); + s = "-"; + sz += 1; + } +} + +long total_calls_add_stack, total_stacks, total_nodes, call_stack_size[201]; + +void * +CallStackP::add_stack (Vector<Histable*> *objs) +{ + // objs: leaf..root + uint64_t hash = objs->size (); + for (long i = objs->size () - 1; i >= 0; --i) + hash ^= (unsigned long long) objs->get (i); + + uint64_t key = hash ? hash : 1; + CallStackNode *node = cstackMap->get (key); +#ifdef DEBUG + if (DUMP_CALL_STACK) + { + total_calls_add_stack++; + call_stack_size[objs->size () > 200 ? 200 : objs->size ()]++; + Dprintf (DUMP_CALL_STACK, + "add_stack: %lld size=%lld key=0x%08llx cashNode=0x%08llx\n", + (long long) total_calls_add_stack, (long long) objs->size (), + (long long) key, (long long) node); + for (long i = 0, sz = VecSize (objs); i < sz; i++) + Dprintf (DUMP_CALL_STACK, " add_stack: %.*s 0x%08llx id=0x%08llx %s\n", + (int) i, NTXT (" "), (long long) objs->get (i), + (long long) objs->get (i)->id, STR (objs->get (i)->get_name ())); + } +#endif + if (node && node->compare (0, objs->size (), objs, root)) + { + Dprintf (DUMP_CALL_STACK, NTXT ("STACK FOUND: key=0x%08llx 0x%08llx id=0x%08llx %s\n"), + (long long) key, (long long) node, + (long long) node->get_instr ()->id, + STR (node->get_instr ()->get_name ())); + return node; + } + node = root; + for (long i = objs->size () - 1; i >= 0; i--) + { + Histable *instr = objs->get (i); + int old_count = node->count; + int left; + CallStackNode *nd = node->find (instr, &left); + if (nd) + { + node = nd; + continue; + } + cstackLock->aquireLock (); // Use one lock for all nodes + // node->aquireLock(); + if (old_count != node->count) + { + nd = node->find (instr, &left); + if (nd) + { // the other thread has created this node + cstackLock->releaseLock (); + // node->releaseLock(); + node = nd; + continue; + } + } + // New Call Stack + total_stacks++; + nd = node; + CallStackNode *first = NULL; + do + { + CallStackNode *anc = node; + total_nodes++; + node = new_Node (anc, objs->get (i)); + if (first) + anc->append (node); + else + first = node; + } + while (i-- > 0); + nd->insert (left, first); + cstackLock->releaseLock (); + // nd->releaseLock(); + break; + } + cstackMap->put (key, node); + if (DUMP_CALL_STACK) + node->dump (); + return node; +} + +CallStackNode * +CallStackP::get_node (int n) +{ + if (n < nodes) + return &chunks[n / CHUNKSZ][n % CHUNKSZ]; + return NULL; +} + +/* + * Debugging methods + */ +void +CallStackP::print (FILE *fd) +{ + FILE *f = (fd == NULL ? stderr : fd); + fprintf (f, GTXT ("CallStack: nodes = %d\n\n"), nodes); + int maxdepth = 0; + int maxwidth = 0; + const char *t; + char *n; + for (int i = 0; i < nodes; i++) + { + CallStackNode *node = &chunks[i / CHUNKSZ][i % CHUNKSZ]; + Histable *instr = node->instr; + if (instr->get_type () == Histable::LINE) + { + t = "L"; + n = ((DbeLine *) instr)->func->get_name (); + } + else if (instr->get_type () == Histable::INSTR) + { + t = "I"; + n = ((DbeInstr *) instr)->func->get_name (); + } + else + { + t = "O"; + n = instr->get_name (); + } + long long addr = (long long) instr->get_addr (); + fprintf (f, GTXT ("node: 0x%016llx anc: 0x%016llx -- 0x%016llX: %s %s\n"), + (unsigned long long) node, (unsigned long long) node->ancestor, + addr, t, n); + } + fprintf (f, GTXT ("md = %d, mw = %d\n"), maxdepth, maxwidth); +} + +/* + * Static CallStack methods + */ +CallStack * +CallStack::getInstance (Experiment *exp) +{ + return new CallStackP (exp); +} + +int +CallStack::stackSize (void *stack) +{ + CallStackNode *node = (CallStackNode *) stack; + int sz = 0; + for (; node; node = node->ancestor) + sz++; + return sz - 1; // don't count the root node +} + +Histable * +CallStack::getStackPC (void *stack, int n) +{ + CallStackNode *node = (CallStackNode *) stack; + while (n-- && node) + node = node->ancestor; + if (node == NULL) + return dbeSession->get_Unknown_Function ()->find_dbeinstr (PCInvlFlag, 0); + return node->instr; +} + +Vector<Histable*> * +CallStack::getStackPCs (void *stack, bool get_hide_stack) +{ + Vector<Histable*> *res = new Vector<Histable*>; + CallStackNode *node = (CallStackNode *) stack; + if (get_hide_stack && node->alt_node != NULL) + node = node->alt_node; + while (node && node->ancestor) + { // skip the root node + res->append (node->instr); + node = node->ancestor; + } + return res; +} + +int +CallStack::compare (void *stack1, void *stack2) +{ + // Quick comparision + if (stack1 == stack2) + return 0; + + CallStackNode *node1 = (CallStackNode *) stack1; + CallStackNode *node2 = (CallStackNode *) stack2; + while (node1 != NULL && node2 != NULL) + { + //to keep the result const on different platforms + //we use instr->id instead of instr + if (node1->instr->id < node2->instr->id) + return -1; + else if (node1->instr->id > node2->instr->id) + return 1; + node1 = node1->ancestor; + node2 = node2->ancestor; + } + if (node1 == NULL && node2 != NULL) + return -1; + else if (node1 != NULL && node2 == NULL) + return 1; + else + return 0; +} + +// LIBRARY VISIBILITY + +void +CallStack::setHideStack (void *stack, void *hideStack) +{ + CallStackNode *hNode = (CallStackNode *) stack; + hNode->alt_node = (CallStackNode *) hideStack; +} diff --git a/gprofng/src/CallStack.h b/gprofng/src/CallStack.h new file mode 100644 index 0000000..62e0686 --- /dev/null +++ b/gprofng/src/CallStack.h @@ -0,0 +1,114 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _CALLSTACK_H +#define _CALLSTACK_H + +#include <stdio.h> +#include "dbe_structs.h" +#include "Experiment.h" +#include "DbeLock.h" + +class DataDescriptor; +class FramePacket; +class DbeInstr; +class Histable; +template <class ITEM> class Vector; +class CallStackNode; + +class Descendants /* : public DbeLock */ +{ +public: + Descendants (); + ~Descendants (); + CallStackNode *find (Histable *hi, int *index); + void append (CallStackNode *item); + void insert (int ind, CallStackNode *item); + int volatile count; + +private: + + enum + { + DELTA = 8 + }; + + int limit; + CallStackNode **data; + CallStackNode *first_data[4]; +}; + +class CallStackNode : public Descendants +{ +public: + CallStackNode (CallStackNode *_ancestor, Histable *_instr); + ~CallStackNode (); + bool compare (long start, long end, Vector<Histable*> *objs, CallStackNode *mRoot); + void dump (); + + CallStackNode * + get_ancestor () + { + return ancestor; + } + + Histable * + get_instr () + { + return instr; + } + + CallStackNode *alt_node; + Histable *instr; + CallStackNode *ancestor; +}; + +class CallStack +{ +public: + static CallStack *getInstance (Experiment *exp); + virtual ~CallStack () { }; + + virtual void add_stack (DataDescriptor *dDscr, long idx, FramePacket *frp, + cstk_ctx_chunk* cstCtxChunk) = 0; + + // Creates a call stack representation for objs and + // returns an opaque pointer to it + virtual void *add_stack (Vector<Histable*> *objs) = 0; + + // Debugging methods + virtual void print (FILE *) = 0; + + // Call stack inquiries + static int stackSize (void *stack); + static Histable *getStackPC (void *stack, int n); + static Vector<Histable*> *getStackPCs (void *stack, bool get_hide_stack = false); + static void setHideStack (void *stack, void *hideStack); + static int compare (void *stack1, void *stack2); + + virtual CallStackNode * + get_node (int) + { + return NULL; + }; + +}; + +#endif /* _CALLSTACK_H */ diff --git a/gprofng/src/CatchOutOfMemory.cc b/gprofng/src/CatchOutOfMemory.cc new file mode 100644 index 0000000..61b91cf --- /dev/null +++ b/gprofng/src/CatchOutOfMemory.cc @@ -0,0 +1,59 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <new> // std::bad_alloc +#include <stdio.h> // fprintf +#include <stdlib.h> // exit +#include "DbeApplication.h" + +static char *name = NULL; + +/** + * Out Of Memory exception handler + */ +void +out_of_mem () +{ + fprintf (stderr, "%s: %s: %s\n", "Error", name ? name : "", "Out of memory\n"); + exit (2); // Out of memory + // throw bad_alloc(); +} + +/** + * Calls real_main inside try{...}catch(std::bad_alloc *) + */ +int +catch_out_of_memory (int (*real_main)(int, char*[]), int argc, char *argv[]) +{ + int i = 0; + name = argv[0]; + std::set_new_handler (out_of_mem); + try + { + i = real_main (argc, argv); + } + catch (std::bad_alloc */*ba*/) + { + exit (2); // Out of memory + } + delete theDbeApplication; + return i; +} diff --git a/gprofng/src/ClassFile.cc b/gprofng/src/ClassFile.cc new file mode 100644 index 0000000..7dd64a7 --- /dev/null +++ b/gprofng/src/ClassFile.cc @@ -0,0 +1,1639 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "util.h" +#include "DbeSession.h" +#include "ClassFile.h" +#include "Function.h" +#include "StringBuilder.h" +#include "DbeFile.h" + +class ByteCodeInfo +{ +public: + + ByteCodeInfo (JMethod *_func, int _bci, int _lno) + { + func = _func; + bci = _bci; + lno = _lno; + }; + + JMethod *func; + int bci; + int lno; +}; + +typedef unsigned char u1; +typedef unsigned short u2; +typedef unsigned int u4; + +// Class File Constants +#define JAVA_MAGIC 0xcafebabe + +enum { + // First argument in access_flags_to_str() + ClassAccess = 1, + FieldAccess, + MethodAccess, + NestedClassAccess, + + // jdk/src/share/classes/sun/tools/java/RuntimeConstants.java + // Type codes + T_CLASS = 0x00000002, + T_BOOLEAN = 0x00000004, + T_CHAR = 0x00000005, + T_FLOAT = 0x00000006, + T_DOUBLE = 0x00000007, + T_BYTE = 0x00000008, + T_SHORT = 0x00000009, + T_INT = 0x0000000a, + T_LONG = 0x0000000b, + +// Access and modifier flags + ACC_PUBLIC = 0x00000001, + ACC_PRIVATE = 0x00000002, + ACC_PROTECTED = 0x00000004, + ACC_STATIC = 0x00000008, + ACC_FINAL = 0x00000010, + ACC_SYNCHRONIZED = 0x00000020, + ACC_VOLATILE = 0x00000040, + ACC_TRANSIENT = 0x00000080, + ACC_NATIVE = 0x00000100, + ACC_INTERFACE = 0x00000200, + ACC_ABSTRACT = 0x00000400, + ACC_STRICT = 0x00000800, + ACC_SYNTHETIC = 0x00001000, + ACC_ANNOTATION = 0x00002000, + ACC_ENUM = 0x00004000, + + ACC_SUPER = 0x00000020, + ACC_BRIDGE = 0x00000040, + ACC_VARARGS = 0x00000080, + +// Opcodes + opc_try = -3, + opc_dead = -2, + opc_label = -1, + opc_nop = 0, + opc_aconst_null = 1, + opc_iconst_m1 = 2, + opc_iconst_0 = 3, + opc_iconst_1 = 4, + opc_iconst_2 = 5, + opc_iconst_3 = 6, + opc_iconst_4 = 7, + opc_iconst_5 = 8, + opc_lconst_0 = 9, + opc_lconst_1 = 10, + opc_fconst_0 = 11, + opc_fconst_1 = 12, + opc_fconst_2 = 13, + opc_dconst_0 = 14, + opc_dconst_1 = 15, + opc_bipush = 16, + opc_sipush = 17, + opc_ldc = 18, + opc_ldc_w = 19, + opc_ldc2_w = 20, + opc_iload = 21, + opc_lload = 22, + opc_fload = 23, + opc_dload = 24, + opc_aload = 25, + opc_iload_0 = 26, + opc_iload_1 = 27, + opc_iload_2 = 28, + opc_iload_3 = 29, + opc_lload_0 = 30, + opc_lload_1 = 31, + opc_lload_2 = 32, + opc_lload_3 = 33, + opc_fload_0 = 34, + opc_fload_1 = 35, + opc_fload_2 = 36, + opc_fload_3 = 37, + opc_dload_0 = 38, + opc_dload_1 = 39, + opc_dload_2 = 40, + opc_dload_3 = 41, + opc_aload_0 = 42, + opc_aload_1 = 43, + opc_aload_2 = 44, + opc_aload_3 = 45, + opc_iaload = 46, + opc_laload = 47, + opc_faload = 48, + opc_daload = 49, + opc_aaload = 50, + opc_baload = 51, + opc_caload = 52, + opc_saload = 53, + opc_istore = 54, + opc_lstore = 55, + opc_fstore = 56, + opc_dstore = 57, + opc_astore = 58, + opc_istore_0 = 59, + opc_istore_1 = 60, + opc_istore_2 = 61, + opc_istore_3 = 62, + opc_lstore_0 = 63, + opc_lstore_1 = 64, + opc_lstore_2 = 65, + opc_lstore_3 = 66, + opc_fstore_0 = 67, + opc_fstore_1 = 68, + opc_fstore_2 = 69, + opc_fstore_3 = 70, + opc_dstore_0 = 71, + opc_dstore_1 = 72, + opc_dstore_2 = 73, + opc_dstore_3 = 74, + opc_astore_0 = 75, + opc_astore_1 = 76, + opc_astore_2 = 77, + opc_astore_3 = 78, + opc_iastore = 79, + opc_lastore = 80, + opc_fastore = 81, + opc_dastore = 82, + opc_aastore = 83, + opc_bastore = 84, + opc_castore = 85, + opc_sastore = 86, + opc_pop = 87, + opc_pop2 = 88, + opc_dup = 89, + opc_dup_x1 = 90, + opc_dup_x2 = 91, + opc_dup2 = 92, + opc_dup2_x1 = 93, + opc_dup2_x2 = 94, + opc_swap = 95, + opc_iadd = 96, + opc_ladd = 97, + opc_fadd = 98, + opc_dadd = 99, + opc_isub = 100, + opc_lsub = 101, + opc_fsub = 102, + opc_dsub = 103, + opc_imul = 104, + opc_lmul = 105, + opc_fmul = 106, + opc_dmul = 107, + opc_idiv = 108, + opc_ldiv = 109, + opc_fdiv = 110, + opc_ddiv = 111, + opc_irem = 112, + opc_lrem = 113, + opc_frem = 114, + opc_drem = 115, + opc_ineg = 116, + opc_lneg = 117, + opc_fneg = 118, + opc_dneg = 119, + opc_ishl = 120, + opc_lshl = 121, + opc_ishr = 122, + opc_lshr = 123, + opc_iushr = 124, + opc_lushr = 125, + opc_iand = 126, + opc_land = 127, + opc_ior = 128, + opc_lor = 129, + opc_ixor = 130, + opc_lxor = 131, + opc_iinc = 132, + opc_i2l = 133, + opc_i2f = 134, + opc_i2d = 135, + opc_l2i = 136, + opc_l2f = 137, + opc_l2d = 138, + opc_f2i = 139, + opc_f2l = 140, + opc_f2d = 141, + opc_d2i = 142, + opc_d2l = 143, + opc_d2f = 144, + opc_i2b = 145, + opc_i2c = 146, + opc_i2s = 147, + opc_lcmp = 148, + opc_fcmpl = 149, + opc_fcmpg = 150, + opc_dcmpl = 151, + opc_dcmpg = 152, + opc_ifeq = 153, + opc_ifne = 154, + opc_iflt = 155, + opc_ifge = 156, + opc_ifgt = 157, + opc_ifle = 158, + opc_if_icmpeq = 159, + opc_if_icmpne = 160, + opc_if_icmplt = 161, + opc_if_icmpge = 162, + opc_if_icmpgt = 163, + opc_if_icmple = 164, + opc_if_acmpeq = 165, + opc_if_acmpne = 166, + opc_goto = 167, + opc_jsr = 168, + opc_ret = 169, + opc_tableswitch = 170, + opc_lookupswitch = 171, + opc_ireturn = 172, + opc_lreturn = 173, + opc_freturn = 174, + opc_dreturn = 175, + opc_areturn = 176, + opc_return = 177, + opc_getstatic = 178, + opc_putstatic = 179, + opc_getfield = 180, + opc_putfield = 181, + opc_invokevirtual = 182, + opc_invokespecial = 183, + opc_invokestatic = 184, + opc_invokeinterface = 185, + opc_invokedynamic = 186, + opc_new = 187, + opc_newarray = 188, + opc_anewarray = 189, + opc_arraylength = 190, + opc_athrow = 191, + opc_checkcast = 192, + opc_instanceof = 193, + opc_monitorenter = 194, + opc_monitorexit = 195, + opc_wide = 196, + opc_multianewarray = 197, + opc_ifnull = 198, + opc_ifnonnull = 199, + opc_goto_w = 200, + opc_jsr_w = 201, + opc_breakpoint = 202, + +// Constant table + CONSTANT_UTF8 = 1, + CONSTANT_UNICODE = 2, + CONSTANT_INTEGER = 3, + CONSTANT_FLOAT = 4, + CONSTANT_LONG = 5, + CONSTANT_DOUBLE = 6, + CONSTANT_CLASS = 7, + CONSTANT_STRING = 8, + CONSTANT_FIELD = 9, + CONSTANT_METHOD = 10, + CONSTANT_INTERFACEMETHOD = 11, + CONSTANT_NAMEANDTYPE = 12, + CONSTANT_METHODHANDLE = 15, + CONSTANT_METHODTYPE = 16, + CONSTANT_INVOKEDYNAMIC = 18 +}; + +static char *opcNames[] = { + NTXT ("nop"), + NTXT ("aconst_null"), + NTXT ("iconst_m1"), + NTXT ("iconst_0"), + NTXT ("iconst_1"), + NTXT ("iconst_2"), + NTXT ("iconst_3"), + NTXT ("iconst_4"), + NTXT ("iconst_5"), + NTXT ("lconst_0"), + NTXT ("lconst_1"), + NTXT ("fconst_0"), + NTXT ("fconst_1"), + NTXT ("fconst_2"), + NTXT ("dconst_0"), + NTXT ("dconst_1"), + NTXT ("bipush"), + NTXT ("sipush"), + NTXT ("ldc"), + NTXT ("ldc_w"), + NTXT ("ldc2_w"), + NTXT ("iload"), + NTXT ("lload"), + NTXT ("fload"), + NTXT ("dload"), + NTXT ("aload"), + NTXT ("iload_0"), + NTXT ("iload_1"), + NTXT ("iload_2"), + NTXT ("iload_3"), + NTXT ("lload_0"), + NTXT ("lload_1"), + NTXT ("lload_2"), + NTXT ("lload_3"), + NTXT ("fload_0"), + NTXT ("fload_1"), + NTXT ("fload_2"), + NTXT ("fload_3"), + NTXT ("dload_0"), + NTXT ("dload_1"), + NTXT ("dload_2"), + NTXT ("dload_3"), + NTXT ("aload_0"), + NTXT ("aload_1"), + NTXT ("aload_2"), + NTXT ("aload_3"), + NTXT ("iaload"), + NTXT ("laload"), + NTXT ("faload"), + NTXT ("daload"), + NTXT ("aaload"), + NTXT ("baload"), + NTXT ("caload"), + NTXT ("saload"), + NTXT ("istore"), + NTXT ("lstore"), + NTXT ("fstore"), + NTXT ("dstore"), + NTXT ("astore"), + NTXT ("istore_0"), + NTXT ("istore_1"), + NTXT ("istore_2"), + NTXT ("istore_3"), + NTXT ("lstore_0"), + NTXT ("lstore_1"), + NTXT ("lstore_2"), + NTXT ("lstore_3"), + NTXT ("fstore_0"), + NTXT ("fstore_1"), + NTXT ("fstore_2"), + NTXT ("fstore_3"), + NTXT ("dstore_0"), + NTXT ("dstore_1"), + NTXT ("dstore_2"), + NTXT ("dstore_3"), + NTXT ("astore_0"), + NTXT ("astore_1"), + NTXT ("astore_2"), + NTXT ("astore_3"), + NTXT ("iastore"), + NTXT ("lastore"), + NTXT ("fastore"), + NTXT ("dastore"), + NTXT ("aastore"), + NTXT ("bastore"), + NTXT ("castore"), + NTXT ("sastore"), + NTXT ("pop"), + NTXT ("pop2"), + NTXT ("dup"), + NTXT ("dup_x1"), + NTXT ("dup_x2"), + NTXT ("dup2"), + NTXT ("dup2_x1"), + NTXT ("dup2_x2"), + NTXT ("swap"), + NTXT ("iadd"), + NTXT ("ladd"), + NTXT ("fadd"), + NTXT ("dadd"), + NTXT ("isub"), + NTXT ("lsub"), + NTXT ("fsub"), + NTXT ("dsub"), + NTXT ("imul"), + NTXT ("lmul"), + NTXT ("fmul"), + NTXT ("dmul"), + NTXT ("idiv"), + NTXT ("ldiv"), + NTXT ("fdiv"), + NTXT ("ddiv"), + NTXT ("irem"), + NTXT ("lrem"), + NTXT ("frem"), + NTXT ("drem"), + NTXT ("ineg"), + NTXT ("lneg"), + NTXT ("fneg"), + NTXT ("dneg"), + NTXT ("ishl"), + NTXT ("lshl"), + NTXT ("ishr"), + NTXT ("lshr"), + NTXT ("iushr"), + NTXT ("lushr"), + NTXT ("iand"), + NTXT ("land"), + NTXT ("ior"), + NTXT ("lor"), + NTXT ("ixor"), + NTXT ("lxor"), + NTXT ("iinc"), + NTXT ("i2l"), + NTXT ("i2f"), + NTXT ("i2d"), + NTXT ("l2i"), + NTXT ("l2f"), + NTXT ("l2d"), + NTXT ("f2i"), + NTXT ("f2l"), + NTXT ("f2d"), + NTXT ("d2i"), + NTXT ("d2l"), + NTXT ("d2f"), + NTXT ("i2b"), + NTXT ("i2c"), + NTXT ("i2s"), + NTXT ("lcmp"), + NTXT ("fcmpl"), + NTXT ("fcmpg"), + NTXT ("dcmpl"), + NTXT ("dcmpg"), + NTXT ("ifeq"), + NTXT ("ifne"), + NTXT ("iflt"), + NTXT ("ifge"), + NTXT ("ifgt"), + NTXT ("ifle"), + NTXT ("if_icmpeq"), + NTXT ("if_icmpne"), + NTXT ("if_icmplt"), + NTXT ("if_icmpge"), + NTXT ("if_icmpgt"), + NTXT ("if_icmple"), + NTXT ("if_acmpeq"), + NTXT ("if_acmpne"), + NTXT ("goto"), + NTXT ("jsr"), + NTXT ("ret"), + NTXT ("tableswitch"), + NTXT ("lookupswitch"), + NTXT ("ireturn"), + NTXT ("lreturn"), + NTXT ("freturn"), + NTXT ("dreturn"), + NTXT ("areturn"), + NTXT ("return"), + NTXT ("getstatic"), + NTXT ("putstatic"), + NTXT ("getfield"), + NTXT ("putfield"), + NTXT ("invokevirtual"), + NTXT ("invokespecial"), + NTXT ("invokestatic"), + NTXT ("invokeinterface"), + NTXT ("invokedynamic"), + NTXT ("new"), + NTXT ("newarray"), + NTXT ("anewarray"), + NTXT ("arraylength"), + NTXT ("athrow"), + NTXT ("checkcast"), + NTXT ("instanceof"), + NTXT ("monitorenter"), + NTXT ("monitorexit"), + NTXT ("wide"), + NTXT ("multianewarray"), + NTXT ("ifnull"), + NTXT ("ifnonnull"), + NTXT ("goto_w"), + NTXT ("jsr_w"), + NTXT ("breakpoint") +}; + + +#define APPEND_FLAG(len, buf, flag, x) \ + if (((x) & (flag)) != 0) \ + { \ + flag &= ~(x); \ + AppendString(len, buf, NTXT("%s%s"), delimiter, #x); \ + delimiter = NTXT("|"); \ + } + +static char * +access_flags_to_str (int kind, int flag) +{ + static char buf[256]; + size_t len = 0; + buf[0] = 0; + if (flag == 0) + { + AppendString (len, buf, NTXT ("0x%x"), (unsigned int) flag); + return buf; + } + const char *delimiter = ""; + if (kind == ClassAccess) + { + APPEND_FLAG (len, buf, flag, ACC_FINAL); + APPEND_FLAG (len, buf, flag, ACC_SUPER); + APPEND_FLAG (len, buf, flag, ACC_INTERFACE); + APPEND_FLAG (len, buf, flag, ACC_ABSTRACT); + APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); + APPEND_FLAG (len, buf, flag, ACC_ANNOTATION); + APPEND_FLAG (len, buf, flag, ACC_ENUM); + if (flag) + AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); + } + else if (kind == FieldAccess) + { + APPEND_FLAG (len, buf, flag, ACC_PUBLIC); + APPEND_FLAG (len, buf, flag, ACC_PRIVATE); + APPEND_FLAG (len, buf, flag, ACC_PROTECTED); + APPEND_FLAG (len, buf, flag, ACC_STATIC); + APPEND_FLAG (len, buf, flag, ACC_FINAL); + APPEND_FLAG (len, buf, flag, ACC_VOLATILE); + APPEND_FLAG (len, buf, flag, ACC_TRANSIENT); + APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); + APPEND_FLAG (len, buf, flag, ACC_ENUM); + if (flag) + AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); + } + else if (kind == MethodAccess) + { + APPEND_FLAG (len, buf, flag, ACC_PUBLIC); + APPEND_FLAG (len, buf, flag, ACC_PRIVATE); + APPEND_FLAG (len, buf, flag, ACC_PROTECTED); + APPEND_FLAG (len, buf, flag, ACC_STATIC); + APPEND_FLAG (len, buf, flag, ACC_FINAL); + APPEND_FLAG (len, buf, flag, ACC_SYNCHRONIZED); + APPEND_FLAG (len, buf, flag, ACC_BRIDGE); + APPEND_FLAG (len, buf, flag, ACC_VARARGS); + APPEND_FLAG (len, buf, flag, ACC_NATIVE); + APPEND_FLAG (len, buf, flag, ACC_ABSTRACT); + APPEND_FLAG (len, buf, flag, ACC_STRICT); + APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); + if (flag) + AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); + } + else if (kind == NestedClassAccess) + { + APPEND_FLAG (len, buf, flag, ACC_PUBLIC); + APPEND_FLAG (len, buf, flag, ACC_PRIVATE); + APPEND_FLAG (len, buf, flag, ACC_PROTECTED); + APPEND_FLAG (len, buf, flag, ACC_STATIC); + APPEND_FLAG (len, buf, flag, ACC_FINAL); + APPEND_FLAG (len, buf, flag, ACC_INTERFACE); + APPEND_FLAG (len, buf, flag, ACC_ABSTRACT); + APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); + APPEND_FLAG (len, buf, flag, ACC_ANNOTATION); + APPEND_FLAG (len, buf, flag, ACC_ENUM); + if (flag) + AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); + } + return buf; +} + +class DataReadException +{ +public: + + DataReadException (char *s) + { + str_err = s; + } + + ~DataReadException () + { + free (str_err); + } + + char * + toString () + { + return str_err; + } + +private: + char *str_err; +}; + +class DataInputStream +{ +public: + + DataInputStream (const unsigned char *bytes, int64_t sz) + { + bp = bp_orig = bytes; + bp_last = bp_orig + sz; + } + + DataInputStream (DataInputStream *in) + { + bp = bp_orig = in->bp_orig; + bp_last = in->bp_last; + } + + u1 + readByte () + { + check (1); + u1 val = *bp; + bp++; + return val; + } + + u2 + readUnsignedShort () + { + check (2); + u2 val = (bp[0] << 8) | bp[1]; + bp += 2; + return val; + } + + u4 + readUnsigned () + { + check (4); + u4 val = (bp[0] << 24) | (bp[1] << 16) | (bp[2] << 8) | bp[3]; + bp += 4; + return val; + } + + const u1 * + getptr () + { + return bp; + } + + const size_t + get_offset () + { + return bp - bp_orig; + } + + void + skip (int n) + { + check (n); + bp += n; + } + + void + reset () + { + bp = bp_orig; + } + + void + copy_bytes (char *buf, int64_t len) + { + check (len); + memcpy (buf, bp, len); + buf[len] = '\0'; + } + +private: + + void + check (int64_t sz) + { + if (sz < 0 || bp + sz > bp_last) + { + DataReadException *e1 = new DataReadException ( + dbe_sprintf (GTXT ("(Cannot read %lld byte(s) offset=0x%llx)\n"), + (long long) sz, (long long) get_offset ())); + throw (e1); + } + }; + + const unsigned char *bp_last; + const unsigned char *bp_orig; + const unsigned char *bp; +}; + +class BinaryConstantPool +{ +public: + BinaryConstantPool (DataInputStream &in); + ~BinaryConstantPool (); + + u1 + getType (int n) + { + return (n < nconst && n > 0) ? types[n] : 0; + }; + char *getString (int index); + +private: + static char *getTypeName (int ty); + static char *type_name_to_str (int ty); + static char *offset_to_str (long long offset); + int nconst; + u1 *types; + int64_t *offsets; + char **strings; + DataInputStream *input; +}; + +char * +BinaryConstantPool::type_name_to_str (int ty) +{ + static char buf[128]; + char *tyName = getTypeName (ty); + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), tyName, ty); + return buf; +} + +char * +BinaryConstantPool::offset_to_str (long long offset) +{ + static char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("offset=0x%06llx (%llu)"), offset, offset); + return buf; +} + +BinaryConstantPool::BinaryConstantPool (DataInputStream &in) +{ + nconst = 0; + types = NULL; + offsets = NULL; + strings = NULL; + input = new DataInputStream (in); + int cntConst = in.readUnsignedShort (); + if (cntConst > 0) + { + types = new u1[cntConst]; + types[0] = 0; + offsets = new int64_t [cntConst]; + strings = new char * [cntConst]; + strings[0] = NULL; + } + Dprintf (DUMP_JAVA_CLASS, NTXT ("# BinaryConstantPool: %d\n"), (int) nconst); + for (int i = 1; i < cntConst; i++) + { + nconst = i + 1; + strings[i] = NULL; + types[i] = in.readByte (); + offsets[i] = in.get_offset (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" %3d %-25s %-25s"), i, offset_to_str (offsets[i]), type_name_to_str (types[i])); + switch (types[i]) + { + case CONSTANT_UTF8: + { + u2 length = in.readUnsignedShort (); + in.skip (length); + Dprintf (DUMP_JAVA_CLASS, " length=%u\n", (unsigned int) length); + break; + } + case CONSTANT_INTEGER: + { + u4 bytes = in.readUnsigned (); + Dprintf (DUMP_JAVA_CLASS, " bytes=0x%08x\n", (unsigned int) bytes); + break; + } + case CONSTANT_FLOAT: + { + u4 bytes = in.readUnsigned (); + Dprintf (DUMP_JAVA_CLASS, " bytes=0x%08x\n", (unsigned int) bytes); + break; + } + case CONSTANT_LONG: + case CONSTANT_DOUBLE: + { + // JVM 4.4.5: all 8-byte constants take up + // two entries in the constant_pool table. + i++; + nconst++; + offsets[i] = 0; + strings[i] = NULL; + u4 high_bytes = in.readUnsigned (); + u4 low_bytes = in.readUnsigned (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" high_bytes=0x%08x low_bytes=0x%08x\n"), + (unsigned int) high_bytes, (unsigned int) low_bytes); + break; + } + case CONSTANT_CLASS: + { + u2 name_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" name_index=%6u\n"), (unsigned int) name_index); + break; + } + case CONSTANT_STRING: + { + u2 string_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" string_index=%4u\n"), (unsigned int) string_index); + break; + } + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_INTERFACEMETHOD: + { + u2 class_index = in.readUnsignedShort (); + u2 name_and_type_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" class_index=%5u name_and_type_index=%u\n"), + (unsigned int) class_index, (unsigned int) name_and_type_index); + break; + } + case CONSTANT_NAMEANDTYPE: + { + u2 name_index = in.readUnsignedShort (); + u2 descriptor_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, " name_index=%6u descriptor_index=%u\n", + (unsigned int) name_index, (unsigned int) descriptor_index); + break; + } + case CONSTANT_METHODHANDLE: + { + u1 reference_kind = in.readByte (); + u2 reference_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, " reference_kind=%u reference_index=%u\n", + (unsigned int) reference_kind, (unsigned int) reference_index); + break; + } + case CONSTANT_METHODTYPE: + { + u2 descriptor_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" descriptor_index=%u\n"), + (unsigned int) descriptor_index); + break; + } + case CONSTANT_INVOKEDYNAMIC: + { + u2 bootstrap_method_attr_index = in.readUnsignedShort (); + u2 name_and_type_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" bootstrap_method_attr_index=%5u name_and_type_index=%u\n"), + (unsigned int) bootstrap_method_attr_index, + (unsigned int) name_and_type_index); + break; + } + default: + Dprintf (DUMP_JAVA_CLASS, NTXT ("\n")); + DataReadException *e1 = new DataReadException ( + dbe_sprintf (GTXT ("BinaryConstantPool[%d]: bad tag %d %s\n"), + i, types[i], offset_to_str (offsets[i]))); + throw (e1); + } + } +} + +BinaryConstantPool::~BinaryConstantPool () +{ + delete[] types; + delete[] offsets; + delete input; + if (strings) + { + for (int i = 0; i < nconst; i++) + free (strings[i]); + delete[] strings; + } +} + +#define CASE_S(x) case x: return (char *) #x + +char * +BinaryConstantPool::getTypeName (int ty) +{ + switch (ty) + { + CASE_S (CONSTANT_UTF8); + CASE_S (CONSTANT_INTEGER); + CASE_S (CONSTANT_FLOAT); + CASE_S (CONSTANT_LONG); + CASE_S (CONSTANT_DOUBLE); + CASE_S (CONSTANT_CLASS); + CASE_S (CONSTANT_STRING); + CASE_S (CONSTANT_FIELD); + CASE_S (CONSTANT_METHOD); + CASE_S (CONSTANT_INTERFACEMETHOD); + CASE_S (CONSTANT_NAMEANDTYPE); + CASE_S (CONSTANT_METHODHANDLE); + CASE_S (CONSTANT_METHODTYPE); + CASE_S (CONSTANT_INVOKEDYNAMIC); + default: return NTXT ("UNKNOWN_TYPE"); + } +} + +char * +BinaryConstantPool::getString (int index) +{ + if (index >= nconst || index <= 0) + return NULL; + if (strings[index]) + return strings[index]; + input->reset (); + input->skip (offsets[index]); + switch (types[index]) + { + case CONSTANT_CLASS: + case CONSTANT_STRING: + case CONSTANT_NAMEANDTYPE: + strings[index] = dbe_strdup (getString (input->readUnsignedShort ())); + return strings[index]; + case CONSTANT_METHOD: + input->readUnsignedShort (); // cl_inx + strings[index] = dbe_strdup (getString (input->readUnsignedShort ())); + return strings[index]; + case CONSTANT_UTF8: + break; + default: + return NULL; + } + u2 len = input->readUnsignedShort (); + strings[index] = (char *) malloc (len + 1); + input->copy_bytes (strings[index], len); + return strings[index]; +} + +ClassFile::ClassFile () : Module () +{ + input = NULL; + bcpool = NULL; + cf_buf = NULL; + cur_jmthd = NULL; + blanksCnt = 0; + cf_bufsz = 0; + lang_code = Sp_lang_java; + class_name = NULL; + class_filename = NULL; + source_name = NULL; + byteCodeInfo = NULL; +} + +char * +ClassFile::get_opc_name (int op) +{ + if (op >= 0 && ((size_t) op) < sizeof (opcNames) / sizeof (char*)) + return opcNames[op]; + switch (op) + { + case opc_try: + return NTXT ("try"); + case opc_dead: + return NTXT ("dead"); + case opc_label: + return NTXT ("label"); + default: + return NTXT ("Unknown op code"); + } +} + +void +ClassFile::openFile (const char *fname) +{ + if (fname == NULL) + return; + int fd = open64 (fname, O_RDONLY); + if (fd == -1) + { + append_msg (CMSG_ERROR, GTXT ("Cannot open file %s"), fname); + return; + } + struct stat64 stat_buf; + if ((fstat64 (fd, &stat_buf) == -1) || (stat_buf.st_size == 0)) + { + close (fd); + append_msg (CMSG_ERROR, GTXT ("Cannot read file %s"), fname); + return; + } + cf_bufsz = stat_buf.st_size; + cf_buf = (unsigned char *) malloc (cf_bufsz); + if (cf_bufsz != read_from_file (fd, cf_buf, cf_bufsz)) + { + free (cf_buf); + cf_buf = NULL; + close (fd); + append_msg (CMSG_ERROR, GTXT ("Cannot read file %s"), fname); + return; + } + close (fd); + + input = new DataInputStream (cf_buf, cf_bufsz); + u4 c_magic = input->readUnsigned (); + if (c_magic != JAVA_MAGIC) + { + append_msg (CMSG_ERROR, GTXT ("Not a class file: %s"), fname); + return; + } + /* u2 minor = */ input->readUnsignedShort (); + /* u2 major = */ input->readUnsignedShort (); + status = AE_OK; +} + +ClassFile::~ClassFile () +{ + free (cf_buf); + free (class_name); + free (class_filename); + free (source_name); + delete bcpool; + delete input; +} + +static void +convertName (char *s) +{ + while (*s) + { + if (*s == '/') + *s = '.'; + s++; + } +} + +void +ClassFile::printConstant (StringBuilder *sb, int index) +{ + u1 type = bcpool->getType (index); + switch (type) + { + case CONSTANT_METHOD: + { + char *str = bcpool->getString (index); + if (str) + { + convertName (str); + sb->append (str); + sb->append (NTXT ("()")); + } + break; + } + case CONSTANT_CLASS: + { + char *str = bcpool->getString (index); + if (str) + { + convertName (str); + sb->append (str); + } + break; + } + case CONSTANT_UTF8: + { + char *str = bcpool->getString (index); + if (str) + sb->append (str); + break; + } + case CONSTANT_STRING: + { + char *str = bcpool->getString (index); + if (str) + { + sb->append ('"'); + sb->append (str); + sb->append ('"'); + } + break; + } + default: + sb->append ('#'); + sb->append ((int) index); + break; + } +} + +long long +ClassFile::printCodeSequence (StringBuilder *sb, uint64_t addr, DataInputStream *in) +{ + int64_t offset = in->get_offset (); + sb->appendf (NTXT ("%08llx: "), (long long) addr); + int opcode = in->readByte (); + if (opcode == opc_wide) + { + opcode = in->readByte (); + sb->append (get_opc_name (opcode)); + sb->append (NTXT ("_w ")); + int arg = in->readUnsignedShort (); + switch (opcode) + { + case opc_aload: case opc_astore: + case opc_fload: case opc_fstore: + case opc_iload: case opc_istore: + case opc_lload: case opc_lstore: + case opc_dload: case opc_dstore: + case opc_ret: + sb->append (arg); + break; + case opc_iinc: + sb->append (arg); + sb->append (' '); + sb->append (in->readUnsignedShort ()); + break; + default: + sb->append (GTXT ("Invalid opcode")); + break; + } + } + else + { + sb->append (get_opc_name (opcode)); + sb->append (' '); + switch (opcode) + { + case opc_aload: case opc_astore: + case opc_fload: case opc_fstore: + case opc_iload: case opc_istore: + case opc_lload: case opc_lstore: + case opc_dload: case opc_dstore: + case opc_ret: + sb->append (in->readByte ()); + break; + case opc_iinc: + sb->append (in->readByte ()); + sb->append (' '); + sb->append (in->readByte ()); + break; + case opc_tableswitch: + { + int align = (addr + 1) % 4; // 1 byte is a length of opc_lookupswitch + if (align != 0) + { + in->skip (4 - align); // four byte boundry + } + long default_skip = in->readUnsigned (); + long low = in->readUnsigned (); + long high = in->readUnsigned (); + sb->appendf (GTXT ("%ld to %ld: default=0x%llx"), + (long) low, (long) high, (long long) (addr + default_skip)); + for (long i = low; i <= high; ++i) + /* u4 i1 = */ in->readUnsigned (); + break; + } + case opc_lookupswitch: + { + int align = (addr + 1) % 4; // 1 byte is a length of opc_lookupswitch + if (align != 0) + in->skip (4 - align); // four byte boundry + u4 default_skip = in->readUnsigned (); + u4 npairs = in->readUnsigned (); + sb->appendf (GTXT ("%d: default=0x%llx"), npairs, + (long long) (addr + default_skip)); + for (int i = 0, nints = npairs * 2; i < nints; i += 2) + { + /* u4 i1 = */ in->readUnsigned (); + /* u4 i2 = */ in->readUnsigned (); + } + break; + } + case opc_newarray: + switch (in->readByte ()) + { + case T_INT: + sb->append (GTXT ("int")); + break; + case T_LONG: + sb->append (GTXT ("long")); + break; + case T_FLOAT: + sb->append (GTXT ("float")); + break; + case T_DOUBLE: + sb->append (GTXT ("double")); + break; + case T_CHAR: + sb->append (GTXT ("char")); + break; + case T_SHORT: + sb->append (GTXT ("short")); + break; + case T_BYTE: + sb->append (GTXT ("byte")); + break; + case T_BOOLEAN: + sb->append (GTXT ("boolean")); + break; + default: + sb->append (GTXT ("BOGUS TYPE")); + break; + } + break; + case opc_anewarray: + sb->append (GTXT ("class ")); + printConstant (sb, in->readUnsignedShort ()); + break; + case opc_sipush: + sb->append (in->readUnsignedShort ()); + break; + case opc_bipush: + sb->append (in->readByte ()); + break; + case opc_ldc: + printConstant (sb, in->readByte ()); + break; + case opc_ldc_w: case opc_ldc2_w: + case opc_instanceof: case opc_checkcast: + case opc_new: + case opc_putstatic: case opc_getstatic: + case opc_putfield: case opc_getfield: + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + printConstant (sb, in->readUnsignedShort ()); + break; + case opc_invokeinterface: + { + u2 index = in->readUnsignedShort (); + u1 count = in->readByte (); + /* u1 zero = */ in->readByte (); + sb->appendf (" #%u, %u) ", (unsigned int) index, (unsigned int) count); + printConstant (sb, index); + break; + } + case opc_multianewarray: + { + u2 index = in->readUnsignedShort (); + printConstant (sb, index); + sb->appendf (GTXT (" dim #%d "), index); + break; + } + case opc_jsr: case opc_goto: + case opc_ifeq: case opc_ifge: case opc_ifgt: + case opc_ifle: case opc_iflt: case opc_ifne: + case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt: + case opc_if_acmpeq: case opc_if_acmpne: + case opc_ifnull: case opc_ifnonnull: + sb->appendf (NTXT ("0x%llx"), (long long) (addr + (short) in->readUnsignedShort ())); + break; + case opc_jsr_w: + case opc_goto_w: + sb->append (addr + (int) in->readUnsigned ()); + break; + default: + break; + } + } + return in->get_offset () - offset; +} + +void +ClassFile::readAttributes (int count) +{ + blanksCnt += 4; + for (int ax = 0; ax < count; ax++) + { + u2 attribute_name_index = input->readUnsignedShort (); + u4 attribute_length = input->readUnsigned (); + char *attribute_name = bcpool->getString (attribute_name_index); + if (!attribute_name) + { + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length); + input->skip (attribute_length); + continue; + } + + if (strcmp (attribute_name, NTXT ("SourceFile")) == 0) + { + u2 sourcefile_index = input->readUnsignedShort (); + source_name = dbe_strdup (bcpool->getString (sourcefile_index)); + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d file_name=%d %s\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length, + (int) sourcefile_index, STR (source_name)); + } + else if (strcmp (attribute_name, NTXT ("InnerClasses")) == 0) + { + int niclasses = input->readUnsignedShort (); + for (int ix = 0; ix < niclasses; ix++) + { + u2 inner_class_info_index = input->readUnsignedShort (); + u2 outer_class_info_index = input->readUnsignedShort (); + u2 inner_name_index = input->readUnsignedShort (); + u2 inner_class_access_flags = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, + NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d name=%d '%s'\n" + "%*cinner_class_info_index=%d outer_class_info_index=%d flags=%s\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length, + (int) inner_name_index, STR (bcpool->getString (inner_name_index)), + (int) (blanksCnt + 10), ' ', + (int) inner_class_info_index, (int) outer_class_info_index, + access_flags_to_str (NestedClassAccess, inner_class_access_flags)); + } + } + else if (strcmp (attribute_name, NTXT ("Code")) == 0) + { + u2 max_stack = input->readUnsignedShort (); + u2 max_locals = input->readUnsignedShort (); + u4 code_length = input->readUnsigned (); + if (cur_jmthd) + { + cur_jmthd->size = code_length; + cur_jmthd->img_fname = dbeFile->get_location (); + cur_jmthd->img_offset = input->get_offset (); + } + input->skip (code_length); + u2 exception_table_length = input->readUnsignedShort (); + input->skip (exception_table_length * (2 + 2 + 2 + 2)); + Dprintf (DUMP_JAVA_CLASS, + NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d max_stack=%d max_locals=%d code_length=%d exception_table_length=%d\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length, + (int) max_stack, (int) max_locals, (int) code_length, (int) exception_table_length); + readAttributes (input->readUnsignedShort ()); + } + else if (strcmp (attribute_name, NTXT ("LineNumberTable")) == 0) + { + int nlines = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d nlines=%d\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length, + (int) nlines); + for (int lx = 0; lx < nlines; lx++) + { + int bci = input->readUnsignedShort (); + int lno = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %3d: pc=%4d (0x%04x) line=%d\n"), + (int) (blanksCnt + 5), ' ', (int) (lx + 1), (int) bci, (int) bci, (int) lno); + if (cur_jmthd) + byteCodeInfo->append (new ByteCodeInfo (cur_jmthd, bci, lno)); + } + } + else + { + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), + (int) attribute_length); + input->skip (attribute_length); + } + } + blanksCnt -= 4; +} + +int +ClassFile::readFile () +{ + if (status != AE_NOTREAD) + return status; + status = AE_OTHER; + + // The ClassFile Structure http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html + try + { + blanksCnt = 4; + cur_jmthd = NULL; + char *fname = dbeFile->get_location (); + openFile (fname); + Dprintf (DUMP_JAVA_CLASS, NTXT ("\nClassFile::readFile status=%d %s location=%s\n"), + (unsigned int) status, STR (get_name ()), STR (fname)); + if (status != AE_OK) + return status; + byteCodeInfo = new Vector<ByteCodeInfo *>(512); + bcpool = new BinaryConstantPool (*input); + u2 access_flags = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT ("\naccess_flags=%s; %s\n"), + access_flags_to_str (ClassAccess, access_flags), + STR (dbeFile->get_name ())); + u2 classNameInd = input->readUnsignedShort (); + class_filename = dbe_strdup (bcpool->getString (classNameInd)); + if (class_filename) + { + class_name = strdup (class_filename); + convertName (class_name); + } + + // Get superclass name + u2 superClassInd = input->readUnsignedShort (); + //char *str = bcpool->getString(superClassInd); + //super_name = str ? convertName( str ) : NULL; + + // Read interfaces + int interfaces_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, + NTXT (" class_name=%3d %-20s superClass=%3d %s interfaces_count=%d\n"), + (int) classNameInd, STR (class_name), + (int) superClassInd, STR (bcpool->getString (superClassInd)), + (int) interfaces_count); + for (int i = 0; i < interfaces_count; i++) + { + u2 index = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" %6lld%s"), (long long) index, + (i % 8 == 7) || (i + 1 == interfaces_count) ? "\n" : ""); + } + + // Read fields + int fields_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" fields_count=%d\n"), fields_count); + for (int i = 0; i < fields_count; i++) + { + u2 fld_access_flags = input->readUnsignedShort (); + u2 name_index = input->readUnsignedShort (); + u2 descriptor_index = input->readUnsignedShort (); + u2 attributes_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, + NTXT (" %2d: name=%3d %-20s flags=%s; desc_ind=%d attr_count=%d\n"), + i, (int) name_index, STR (bcpool->getString (name_index)), + access_flags_to_str (FieldAccess, fld_access_flags), + (int) descriptor_index, (int) attributes_count); + readAttributes (attributes_count); + } + + // Read methods + int methods_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT ("\n methods_count=%d\n"), (int) methods_count); + int func_cnt = functions->size (); + for (int i = 0; i < methods_count; i++) + { + u2 mthd_access_flags = input->readUnsignedShort (); + u2 name_index = input->readUnsignedShort (); + u2 descriptor_index = input->readUnsignedShort (); + char *mname = bcpool->getString (name_index); + if (mname == NULL) + { + DataReadException *e1 = new DataReadException (dbe_sprintf (GTXT ("method name[%d] is NULL\n"), i)); + throw (e1); + } + char *msign = bcpool->getString (descriptor_index); + if (msign == NULL) + { + DataReadException *e1 = new DataReadException (dbe_sprintf (GTXT ("method signature[%d] is NULL\n"), i)); + throw (e1); + } + size_t len = strlen (class_name); + cur_jmthd = NULL; + for (int idx = 0; idx < func_cnt; idx++) + { + JMethod *jmthd = (JMethod*) functions->fetch (idx); + char *jmt_name = jmthd->get_name (Histable::SHORT); + if (strncmp (jmt_name, class_name, len) == 0) + { + if (strcmp (jmt_name + len + 1, mname) == 0 && + strcmp (jmthd->get_signature (), msign) == 0) + { + cur_jmthd = jmthd; + break; + } + } + } + if (cur_jmthd == NULL) + { + cur_jmthd = dbeSession->createJMethod (); + cur_jmthd->module = this; + cur_jmthd->set_signature (dbe_strdup (msign)); + char *nm = dbe_sprintf (NTXT ("%s.%s"), class_name, mname); + cur_jmthd->set_name (nm); + free (nm); + functions->append (cur_jmthd); + } + if ((mthd_access_flags & ACC_NATIVE) != 0) + { + cur_jmthd->flags |= FUNC_FLAG_NATIVE; + } + u2 attributes_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, + NTXT (" %2d: name=%d %-20s flags=%s desc_ind=%d attr_count=%d\n"), + (int) (i + 1), (int) name_index, STR (bcpool->getString (name_index)), + access_flags_to_str (MethodAccess, mthd_access_flags), + (int) descriptor_index, (int) attributes_count); + readAttributes (attributes_count); + cur_jmthd->popSrcFile (); + } + + // Read global attributes + u2 global_attributes_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" global_attributes_count=%d\n"), global_attributes_count); + readAttributes (global_attributes_count); + status = AE_OK; + } + catch (DataReadException *ex) + { + append_msg (CMSG_ERROR, GTXT ("Cannot read class file %s (%s)"), get_name (), ex->toString ()); + delete ex; + status = AE_OTHER; + } + + char *fnm = NULL; + if (class_filename) + { + if (strcmp (class_filename, get_name ()) != 0) + set_name (strdup (class_filename)); + if (source_name) + { + char *bname = strrchr (class_filename, '/'); + if (bname) + fnm = dbe_sprintf (NTXT ("%.*s/%s"), (int) (bname - class_filename), + class_filename, source_name); + else + fnm = strdup (source_name); + } + else + fnm = get_java_file_name (class_filename, false); + } + else if (source_name) + fnm = strdup (source_name); + if (fnm) + { + set_file_name (fnm); + main_source = findSource (file_name, true); + main_source->dbeFile->filetype |= DbeFile::F_JAVA_SOURCE; + } + + for (long i = 0, sz = VecSize (functions); i < sz; i++) + functions->get (i)->def_source = main_source; + JMethod *func = NULL; + for (long i = 0, sz = VecSize (byteCodeInfo); i < sz; i++) + { + ByteCodeInfo *p = byteCodeInfo->get (i); + if (func != p->func) + { + if (func) + func->popSrcFile (); + func = p->func; + func->line_first = p->lno; + func->pushSrcFile (main_source, 0); + } + func->line_last = p->lno; + func->add_PC_info (p->bci, p->lno, main_source); + } + if (func) + func->popSrcFile (); + Destroy (byteCodeInfo); + Dprintf (DUMP_JAVA_CLASS, NTXT ("\n status=%d class_filename=%s class_name=%s source_name=%s file_name=%s %s\n"), + (unsigned int) status, STR (class_filename), STR (class_name), + STR (source_name), STR (file_name), + STR (get_name ())); + return status; +} + +#define MAX_CLASS_SIZE 65536 + +char * +ClassFile::get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, int64_t &inst_size) +{ + int64_t offset = f_offset + (inst_address - start_address); + if ((cf_buf == NULL) || (inst_address >= end_address) || (offset >= cf_bufsz)) + { + inst_size = 0; + return NULL; + } + + // Check for an implausibly large size + if ((inst_address - start_address) > MAX_CLASS_SIZE) + { + append_msg (CMSG_ERROR, GTXT ("Cannot disassemble class file %s (%s), implausible size = %lld"), + get_name (), dbeFile->get_location (), + (end_address - start_address)); + inst_size = 0; + return NULL; + } + + StringBuilder sb; + DataInputStream *in = new DataInputStream (input); + try + { + in->skip (offset); + inst_size = printCodeSequence (&sb, inst_address - start_address, in); + } + catch (DataReadException *ex) + { + append_msg (CMSG_ERROR, GTXT ("Cannot disassemble class file %s (%s) %s"), + get_name (), dbeFile->get_location (), ex->toString ()); + delete ex; + inst_size = 0; + } + delete in; + if (inst_size == 0) + return NULL; + return sb.toString (); +} + +char * +ClassFile::get_java_file_name (char *clname, bool classSuffix) +{ + size_t len = strlen (clname); + if (len > 6 && streq (clname + len - 6, NTXT (".class"))) + len -= 6; + if (!classSuffix) + { // remove $SubClassName from "ClassName$SubClassName" + char *tmp = strchr (clname, '$'); + if (tmp) + len = tmp - clname; + } + char *clpath = (char *) malloc (len + 10); + for (size_t i = 0; i < len; i++) + clpath[i] = (clname[i] == '.') ? '/' : clname[i]; + snprintf (clpath + len, 10, classSuffix ? NTXT (".class") : NTXT (".java")); + return clpath; +} diff --git a/gprofng/src/ClassFile.h b/gprofng/src/ClassFile.h new file mode 100644 index 0000000..bd4c61b --- /dev/null +++ b/gprofng/src/ClassFile.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _CLASSFILE_H +#define _CLASSFILE_H + +#include "Module.h" + +class DataInputStream; +class BinaryConstantPool; +class JMethod; +class StringBuilder; +class ByteCodeInfo; + +class ClassFile : public Module +{ +public: + ClassFile (); + virtual ~ClassFile (); + virtual int readFile (); + virtual char *get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, + int64_t &inst_size); + static char *get_java_file_name (char *clname, bool classSuffix); + +private: + + void openFile (const char *fname); + char *get_opc_name (int op); + void readAttributes (int count); + void printConstant (StringBuilder *sb, int index); + long long printCodeSequence (StringBuilder *sb, uint64_t addr, DataInputStream *in); + + unsigned char *cf_buf; + int64_t cf_bufsz; + int blanksCnt; + DataInputStream *input; + BinaryConstantPool *bcpool; + JMethod *cur_jmthd; + char *class_name; + char *class_filename; + char *source_name; + Vector<ByteCodeInfo *> *byteCodeInfo; +}; + +#endif diff --git a/gprofng/src/Command.cc b/gprofng/src/Command.cc new file mode 100644 index 0000000..d1620d7 --- /dev/null +++ b/gprofng/src/Command.cc @@ -0,0 +1,562 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <string.h> +#include <stdlib.h> +#include <sys/param.h> + +#include "gp-defs.h" +#include "Command.h" +#include "DbeSession.h" +#include "MemorySpace.h" +#include "i18n.h" +#include "StringBuilder.h" + +const char *Command::DEFAULT_CMD = "default"; // token for default +const char *Command::ALL_CMD = "all"; // token for all +const char *Command::ANY_CMD = "any"; // token for any +const char *Command::NONE_CMD = "none"; // token for none +const char *Command::HWC_CMD = "hwc"; // token for all HWC +const char *Command::BIT_CMD = "bit"; // token for any bit-generated metric +const char *Command::DEFAULT_METRICS = "ei.user:name"; // if no .rc files read +const char *Command::DEFAULT_SORT = "e.user:name"; // if no .rc files read + +static char *fhdr, *cchdr, *lahdr, *iohdr, *sdhdr, *lsthdr, *lohdr; +static char *methdr, *othdr, *mischdr, *deflthdr; +static char *selhdr, *filthdr, *outhdr, *exphdr, *obj_allhdr; +static char *unsuphdr, *indxobjhdr; +static char *helphdr, *rahdr, *ddhdr, *typehdr, *typehdr2; + +// This is the list of commands, which governs the parser scan, as +// well as the help command. +// A line with the tag NO_CMD is skipped in parsing, but is used +// to provide subheadings for the help +// The HELP line must be the last one in the list of commands +// to be shown by "-help"; The HHELP line must be the +// last one to be shown by "-xhelp" +// The LAST_CMD line must be the last one recognized by the parser +// +// The ordering of this list should match the ordering in the man +// page, and the subheader lines should match the subheadings in +// the man page. + +static char *desc[LAST_CMD]; + +static Cmdtable cmd_lst[] = { // list of commands + // User Commands + { NO_CMD, "", NULL, NULL, 0, &fhdr}, + { FUNCS, "functions", NULL, NULL, 0, &desc[FUNCS]}, + { METRICS, "metrics", NULL, "metric_spec", 1, &desc[METRICS]}, + { SORT, "sort", NULL, "metric_spec", 1, &desc[SORT]}, + { FDETAIL, "fsummary", NULL, NULL, 0, &desc[FDETAIL]}, + { FSINGLE, "fsingle", NULL, "function_name #", 2, &desc[FSINGLE]}, + + { NO_CMD, "", NULL, NULL, 0, &cchdr}, + { GPROF, "callers-callees", "gprof", NULL, 0, &desc[GPROF]}, + { CSINGLE, "csingle", NULL, "function_name #", 2, &desc[CSINGLE]}, + { CPREPEND, "cprepend", NULL, "function_name #", 2, &desc[CPREPEND]}, + { CAPPEND, "cappend", NULL, "function_name #", 2, &desc[CAPPEND]}, + { CRMFIRST, "crmfirst", NULL, NULL, 0, &desc[CRMFIRST]}, + { CRMLAST, "crmlast", NULL, NULL, 0, &desc[CRMLAST]}, + { CALLTREE, "calltree", "ctree", NULL, 0, &desc[CALLTREE]}, + + { NO_CMD, "", NULL, NULL, 0, &lahdr}, + { LEAKS, "leaks", NULL, NULL, 0, &desc[LEAKS]}, + { ALLOCS, "allocs", NULL, NULL, 0, &desc[ALLOCS]}, + { HEAP, "heap", NULL, NULL, 0, &desc[HEAP]}, + { HEAPSTAT, "heapstat", NULL, NULL, 0, &desc[HEAPSTAT]}, + + { NO_CMD, "", NULL, NULL, 0, &iohdr}, + { IOACTIVITY, "ioactivity", NULL, NULL, 0, &desc[IOACTIVITY]}, + { IOVFD, "iodetail", NULL, NULL, 0, &desc[IOVFD]}, + { IOCALLSTACK, "iocallstack", NULL, NULL, 0, &desc[IOCALLSTACK]}, + { IOSTAT, "iostat", NULL, NULL, 0, &desc[IOSTAT]}, + + // PC, line, source and dissassembly commands + { NO_CMD, "", NULL, NULL, 0, &sdhdr}, + { HOTPCS, "pcs", NULL, NULL, 0, &desc[HOTPCS]}, + { PDETAIL, "psummary", NULL, NULL, 0, &desc[PDETAIL]}, + { HOTLINES, "lines", NULL, NULL, 0, &desc[HOTLINES]}, + { LDETAIL, "lsummary", NULL, NULL, 0, &desc[LDETAIL]}, + { SOURCE, "source", NULL, "func/file #", 2, &desc[SOURCE]}, + { DISASM, "disasm", NULL, "func/file #", 2, &desc[DISASM]}, + { SCOMPCOM, "scc", NULL, "com_spec", 1, &desc[SCOMPCOM]}, + { STHRESH, "sthresh", NULL, "value", 1, &desc[STHRESH]}, + { DCOMPCOM, "dcc", NULL, "com_spec", 1, &desc[DCOMPCOM]}, + { COMPCOM, "cc", NULL, "com_spec", 1, &desc[COMPCOM]}, + { DTHRESH, "dthresh", NULL, "value", 1, &desc[DTHRESH]}, + { SETPATH, "setpath", NULL, "path_list", 1, &desc[SETPATH]}, + { ADDPATH, "addpath", NULL, "path_list", 1, &desc[ADDPATH]}, + { PATHMAP, "pathmap", NULL, "old_prefix new_prefix", 2, &desc[PATHMAP]}, + { LIBDIRS, "preload_libdirs", NULL, NULL, 1, &desc[PATHMAP]}, + + // Index Object commands + { NO_CMD, "", NULL, NULL, 0, &indxobjhdr}, + { INDXOBJ, "indxobj", NULL, "type", 1, &desc[INDXOBJ]}, + { INDXOBJLIST, "indxobj_list", NULL, NULL, 0, &desc[INDXOBJLIST]}, + { INDXOBJDEF, "indxobj_define", NULL, "type \"index-expr\"", 2, &desc[INDXOBJDEF]}, + + // Deadlock detection commands + { NO_CMD, "", NULL, NULL, 0, &ddhdr}, + { DEADLOCK_EVNTS, "deadlocks", NULL, NULL, 0, &desc[DEADLOCK_EVNTS]}, + { DEADLOCK_SUM, "dsummary", NULL, "{deadlock_id|all}", 1, &desc[DEADLOCK_SUM]}, + + { NO_CMD, "", NULL, NULL, 0, &lsthdr}, + { EXP_LIST, "experiment_list", "exp_list", NULL, 0, &desc[EXP_LIST]}, + { SAMPLE_LIST, "sample_list", NULL, NULL, 0, &desc[SAMPLE_LIST]}, + { LWP_LIST, "lwp_list", NULL, NULL, 0, &desc[LWP_LIST]}, + { THREAD_LIST, "thread_list", NULL, NULL, 0, &desc[THREAD_LIST]}, + { CPU_LIST, "cpu_list", NULL, NULL, 0, &desc[CPU_LIST]}, + + { NO_CMD, "", NULL, NULL, 0, &filthdr}, + { FILTERS, "filters", NULL, "filter-specification", 1, &desc[FILTERS]}, + { DESCRIBE, "describe", NULL, NULL, 0, &desc[DESCRIBE]}, + + { NO_CMD, "", NULL, NULL, 0, &selhdr}, + { SAMPLE_SELECT, "sample_select", NULL, "sample_spec", 1, &desc[SAMPLE_SELECT]}, + { LWP_SELECT, "lwp_select", NULL, "lwp_spec", 1, &desc[LWP_SELECT]}, + { THREAD_SELECT, "thread_select", NULL, "thread_spec", 1, &desc[THREAD_SELECT]}, + { CPU_SELECT, "cpu_select", NULL, "cpu_spec", 1, &desc[CPU_SELECT]}, + + { NO_CMD, "", NULL, NULL, 0, &lohdr}, + { OBJECT_LIST, "object_list", NULL, NULL, 0, &desc[OBJECT_LIST]}, + { OBJECT_SHOW, "object_show", NULL, "obj1,...", 1, &desc[OBJECT_SHOW]}, + { OBJECT_HIDE, "object_hide", NULL, "obj1,...", 1, &desc[OBJECT_HIDE]}, + { OBJECT_API, "object_api", NULL, "obj1,...", 1, &desc[OBJECT_API]}, + { DUMMY_CMD, " ", NULL, NULL, 0, &obj_allhdr}, + { OBJECTS_DEFAULT, "objects_default", NULL, NULL, 1, &desc[OBJECTS_DEFAULT]}, + + { OBJECT_SELECT, "object_select", NULL, "obj1,...", 1, &desc[OBJECT_SELECT]}, + + { NO_CMD, "", NULL, NULL, 0, &methdr}, + { METRIC_LIST, "metric_list", NULL, NULL, 0, &desc[METRIC_LIST]}, + { GMETRIC_LIST, "cmetric_list", "gmetric_list", NULL, 0, &desc[GMETRIC_LIST]}, + { INDX_METRIC_LIST, "indx_metric_list", NULL, NULL, 1, &desc[INDX_METRIC_LIST]}, + + { NO_CMD, "", NULL, NULL, 0, &outhdr}, + { OUTFILE, "outfile", NULL, "filename", 1, &desc[OUTFILE]}, + { APPENDFILE, "appendfile", NULL, "filename", 1, &desc[APPENDFILE]}, + { LIMIT, "limit", NULL, "n", 1, &desc[LIMIT]}, + { NAMEFMT, "name", NULL, "{long|short|mangled}[:{soname|nosoname}]", 1, &desc[NAMEFMT]}, + { VIEWMODE, "viewmode", NULL, "{user|expert|machine}", 1, &desc[VIEWMODE]}, + { COMPARE, "compare", NULL, "{on|off|delta|ratio}", 1, &desc[COMPARE]}, + { PRINTMODE, "printmode", NULL, "string", 1, &desc[PRINTMODE]}, + + { NO_CMD, "", NULL, NULL, 0, &othdr}, + { HEADER, "header", NULL, "exp_id", 1, &desc[HEADER]}, + { OBJECTS, "objects", NULL, NULL, 0, &desc[OBJECTS]}, + { OVERVIEW_NEW, "overview", NULL, NULL, 0, &desc[OVERVIEW_NEW]}, + { SAMPLE_DETAIL, "sample_detail", NULL, "exp_id", 1, &desc[SAMPLE_DETAIL]}, + { STATISTICS, "statistics", NULL, "exp_id", 1, &desc[STATISTICS]}, + + { NO_CMD, "", NULL, NULL, 0, &exphdr}, + { OPEN_EXP, "open_exp", NULL, "experiment", 1, &desc[OPEN_EXP]}, + { ADD_EXP, "add_exp", NULL, "experiment", 1, &desc[ADD_EXP]}, + { DROP_EXP, "drop_exp", NULL, "experiment", 1, &desc[DROP_EXP]}, + + { NO_CMD, "", NULL, NULL, 0, &deflthdr}, + { DMETRICS, "dmetrics", NULL, "metric_spec", 1, &desc[DMETRICS]}, + { DSORT, "dsort", NULL, "metric_spec", 1, &desc[DSORT]}, + { EN_DESC, "en_desc", NULL, "{on|off|=<regex>}", 1, &desc[EN_DESC]}, + + { NO_CMD, "", NULL, NULL, 0, &mischdr}, + { DUMMY_CMD, "<type>", NULL, NULL, 0, &typehdr}, + { DUMMY_CMD, " ", NULL, NULL, 0, &typehdr2}, + + { IFREQ, "ifreq", NULL, NULL, 0, &desc[IFREQ]}, + { PROCSTATS, "procstats", NULL, NULL, 0, &desc[PROCSTATS]}, + { SCRIPT, "script", NULL, "file", 1, &desc[SCRIPT]}, + { VERSION_cmd, "version", NULL, NULL, 0, &desc[VERSION_cmd]}, + { QUIT, "quit", "exit", NULL, 0, &desc[QUIT]}, + + { NO_CMD, "", NULL, NULL, 0, &helphdr}, + { HELP, "help", NULL, NULL, 0, &desc[HELP]}, + + { NO_CMD, "", NULL, NULL, 0, &unsuphdr}, + { HELP, "-help", NULL, NULL, 0, &desc[HELP]}, + { DUMPFUNC, "dfuncs", NULL, "string", 1, &desc[DUMPFUNC]}, + { DUMPDOBJS, "ddobjs", NULL, "string", 1, &desc[DUMPDOBJS]}, + { DUMPNODES, "dnodes", NULL, NULL, 0, &desc[DUMPNODES]}, + { DUMPSTACKS, "dstacks", NULL, NULL, 0, &desc[DUMPSTACKS]}, + { DUMPUNK, "dunkpc", NULL, NULL, 0, &desc[DUMPUNK]}, + { DUMPMAP, "dmap", NULL, NULL, 0, &desc[DUMPMAP]}, + { DUMPENTITIES, "dentities", NULL, NULL, 0, &desc[DUMPENTITIES]}, + { IGNORE_NO_XHWCPROF, "ignore_no_xhwcprof", NULL, NULL, 0, &desc[IGNORE_NO_XHWCPROF]}, + { IGNORE_FS_WARN, "ignore_fs_warn", NULL, NULL, 0, &desc[IGNORE_FS_WARN]}, + + { DUMP_PROFILE, "dprofile", NULL, NULL, 0, &desc[DUMP_PROFILE]}, + { DUMP_SYNC, "dsync", NULL, NULL, 0, &desc[DUMP_SYNC]}, + { DUMP_IOTRACE, "diotrace", NULL, NULL, 0, &desc[DUMP_IOTRACE]}, + { DUMP_HWC, "dhwc", NULL, NULL, 0, &desc[DUMP_HWC]}, + { DUMP_HEAP, "dheap", NULL, NULL, 0, &desc[DUMP_HEAP]}, + { RACE_ACCS, "r_accs", NULL, NULL, 0, &desc[RACE_ACCS]}, + + { DMPI_FUNCS, "dmpi_funcs", NULL, NULL, 0, &desc[DMPI_FUNCS]}, + { DMPI_MSGS, "dmpi_msgs", NULL, NULL, 0, &desc[DMPI_MSGS]}, + { DMPI_EVENTS, "dmpi_events", NULL, NULL, 0, &desc[DMPI_EVENTS]}, + + { DMEM, "dmem", NULL, NULL, 1, &desc[DMEM]}, + { DUMP_GC, "dumpgc", NULL, NULL, 0, &desc[DUMP_GC]}, + { DKILL, "dkill", NULL, NULL, 2, &desc[DKILL]}, + + { QQUIT, "xquit", NULL, NULL, 0, &desc[QQUIT]}, + // use xquit for memory leak detection in dbe; it's + // like quit, but deletes all data loaded + + { HHELP, "xhelp", NULL, NULL, 0, &desc[HHELP]}, + { WHOAMI, "-whoami", NULL, NULL, 0, &desc[WHOAMI]}, + + // these are not recognized at this point + { LOADOBJECT, "segments", "pmap", NULL, 0, &desc[LOADOBJECT]}, + { LOADOBJECT_LIST, "segment_list", NULL, NULL, 0, &desc[LOADOBJECT_LIST]}, + { LOADOBJECT_SELECT, "segment_select", NULL, "seg1,...", 1, &desc[LOADOBJECT_SELECT]}, + + { LAST_CMD, "xxxx", NULL, NULL, 0, NULL} +}; + +CmdType +Command::get_command (char *cmd, int &arg_count, int &cparam) +{ + int i; + int len = (int) strlen (cmd); + bool got = false; + CmdType token = UNKNOWN_CMD; + arg_count = 0; + cparam = -1; + if (*cmd == '\0') // - command + return STDIN; + if (*cmd == '#') // comment + return COMMENT; + if (strcmp (cmd, "V") == 0 || strcmp (cmd, "-version") == 0) + return VERSION_cmd; + if (strcmp (cmd, "-help") == 0) + return HELP; + if (strncmp (cmd, NTXT ("-whoami="), 8) == 0) + { + cparam = 8; + return WHOAMI; + } + + if (*cmd == '-') + cmd++; + for (i = 0;; i++) + { + if (cmd_lst[i].token == LAST_CMD) + break; + if (!strncasecmp (cmd, cmd_lst[i].str, len) || + (cmd_lst[i].alt && !strncasecmp (cmd, cmd_lst[i].alt, len))) + { + // Is it unambiguous? + if (!strcasecmp (cmd, cmd_lst[i].str) + || (cmd_lst[i].alt && !strcasecmp (cmd, cmd_lst[i].alt))) + { + // exact, full-length match + token = cmd_lst[i].token; + arg_count = cmd_lst[i].arg_count; + return token; + } + if (got) + return AMBIGUOUS_CMD; + got = true; + token = cmd_lst[i].token; + arg_count = cmd_lst[i].arg_count; + } + } + + // Did we find it? + if (token != UNKNOWN_CMD) + return token; + + // See if it's the name of a index object + if (dbeSession) + { + int indxtype = dbeSession->findIndexSpaceByName (cmd); + if (indxtype >= 0) + { + // found it + cparam = indxtype; + return INDXOBJ; + } + } + return token; +} + +const char * +Command::get_cmd_str (CmdType type) +{ + for (int i = 0;; i++) + { + if (cmd_lst[i].token == LAST_CMD) + break; + if (type == cmd_lst[i].token) + return cmd_lst[i].str; + } + return "xxxx"; +} + +char * +Command::get_err_string (Cmd_status err) +{ + switch (err) + { + case CMD_OK: + return NULL; + case CMD_BAD: + return GTXT ("command bad"); + case CMD_AMBIGUOUS: + return GTXT ("command ambiguous"); + case CMD_BAD_ARG: + return GTXT ("Invalid argument to command"); + case CMD_OUTRANGE: + return GTXT ("argument to command is out-of-range"); + case CMD_INVALID: + return GTXT ("invalid command"); + } + return NULL; +} + +void +Command::print_help (char *prog_name, bool cmd_line, bool usermode, FILE *outf) +{ + char *fmt, *msg; + int i; + StringBuilder sb; + enum CmdType nc; + init_desc (); + if (usermode) // show the hidden ones, too + nc = HELP; + else + nc = HHELP; + + if (cmd_line) + fprintf (outf, GTXT ("Usage: %s [ -script script | -command | - ] exper_1 ... exper_n\n"), + prog_name); + fprintf (outf, GTXT ("An alternate spelling for a command is shown in [], where applicable.\n\n" + "Those commands followed by a * may appear in .rc files.\n\n" + "Those commands followed by a $ can only appear in .rc files.\n\n")); + fmt = fmt_help (nc, ' '); + for (i = 0;; i++) + { + // check for end of list + if (cmd_lst[i].token == LAST_CMD) + break; + if (cmd_lst[i].token == NO_CMD) // this is a header line + fprintf (outf, NTXT (" %s\n"), *cmd_lst[i].desc); + else + { + if (strlen (cmd_lst[i].str) == 0) + continue; + // this is a real command line + sb.setLength (0); + sb.append (cmd_lst[i].str); + if (cmd_lst[i].alt) + { + sb.append ('['); + sb.append (cmd_lst[i].alt); + sb.append (']'); + } + if (cmd_lst[i].arg) + { + sb.append (' '); + sb.append (cmd_lst[i].arg); + } + msg = sb.toString (); + fprintf (outf, fmt, msg, *cmd_lst[i].desc); + free (msg); + } + // check for end of list + if (cmd_lst[i].token == nc) + break; + } +} + +// construct format for printing help +char * +Command::fmt_help (int nc, char head) +{ + int len, max_len, i; + static char fmt[BUFSIZ]; + + max_len = 0; + for (i = 0; i < nc; i++) + { + len = (int) strlen (cmd_lst[i].str); + if (cmd_lst[i].alt) + len += (int) strlen (cmd_lst[i].alt) + 2; + if (cmd_lst[i].arg) + len += (int) strlen (cmd_lst[i].arg) + 2; + if (max_len < len) + max_len = len; + } + snprintf (fmt, sizeof (fmt), NTXT (" %c%%-%ds %%s\n"), head, max_len + 1); + return fmt; +} + +void +Command::init_desc () +{ + if (desc[0] != NULL) + return; + desc[FUNCS] = GTXT ("display functions with current metrics"); + desc[HOTPCS] = GTXT ("display hot PC's with current metrics"); + desc[HOTLINES] = GTXT ("display hot lines with current metrics"); + desc[FDETAIL] = GTXT ("display summary metrics for each function"); + desc[OBJECTS] = GTXT ("display object list with errors or warnings"); + desc[COMPARE] = GTXT ("enable comparison mode for experiments *"); + desc[PRINTMODE] = GTXT ("set the mode for printing tables *"); + desc[LDETAIL] = GTXT ("display summary metrics for each hot line"); + desc[PDETAIL] = GTXT ("display summary metrics for each hot PC"); + desc[SOURCE] = GTXT ("display annotated source for function/file"); + desc[DISASM] = GTXT ("display annotated disassembly for function/file"); + desc[SCOMPCOM] = GTXT ("set compiler commentary classes for source *"); + desc[STHRESH] = GTXT ("set highlight threshold for source *"); + desc[DCOMPCOM] = GTXT ("set compiler commentary classes for disasm *"); + desc[COMPCOM] = GTXT ("set compiler commentary classes for both source and disasm *"); + desc[DTHRESH] = GTXT ("set highlight threshold for disasm *"); + desc[METRIC_LIST] = GTXT ("display the available metrics and dmetrics keywords"); + desc[METRICS] = GTXT ("set a new list of metrics"); + desc[SORT] = GTXT ("sort tables by the specified metric"); + desc[GPROF] = GTXT ("display the callers-callees for each function"); + desc[CALLTREE] = GTXT ("display the tree of function calls"); + desc[CALLFLAME] = GTXT ("request calltree flame chart -- not a command, but used in the tabs command"); + desc[GMETRIC_LIST] = GTXT ("display the available callers-callees metrics"); + desc[FSINGLE] = GTXT ("display the summary metrics for specified function"); + desc[CSINGLE] = GTXT ("display the callers-callees for the specified function"); + desc[CPREPEND] = GTXT ("add specified function to the head of the callstack fragment"); + desc[CAPPEND] = GTXT ("add specified function to the end of the callstack fragment"); + desc[CRMFIRST] = GTXT ("remove the first function from the callstack fragment"); + desc[CRMLAST] = GTXT ("remove the last function from the callstack fragment"); + desc[LEAKS] = GTXT ("display memory leaks, aggregated by callstack"); + desc[ALLOCS] = GTXT ("display allocations, aggregated by callstack"); + desc[HEAP] = GTXT ("display memory allocations and leaks, aggregated by callstack"); + desc[HEAPSTAT] = GTXT ("display heap statistics report"); + desc[IOACTIVITY] = GTXT ("display I/O activity report, aggregated by file name"); + desc[IOVFD] = GTXT ("display I/O activity report, aggregated by file descriptor"); + desc[IOCALLSTACK] = GTXT ("display I/O activity report, aggregated by callstack"); + desc[IOSTAT] = GTXT ("display I/O statistics report"); + desc[RACE_ACCS] = GTXT ("dump race access events"); + desc[DMPI_MSGS] = GTXT ("dump mpi messages"); + desc[DMPI_FUNCS] = GTXT ("dump mpi function calls"); + desc[DMPI_EVENTS] = GTXT ("dump mpi trace events"); + desc[DMEM] = GTXT ("debug command for internal use"); + desc[DUMP_GC] = GTXT ("dump Java garbage collector events"); + desc[DKILL] = GTXT ("send process p signal s"); + desc[DEADLOCK_EVNTS] = GTXT ("display deadlock events"); + desc[DEADLOCK_SUM] = GTXT ("display summary for the deadlock event"); + desc[HEADER] = GTXT ("display information about the experiment"); + desc[OVERVIEW_NEW] = GTXT ("display the overview of all loaded experiments"); + desc[SAMPLE_DETAIL] = GTXT ("display the current sample list with data"); + desc[STATISTICS] = GTXT ("display the execution statistics data"); + desc[EXP_LIST] = GTXT ("display the existing experiments"); + desc[DESCRIBE] = GTXT ("describe recorded data and tokens available for filtering data"); + desc[OBJECT_SHOW] = GTXT ("set load objects to show all functions *"); + desc[OBJECT_HIDE] = GTXT ("set load objects to hide functions *"); + desc[OBJECT_API] = GTXT ("set load objects to show API (entry point) only *"); + desc[OBJECTS_DEFAULT] = GTXT ("reset load objects show to defaults"); + desc[OBJECT_LIST] = GTXT ("display load objects, functions-shown flag"); + desc[OBJECT_SELECT] = GTXT ("set list of load objects whose functions are shown"); + desc[SAMPLE_LIST] = GTXT ("display the list of existing samples"); + desc[SAMPLE_SELECT] = GTXT ("set a new list of samples"); + desc[THREAD_LIST] = GTXT ("display the list of existing threads"); + desc[THREAD_SELECT] = GTXT ("set a new list of threads"); + desc[LWP_LIST] = GTXT ("display the list of existing LWPs"); + desc[LWP_SELECT] = GTXT ("set a new list of LWPs"); + desc[CPU_LIST] = GTXT ("display the list of CPUs"); + desc[CPU_SELECT] = GTXT ("set a new list of CPUs"); + desc[OUTFILE] = GTXT ("open filename for subsequent output"); + desc[APPENDFILE] = GTXT ("open filename for subsequent appended output"); + desc[LIMIT] = GTXT ("limit output to the first n entries (n=0 for no limit)"); + desc[NAMEFMT] = GTXT ("set long/short/mangled names for functions *"); + desc[VIEWMODE] = GTXT ("set viewmode user|expert|machine *"); + desc[EN_DESC] = GTXT ("enable descendant processes on|off|regex matches lineage or program name $"); + desc[SETPATH] = GTXT ("set search path for annotated src/dis"); + desc[ADDPATH] = GTXT ("add search path for annotated src/dis *"); + desc[PATHMAP] = GTXT ("remap path prefix for annotated src/dis *"); + desc[LIBDIRS] = GTXT ("set path where the gprofng libraries are installed"); + desc[SCRIPT] = GTXT ("read er_print commands from script file"); + desc[PROCSTATS] = GTXT ("display processing statistics"); + desc[ADD_EXP] = GTXT ("add experiment or group"); + desc[DROP_EXP] = GTXT ("drop experiment"); + desc[OPEN_EXP] = GTXT ("open experiment or group (drops all loaded experiments first)"); + desc[VERSION_cmd] = GTXT ("display the current release version"); + desc[HELP] = GTXT ("display the list of available commands"); + desc[QUIT] = GTXT ("terminate processing and exit"); + desc[DMETRICS] = GTXT ("set default function list metrics $"); + desc[DSORT] = GTXT ("set default function list sort metric $"); + desc[TLMODE] = GTXT ("set default timeline mode, align, depth $"); + desc[TLDATA] = GTXT ("set default timeline visible data $"); + desc[TABS] = GTXT ("set default visible tabs $"); + desc[RTABS] = GTXT ("set default visible tabs for Thread Analyzer Experiment $"); + desc[INDXOBJ] = GTXT ("display index objects of a specified type with current metrics"); + desc[INDXOBJLIST] = GTXT ("display list of index objects"); + desc[INDXOBJDEF] = GTXT ("define a new index object type *"); + desc[INDX_METRIC_LIST] = GTXT ("display the available index object metrics"); + desc[IFREQ] = GTXT ("display instruction-frequency report"); + desc[TIMELINE] = GTXT ("request timeline -- not a command, but used in the tabs command"); + desc[MPI_TIMELINE] = GTXT ("request mpi-timeline -- not a command, but used in the tabs command"); + desc[MPI_CHART] = GTXT ("request mpi chart -- not a command, but used in the tabs command"); + desc[DUALSOURCE] = GTXT ("request dualsource tab -- not a command, but used in the tabs command"); + desc[SOURCEDISAM] = GTXT ("request source/disassembly tab -- not a command, but used in the tabs command"); + desc[DUMPNODES] = GTXT ("dump pathtree node table"); + desc[DUMPSTACKS] = GTXT ("dump Experiment callstack tables"); + desc[DUMPUNK] = GTXT ("dump <Unknown> PCs"); + desc[DUMPFUNC] = GTXT ("dump functions whose name matches string"); + desc[DUMPDOBJS] = GTXT ("dump dataobjects whose name matches string"); + desc[DUMPMAP] = GTXT ("dump load-object map"); + desc[DUMPENTITIES] = GTXT ("dump threads, lwps, cpus"); + desc[DUMP_PROFILE] = GTXT ("dump clock profile events"); + desc[DUMP_SYNC] = GTXT ("dump synchronization trace events"); + desc[DUMP_IOTRACE] = GTXT ("dump IO trace events"); + desc[DUMP_HWC] = GTXT ("dump HWC profile events"); + desc[DUMP_HEAP] = GTXT ("dump heap trace events"); + desc[IGNORE_NO_XHWCPROF] = GTXT ("ignore absence of -xhwcprof info in dataspace profiling $"); + desc[IGNORE_FS_WARN] = GTXT ("ignore filesystem (nfs, ...) warning $"); + desc[HHELP] = GTXT ("display help including unsupported commands"); + desc[QQUIT] = GTXT ("terminate processing and exit"); + desc[LOADOBJECT] = GTXT ("display the address map with current metrics"); + desc[LOADOBJECT_LIST] = GTXT ("display segments, indicating which are selected"); + desc[LOADOBJECT_SELECT] = GTXT ("set a new list of segments"); + desc[FILTERS] = GTXT ("define a filter"); + + fhdr = GTXT ("\nCommands controlling the function list:"); + cchdr = GTXT ("\nCommands controlling the callers-callees and calltree lists:"); + lahdr = GTXT ("\nCommands controlling the leak and allocation lists:"); + iohdr = GTXT ("\nCommand controlling the I/O activity report:"); + rahdr = GTXT ("\nCommands controlling the race events lists:"); + ddhdr = GTXT ("\nCommands controlling the deadlock events lists:"); + typehdr = GTXT ("equivalent to \"memobj type\", or \"indxobj type\""); + typehdr2 = GTXT (" where type is a memory object or index object type"); + sdhdr = GTXT ("\nCommands controlling the source and disassembly listings:"); + lsthdr = GTXT ("\nCommands listing experiments, samples and threads:"); + lohdr = GTXT ("\nCommands controlling load object selection:"); + obj_allhdr = GTXT (" the special object name `all' refers to all load objects"); + methdr = GTXT ("\nCommands that list metrics:"); + othdr = GTXT ("\nCommands that print other displays:"); + outhdr = GTXT ("\nCommands that control output:"); + mischdr = GTXT ("\nMiscellaneous commands:"); + exphdr = GTXT ("\nCommands for experiments (scripts and interactive mode only):"); + deflthdr = GTXT ("\nDefault-setting commands:"); + selhdr = GTXT ("\nCommands controlling old-style filters/selection:"); + filthdr = GTXT ("\nCommands controlling filters:"); + indxobjhdr = GTXT ("\nCommands controlling the index objects:"); + unsuphdr = GTXT ("\nUnsupported commands:"); + helphdr = GTXT ("\nHelp command:"); +} diff --git a/gprofng/src/Command.h b/gprofng/src/Command.h new file mode 100644 index 0000000..4dd28ad --- /dev/null +++ b/gprofng/src/Command.h @@ -0,0 +1,286 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _COMMAND_H +#define _COMMAND_H + + +#include <stdio.h> +#include <sys/types.h> + +#include "Metric.h" +#include "Hist_data.h" +#include "dbe_types.h" +#include "vec.h" +#include "enums.h" + +// This enum lists all the commands parsed by er_print +// The ordering here is not important, but LAST_CMD must +// be defined as the last command for which a help line will exist +// Command.cc has a matching list, and the ordering in +// that list determines what shows up under the help and xhelp commands. +// In particular, the entry for HELP is the last one printed +// for the help command, and the entry for HHELP is the last +// one printed for xhelp. + +enum CmdType +{ + // Pathtree-related commands + FUNCS = 0, + HOTPCS, + HOTLINES, + FDETAIL, + OBJECTS, + LDETAIL, + PDETAIL, + SOURCE, + DISASM, + METRIC_LIST, + METRICS, + SORT, + GPROF, + GMETRIC_LIST, + FSINGLE, + CSINGLE, + CPREPEND, + CAPPEND, + CRMFIRST, + CRMLAST, + CALLTREE, + CALLFLAME, + + // Source/disassembly control commands + SCOMPCOM, + STHRESH, + DCOMPCOM, + COMPCOM, + DTHRESH, + + // Heap trace-related commands + LEAKS, + ALLOCS, + HEAP, + HEAPSTAT, + + // I/O trace-related commands + IOACTIVITY, + IOVFD, + IOCALLSTACK, + IOSTAT, + + // Race detection related commands + RACE_EVNTS, + RACE_SUM, + + // Deadlock detection commands + DEADLOCK_EVNTS, + DEADLOCK_SUM, + + // DataSpace commands + DOBJECTS, + DO_SINGLE, + DO_LAYOUT, + DO_METRIC_LIST, + + // MemorySpace commands + MEMOBJ, + MEMOBJLIST, + MEMOBJDEF, + MEMOBJDROP, + MACHINEMODEL, + + // Custom tab commands + INDXOBJDEF, + INDXOBJLIST, + INDXOBJ, + INDX_METRIC_LIST, + + // Old-style filtering commands + OBJECT_LIST, + OBJECT_SELECT, + SAMPLE_LIST, + SAMPLE_SELECT, + THREAD_LIST, + THREAD_SELECT, + LWP_LIST, + LWP_SELECT, + CPU_LIST, + CPU_SELECT, + + // Shared Object display commands + OBJECT_SHOW, + OBJECT_HIDE, + OBJECT_API, + OBJECTS_DEFAULT, + + // the new filtering commands + FILTERS, + + // Miscellaneous commands + COMPARE, + PRINTMODE, + HEADER, + OVERVIEW_NEW, + SAMPLE_DETAIL, + STATISTICS, + EXP_LIST, + DESCRIBE, + OUTFILE, + APPENDFILE, + LIMIT, + NAMEFMT, + VIEWMODE, + EN_DESC, + SETPATH, + ADDPATH, + PATHMAP, + LIBDIRS, + SCRIPT, + VERSION_cmd, + QUIT, + PROCSTATS, + + // Experiments handling commands + ADD_EXP, + DROP_EXP, + OPEN_EXP, + + // .rc-only Commands + DMETRICS, + DSORT, + TLMODE, + TLDATA, + TABS, + TIMELINE, + MPI_TIMELINE, + MPI_CHART, + TIMELINE_CLASSIC_TBR, + SOURCE_V2, + DISASM_V2, + RTABS, + DUALSOURCE, + SOURCEDISAM, + + HELP, // this is the last of the commands listed with "help" + IFREQ, + DUMPNODES, + DUMPSTACKS, + DUMPUNK, + DUMPFUNC, + DUMPDOBJS, + DUMPMAP, + DUMPENTITIES, + DUMP_PROFILE, + DUMP_SYNC, + DUMP_HWC, + DUMP_HEAP, + DUMP_IOTRACE, + RACE_ACCS, + DMPI_FUNCS, + DMPI_MSGS, + DMPI_EVENTS, + DMEM, + DUMP_GC, + DKILL, + IGNORE_NO_XHWCPROF, + IGNORE_FS_WARN, + QQUIT, + HHELP, // this is the last command listed with "xhelp" + NO_CMD, // Dummy command, used for headers in help + DUMMY_CMD, // Dummy command, used for help + + // unused commands + LOADOBJECT, + LOADOBJECT_LIST, + LOADOBJECT_SELECT, + + // Internal-only Commands + LAST_CMD, // No more commands for which a help line is possible + STDIN, + COMMENT, + WHOAMI, + + // Error return "commands" + AMBIGUOUS_CMD, + UNKNOWN_CMD +}; + +typedef struct +{ + const CmdType token; // command key + const char *str; // command string + const char *alt; // alternate command string + const char *arg; // argument string for help + const int arg_count; // no. of arguments + char **desc; // description for help +} Cmdtable; + +// Command class: never instantiated, completely static +class Command +{ +public: + + // look up a string in the command table, return type, set number of args + static CmdType get_command (char *cmd, int &arg_count, int ¶m); + static const char *get_cmd_str (CmdType type); + static void print_help (char *prog_name, bool cmd_line, bool usermode, FILE *outf); + static char *get_err_string (Cmd_status err); + + static const char *DEFAULT_METRICS; // default if no .rc files read + static const char *DEFAULT_SORT; // default if no .rc files read + static const char *DEFAULT_CMD; // token for default + static const char *ALL_CMD; // token for all + static const char *ANY_CMD; // token for any + static const char *NONE_CMD; // token for none + static const char *HWC_CMD; // token for all HWC + static const char *BIT_CMD; // token for any bit-derived metric + +private: + static const int user_no; // the last user command + static const int hidden_no; // the last hidden command + static const int command_no; // the last parsable command + + static void init_desc (); + static char *fmt_help (int nc, char head); +}; + +// Analyzer display tabs +struct DispTab +{ + DispTab (int ntype, int num, bool vis, CmdType token) + { + type = ntype; + order = num; + visible = vis; + available = true; + cmdtoken = token; + } + + void setAvailability (bool val) { available = val; } + + int type; // Display type + int order; // Order in which tabs should appear in GUI + bool visible; // Is Tab visible + bool available; // Is tab available for this experiment + CmdType cmdtoken; // command token + int param; // command parameter (used for memory space) +}; + +#endif /* ! _COMMAND_H */ diff --git a/gprofng/src/CompCom.cc b/gprofng/src/CompCom.cc new file mode 100644 index 0000000..3a035a9 --- /dev/null +++ b/gprofng/src/CompCom.cc @@ -0,0 +1,313 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <locale.h> +#include <sys/param.h> + +#include "demangle.h" +#include "gp-defs.h" +#include "StringBuilder.h" +#include "CompCom.h" +#include "Elf.h" +#include "util.h" +#include "i18n.h" +#include "comp_com.c" + +CompComment::CompComment (Elf *_elf, int _compcom) +{ + elf = _elf; + compcom = _compcom; + elf_cls = elf->elf_getclass (); +} + +CompComment::~CompComment () { } + +int +CompComment::get_align (int64_t offset, int align) +{ + int val = (int) (offset % align); + if (val) + val = align - val; + return val; +} + +/* + * Preprocesses the header structure, builds a table of messages with the line + * numbers, PCoffsets, original index, and compmsg pointer for each message. + * If the show_bits field is not in the message, this routine would fill it in + * from the mapping from COMPMSG_ID + */ +int +CompComment::compcom_open (CheckSrcName check_src) +{ + if (check_src == NULL) + return 0; + Elf_Data *data = elf->elf_getdata (compcom); + uint64_t b_offset = data->d_off; + if (get_align (b_offset, 4)) // not align 4 + return 0; + char *CommData = (char *) data->d_buf; + uint64_t offset = b_offset; + for (uint64_t e_offset = b_offset + data->d_size; offset < e_offset;) + { + offset += get_align (offset, (int) data->d_align); + if (offset >= e_offset) + return 0; + compcomhdr *hdr = (compcomhdr *) (CommData + (offset - b_offset)); + int hdr_msgcount = elf->decode (hdr->msgcount); + int hdr_srcname = elf->decode (hdr->srcname); + int hdr_stringlen = elf->decode (hdr->stringlen); + int hdr_paramcount = elf->decode (hdr->paramcount); + size_t length = sizeof (compcomhdr) + hdr_msgcount * sizeof (compmsg) + + hdr_paramcount * sizeof (int32_t); + if (offset + length + hdr_stringlen > e_offset || hdr_srcname < 0 + || hdr_srcname >= hdr_stringlen) + return 0; + + // check source file + char *src_name = (char *) (((char*) hdr) + length + hdr_srcname); + if (check_src (src_name)) + { + msgs = (compmsg *) (((char *) hdr) + sizeof (compcomhdr)); + params = (int32_t *) ((char *) msgs + hdr_msgcount * sizeof (compmsg)); + strs = (char *) ((char *) params + hdr_paramcount * sizeof (int32_t)); + + // initialize the I18N/L10N strings & set the visible table + ccm_vis_init (); + return hdr_msgcount; + } + offset += (length + hdr_stringlen); + } + return 0; +} + +char * +CompComment::get_demangle_name (char *fname) +{ + if (*fname == '_') + return cplus_demangle (fname, DMGL_PARAMS); + return NULL; +} + +/* + * takes the message, and returns the I18N string for the message. + */ +char * +CompComment::compcom_format (int index, compmsg *msg, int &visible) +{ + compmsg *p = msgs + index; + msg->instaddr = elf->decode (p->instaddr); + msg->lineno = elf->decode (p->lineno); + msg->msg_type = elf->decode (p->msg_type); + msg->nparam = elf->decode (p->nparam); + msg->param_index = elf->decode (p->param_index); + + int vindex = ccm_vis_index (msg->msg_type); + char *mbuf; + Ccm_Primtype_t prim_ty; + visible = ccm_attrs[vindex].vis; + if (ccm_attrs[vindex].msg == NULL) + { + /* Print CCM_UNKNOWN message */ + int uindex = ccm_vis_index (CCM_UNKNOWN); + visible = ccm_attrs[uindex].vis; + return dbe_sprintf (ccm_attrs[uindex].msg, vindex); + } + + /* + * Construct the output buffer based on the primitive types of the + * message parameters. + * + * Parameter lists have to be handled carefully -- the 1 parameter + * is built up of all the elements separated by ", ". + * + * Old way: Case by message format string. + */ + int *ind = params + msg->param_index; + int plist_idx = ccm_paramlist_index (msg->msg_type); + if (plist_idx <= 0) + { + /* No parameter list to handle; 0 parameters case is handled */ + + enum + { + MAX_COMPCOM_ARGS = 13 + }; + char *parms[MAX_COMPCOM_ARGS]; + if (msg->nparam >= MAX_COMPCOM_ARGS) + { + fprintf (stderr, + GTXT ("Warning: improperly formatted compiler commentary message (%d parameters >= %d);\n please report this bug against the compiler\n"), + msg->nparam, MAX_COMPCOM_ARGS); + return NULL; + } + for (int i = 0; i < MAX_COMPCOM_ARGS; i++) + parms[i] = NULL; // initialize array + int prm_cnt = ccm_num_params (msg->msg_type); + if (prm_cnt != msg->nparam) + { + fprintf (stderr, + GTXT ("Warning, improperly formatted compiler commentary message (parameter count mismatch = %d, param# = %d, msg_type = %x, `%s');\n please report this bug against the compiler\n"), + prm_cnt, msg->nparam, msg->msg_type, ccm_attrs[vindex].msg); + return NULL; + } + for (int i = 0; i < msg->nparam; i++) + { + /* Parameters in message-type numbered from '1' */ + prim_ty = ccm_param_primtype (msg->msg_type, i + 1); + if (prim_ty == CCM_PRIMTYPE_INTEGER) + { + unsigned long v = elf->decode (ind[i]); + parms[i] = (char*) v; + } + else if (prim_ty == CCM_PRIMTYPE_STRING) + { + char *fname = strs + elf->decode (ind[i]); + char *demName = get_demangle_name (fname); + parms[i] = demName ? demName : dbe_strdup (fname); + } + else if (prim_ty == CCM_PRIMTYPE_HEXSTRING) + parms[i] = dbe_sprintf (elf_cls == ELFCLASS32 ? NTXT ("0x%08llx") : NTXT ("0x%016llx"), + (unsigned long long) msg->instaddr); + else + { + fprintf (stderr, + GTXT ("Warning, improperly formatted compiler commentary message (unexpected primitive type %d);\n please report this bug against the compiler\n"), + prim_ty); + // Dummy code to avoid compiler's warning: static function ccm_param_hightype is not used + Ccm_Hitype_t hightype = CCM_HITYPE_NONE; + if (hightype != CCM_HITYPE_NONE) + hightype = ccm_param_hightype (msg->msg_type, i + 1); + return NULL; + } + } + + /* + * Must make sure to pass _ALL_ params; may pass more because + * the format won't access the 'extra' parameters if all the + * rules for messages have been followed. + */ + mbuf = dbe_sprintf (ccm_attrs[vindex].msg, parms[0], parms[1], parms[2], + parms[3], parms[4], parms[5], parms[6], parms[7], + parms[8], parms[9], parms[10], parms[11]); + // Cleanup allocated memory. + for (int i = 0; i < msg->nparam; i++) + { + prim_ty = ccm_param_primtype (msg->msg_type, i + 1); + if (prim_ty == CCM_PRIMTYPE_STRING || prim_ty == CCM_PRIMTYPE_HEXSTRING) + free (parms[i]); + } + } + else + { + /* + * Parameter list messages never have 0 parameters; the + * primitive type for the parameter list elements is always + * the same. And as of 22-Sep-2006, it was always + * CCM_PRIMTYPE_STRING. + * + * Account for different bases of parameter indices and + * 'nparam' count (1 and 0, respectively). + */ + char *parms[3]; + if (plist_idx > (int) ((sizeof (parms) / sizeof (char*)))) + { + fprintf (stderr, + GTXT ("Warning: improperly formatted compiler commentary message (msg->nparam=%d plist_idx=%d);\n please report this bug against the compiler\n"), + msg->nparam, plist_idx); + return NULL; + } + for (size_t i = 0; i < (sizeof (parms) / sizeof (char*)); i++) + parms[i] = NULL; // initialize array + + StringBuilder sb; + prim_ty = ccm_param_primtype (msg->msg_type, plist_idx); + for (int i = plist_idx - 1; i < msg->nparam; i++) + { + if (i != plist_idx - 1) + sb.append (GTXT (", ")); + if (prim_ty == CCM_PRIMTYPE_INTEGER) + sb.append (elf->decode (ind[i])); + else if (prim_ty == CCM_PRIMTYPE_STRING) + { + char *fname = strs + elf->decode (ind[i]); + char *demName = get_demangle_name (fname); + if (demName) + { + sb.append (demName); + delete demName; + } + else + sb.append (fname); + } + else if (prim_ty == CCM_PRIMTYPE_HEXSTRING) + sb.appendf (elf_cls == ELFCLASS32 ? NTXT ("0x%08llx") : NTXT ("0x%016llx"), + (unsigned long long) msg->instaddr); + } + parms[plist_idx - 1] = sb.toString (); + + for (int i = 0; i < plist_idx - 1; i++) + { + prim_ty = ccm_param_primtype (msg->msg_type, i + 1); + if (prim_ty == CCM_PRIMTYPE_INTEGER) + { + unsigned long v = elf->decode (ind[i]); + parms[i] = (char*) v; + } + else if (prim_ty == CCM_PRIMTYPE_STRING) + { + char *fname = strs + elf->decode (ind[i]); + char *demName = get_demangle_name (fname); + parms[i] = demName ? demName : dbe_strdup (fname); + } + else if (prim_ty == CCM_PRIMTYPE_HEXSTRING) + parms[i] = dbe_sprintf (elf_cls == ELFCLASS32 ? NTXT ("0x%08llx") : NTXT ("0x%016llx"), + (unsigned long long) msg->instaddr); + else + { + fprintf (stderr, + GTXT ("Warning, improperly formatted compiler commentary message (unexpected primitive type %d);\n please report this bug against the compiler\n"), + prim_ty); + return NULL; + } + } + + /* + * We have reduced the parameter list to a single string (as + * the printf format specifier requires), so only have + * 'plist_idx' parameters. + */ + mbuf = dbe_sprintf (ccm_attrs[vindex].msg, parms[0], parms[1], parms[2]); + + // Cleanup allocated memory. + free (parms[plist_idx - 1]); + for (int i = 0; i < plist_idx - 1; i++) + { + prim_ty = ccm_param_primtype (msg->msg_type, i + 1); + if (prim_ty == CCM_PRIMTYPE_STRING || prim_ty == CCM_PRIMTYPE_STRING) + free (parms[i]); + } + } + return mbuf; +} diff --git a/gprofng/src/CompCom.h b/gprofng/src/CompCom.h new file mode 100644 index 0000000..e653939 --- /dev/null +++ b/gprofng/src/CompCom.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _COMPCOM_H +#define _COMPCOM_H + +#include <sys/types.h> +#include "comp_com.h" + +class Elf; +typedef int (*CheckSrcName) (char *); + +class CompComment +{ +public: + CompComment (Elf *_elf, int _compcom); + ~CompComment (); + int compcom_open (CheckSrcName check_src); + char *compcom_format (int index, compmsg *msg, int &visible); + +private: + int get_align (int64_t, int align); + char *get_demangle_name (char *fname); + + Elf *elf; + int compcom, elf_cls; + compmsg *msgs; /* the array of messages */ + int32_t *params; /* the parameters used in the messages parameters are + * either integers or string-indices */ + char *strs; /* the strings used in the messages */ +}; + +class ComC +{ +public: + ComC () { com_str = NULL; }; + ~ComC () { free (com_str); }; + + int sec; + int type; + int visible; + int line; + char *com_str; +}; + +#endif /* _COMPCOM_H */ diff --git a/gprofng/src/DataObject.cc b/gprofng/src/DataObject.cc new file mode 100644 index 0000000..870a531 --- /dev/null +++ b/gprofng/src/DataObject.cc @@ -0,0 +1,193 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <assert.h> +#include <string.h> +#include <ctype.h> + +#include "util.h" +#include "DbeSession.h" +#include "Application.h" +#include "DataObject.h" +#include "Module.h" +#include "debug.h" + +DataObject::DataObject () +{ + name = NULL; + parent = NULL; + master = NULL; + _unannotated_name = NULL; + _typename = NULL; + _instname = NULL; + scope = NULL; + EAs = new Vector<DbeEA*>; + size = 0; + offset = (uint64_t) (-1); +} + +DataObject::~DataObject () +{ + free (_unannotated_name); + free (_typename); + free (_instname); + EAs->destroy (); + delete EAs; +} + +// get_addr() doesn't return an actual address for a DataObject +// but rather synthesises an address-like identifier tuple. +// XXXX since an aggregate and its first element have identical tuples +// may need to arrange for special-purpose sorting "by address" +uint64_t +DataObject::get_addr () +{ + uint64_t addr; + if (parent && parent->get_typename ()) + addr = MAKE_ADDRESS (parent->id, offset); // element + else if (parent) + addr = MAKE_ADDRESS (parent->id, id) | 0x8000000000000000ULL; // Scalar, Unknown + else if (id == dbeSession->get_Scalars_DataObject ()->id) + addr = MAKE_ADDRESS (id, 0) | 0x8000000000000000ULL; // Scalar aggregate + else if (id == dbeSession->get_Unknown_DataObject ()->id) + addr = MAKE_ADDRESS (id, 0) | 0x8000000000000000ULL; // Unknown aggregate + else + addr = MAKE_ADDRESS (id, 0); // aggregate + return addr; +} + +Histable * +DataObject::convertto (Histable_type type, Histable *) +{ + return type == DOBJECT ? this : NULL; +} + +char +DataObject::get_offset_mark () +{ + enum + { + blocksize = 32 + }; + + if (size == 0 || offset == -1) + return '?'; // undefined + if (size > blocksize) + return '#'; // requires multiple blocks + if (size == blocksize && (offset % blocksize == 0)) + return '<'; // fits block entirely + if (offset % blocksize == 0) + return '/'; // starts block + if ((offset + size) % blocksize == 0) + return '\\'; // closes block + if (offset / blocksize == ((offset + size) / blocksize)) + return '|'; // inside block + return 'X'; // crosses blocks unnecessarily +} + +char * +DataObject::get_offset_name () +{ + char *offset_name; + if (parent && parent->get_typename ()) // element + offset_name = dbe_sprintf (GTXT ("%c%+6lld .{%s %s}"), + get_offset_mark (), (long long) offset, + _typename ? _typename : GTXT ("NO_TYPE"), + _instname ? _instname : GTXT ("-")); // "NO_NAME" + else if ((offset != -1) && (offset > 0)) // filler + offset_name = dbe_sprintf (GTXT ("%c%+6lld %s"), get_offset_mark (), + (long long) offset, get_name ()); + else if (parent) // Scalar/Unknown element + offset_name = dbe_sprintf (GTXT (" .%s"), get_unannotated_name ()); + else // aggregate + offset_name = dbe_strdup (get_name ()); + return offset_name; +} + +void +DataObject::set_dobjname (char *type_name, char *inst_name) +{ + _unannotated_name = _typename = _instname = NULL; + if (inst_name) + _instname = dbe_strdup (inst_name); + + char *buf; + if (parent == dbeSession->get_Scalars_DataObject ()) + { + if (type_name) + _typename = dbe_strdup (type_name); + _unannotated_name = dbe_sprintf (NTXT ("{%s %s}"), type_name, + inst_name ? inst_name : NTXT ("-")); + buf = dbe_sprintf (NTXT ("%s.%s"), parent->get_name (), _unannotated_name); + } + else if (parent == dbeSession->get_Unknown_DataObject ()) + { + _unannotated_name = dbe_strdup (type_name); + buf = dbe_sprintf (NTXT ("%s.%s"), parent->get_name (), _unannotated_name); + } + else + { + if (type_name) + _typename = dbe_strdup (type_name); + if (parent && parent->get_typename ()) + buf = dbe_sprintf (NTXT ("%s.{%s %s}"), + parent->get_name () ? parent->get_name () : NTXT ("ORPHAN"), + type_name ? type_name : NTXT ("NO_TYPE"), + inst_name ? inst_name : NTXT ("-")); // "NO_NAME" + else + buf = dbe_sprintf (NTXT ("{%s %s}"), + type_name ? type_name : NTXT ("NO_TYPE"), + inst_name ? inst_name : NTXT ("-")); // "NO_NAME" + } + name = buf; + dbeSession->dobj_updateHT (this); +} + +void +DataObject::set_name (char *string) +{ + name = dbe_strdup (string); + dbeSession->dobj_updateHT (this); +} + +DbeEA * +DataObject::find_dbeEA (Vaddr EA) +{ + DbeEA *dbeEA; + int left = 0; + int right = EAs->size () - 1; + while (left <= right) + { + int index = (left + right) / 2; + dbeEA = EAs->fetch (index); + if (EA < dbeEA->eaddr) + right = index - 1; + else if (EA > dbeEA->eaddr) + left = index + 1; + else + return dbeEA; + } + + // None found, create a new one + dbeEA = new DbeEA (this, EA); + EAs->insert (left, dbeEA); + return dbeEA; +} diff --git a/gprofng/src/DataObject.h b/gprofng/src/DataObject.h new file mode 100644 index 0000000..f70bbad --- /dev/null +++ b/gprofng/src/DataObject.h @@ -0,0 +1,82 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DATAOBJECT_H +#define _DATAOBJECT_H + +// A DataObject object represents a distinct dataobject. + +#include "dbe_structs.h" +#include "Histable.h" + +extern char *DOBJ_UNSPECIFIED; +extern char *DOBJ_UNIDENTIFIED; +extern char *DOBJ_UNDETERMINED; +extern char *DOBJ_ANON; +extern char *DOBJ_UNASCERTAINABLE; +extern char *DOBJ_UNVERIFIABLE; +extern char *DOBJ_UNRESOLVABLE; + +class DataObject : public Histable +{ +public: + DataObject (); + ~DataObject (); + + static const unsigned UNSPECIFIED_ID = 0xFFFFFFFF; + + int64_t size; // size of the dataobject in bytes + int64_t offset; // offset of dataobject from parent + DataObject *parent; // this dataobject's parent (if any) + Histable *scope; // scope of this dataobject + DataObject *master; // this dataobject's master (if any) + + Histable_type get_type () { return DOBJECT; } + int64_t get_size () { return size; } + int64_t get_offset () { return offset; } + DataObject *get_parent () { return parent; } + DataObject *get_master () { return master; } + char *get_typename () { return _typename; } + char *get_instname () { return _instname; } + Histable *get_scope () { return scope; } + + char *get_unannotated_name () + { // name without a <Scalar> or <Unknown> prefix + if (_unannotated_name) + return _unannotated_name; + return get_name (); + } + + uint64_t get_addr (); + char get_offset_mark (); + char *get_offset_name (); + void set_dobjname (char *type_name, char *inst_name); // dobj->parent must already be set + void set_name (char *); + Histable *convertto (Histable_type type, Histable *obj = NULL); + DbeEA *find_dbeEA (Vaddr EA); + +private: + char *_unannotated_name; // name without a <Scalar> or <Unknown> prefix + char *_typename; // name of this dataobject's type + char *_instname; // name of this dataobject instance + Vector<DbeEA*> *EAs; +}; + +#endif /* _DATAOBJECT_H */ diff --git a/gprofng/src/DataSpace.cc b/gprofng/src/DataSpace.cc new file mode 100644 index 0000000..e5b48dd --- /dev/null +++ b/gprofng/src/DataSpace.cc @@ -0,0 +1,558 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdlib.h> +#include <stdarg.h> + +#include "util.h" +#include "Application.h" +#include "CallStack.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "DataObject.h" +#include "DbeSession.h" +#include "MetricList.h" +#include "Function.h" +#include "Module.h" +#include "MemObject.h" +#include "DbeView.h" +#include "Metric.h" +#include "DataSpace.h" +#include "LoadObject.h" + +#include "debug.h" +#include "ABS.h" + +//char *DOBJ_UNSPECIFIED = STXT("(Not identified by the compiler as a memory-referencing instruction)"); +char *DOBJ_UNSPECIFIED = STXT("(No type information)"); +char *DOBJ_UNIDENTIFIED = STXT("(No identifying descriptor provided by the compiler)"); +char *DOBJ_UNDETERMINED = STXT("(Not determined from the symbolic information provided by the compiler)"); +char *DOBJ_ANON = STXT("(Padding in structure)"); + +// run-time codes +// ABS_UNSUPPORTED = 0x01, /* inappropriate HWC event type */ +// ABS_BLOCKED = 0x02, /* runtime backtrack blocker reached */ +// ABS_INCOMPLETE = 0x03, /* runtime backtrack limit reached */ +// ABS_REG_LOSS = 0x04, /* address register contaminated */ +// ABS_INVALID_EA = 0x05, /* invalid effective address value */ + +const char *ABS_RT_CODES[NUM_ABS_RT_CODES] = { + "(OK)", + "(Dataspace data not requested during data collection)", + "(Backtracking was prevented by a jump or call instruction)", + "(Backtracking did not find trigger PC)", + "(Could not determine VA because registers changed after trigger instruction)", + "(Memory-referencing instruction did not specify a valid VA)", + "(UNKNOWN)" +}; + +// post-processing codes +// ABS_NO_CTI_INFO = 0x10, /* no AnalyzerInfo for validation */ +// ABS_INFO_FAILED = 0x20, /* info failed to validate backtrack */ +// ABS_CTI_TARGET = 0x30, /* CTI target invalidated backtrack */ +char *DOBJ_UNASCERTAINABLE = STXT("(Module with trigger PC not compiled with -xhwcprof)"); +char *DOBJ_UNVERIFIABLE = STXT("(Backtracking failed to find a valid branch target)"); +char *DOBJ_UNRESOLVABLE = STXT("(Backtracking traversed a branch target)"); + +char *ABS_PP_CODES[NUM_ABS_PP_CODES] = { + STXT ("(OK)"), + DOBJ_UNASCERTAINABLE, + DOBJ_UNVERIFIABLE, + DOBJ_UNRESOLVABLE, + STXT ("(<INTERNAL ERROR DURING POST-PROCESSING>)") +}; + +DataSpace::DataSpace (DbeView *_dbev, int /* _picked */) +{ + dbev = _dbev; +} + +DataSpace::~DataSpace () { } + +void +DataSpace::reset () { } + +char * +DataSpace::status_str () +{ + return NULL; +} + +Histable * +DataSpace::get_hist_obj (Histable::Type type, DataView *dview, long i) +{ + DataObject *dobj = NULL; + char *errcode = NTXT ("<internal error>"); + switch (type) + { + case Histable::DOBJECT: + dobj = (DataObject*) dview->getObjValue (PROP_HWCDOBJ, i); + if (dobj == NULL) + { + Vaddr leafVA = (Vaddr) dview->getLongValue (PROP_VADDR, i); + unsigned rt_code = (unsigned) ABS_GET_RT_CODE (leafVA); + unsigned pp_code = (unsigned) ABS_GET_PP_CODE (leafVA); + if (leafVA < ABS_CODE_RANGE + && (pp_code || (rt_code && rt_code != ABS_REG_LOSS))) + { + if (rt_code >= NUM_ABS_RT_CODES) + rt_code = NUM_ABS_RT_CODES - 1; + if (pp_code >= NUM_ABS_PP_CODES) + pp_code = NUM_ABS_PP_CODES - 1; + if (rt_code) + errcode = PTXT (ABS_RT_CODES[rt_code]); + else + errcode = PTXT (ABS_PP_CODES[pp_code]); + } + else + { + // associate dataobject with event + int index; + + // search for memop in Module infoList + void *cstack = dview->getObjValue (PROP_MSTACK, i); + Histable *leafPCObj = CallStack::getStackPC (cstack, 0); + DbeInstr *leafPC = NULL; + if (leafPCObj->get_type () == Histable::INSTR) + leafPC = (DbeInstr*) leafPCObj; + else // DBELINE + leafPC = (DbeInstr*) leafPCObj->convertto (Histable::INSTR); + Function *func = leafPC->func; + uint64_t leafPC_offset = func->img_offset + leafPC->addr; + Module *mod = func->module; + uint32_t dtype_id = 0; + inst_info_t *info = NULL; + Vec_loop (inst_info_t*, mod->infoList, index, info) + { + if (info->offset == leafPC_offset) + { + dtype_id = info->memop->datatype_id; + break; + } + } + dobj = mod->get_dobj (dtype_id); + if (dobj == NULL) + { + // ensure dobj is determined + if (dtype_id == DataObject::UNSPECIFIED_ID) + errcode = PTXT (DOBJ_UNSPECIFIED); + else + errcode = PTXT (DOBJ_UNIDENTIFIED); + } + else + { + // determine associated master dataobject + if (!dobj->master && dobj->scope) + dobj->master = dbeSession->createMasterDataObject (dobj); + if (dobj->scope) + dobj = dobj->master; // use associated master + } + } + if (!dobj) + { + // if dobj is not set yet, supply a dobj for errcode + // search for a dobj with the same name + dobj = dbeSession->find_dobj_by_name (errcode); + if (dobj == NULL) + { + // create new DataObject for unknown code + dobj = (DataObject*) dbeSession->createHistObject (Histable::DOBJECT); + dobj->size = 0; + dobj->offset = -1; + dobj->parent = dbeSession->get_Unknown_DataObject (); + dobj->set_dobjname (errcode, NULL); // dobj->parent must already be set + } + } + dview->setObjValue (PROP_HWCDOBJ, i, dobj); + } + break; + default: + break; + } + return dobj; +} + +Hist_data * +DataSpace::compute_metrics (MetricList *mlist, Histable::Type type, + Hist_data::Mode mode, Histable *sel_obj) +{ + int nmetrics = mlist->get_items ()->size (); + int sort_ind = -1; + Hist_data::HistItem *hi; + int index; + + // reset event_data count for all datatypes + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + for (int i = 0, sz = lobjs ? lobjs->size () : -1; i < sz; i++) + { + LoadObject *lo = lobjs->fetch (i); + Vector<Module*> *modules = lo->seg_modules; + for (int j = 0, msize = modules ? modules->size () : -1; j < msize; j++) + { + Module *mod = modules->fetch (j); + mod->reset_datatypes (); + } + } + Hist_data *hist_data = new Hist_data (mlist, type, mode); + + // add each experiment, skipping disabled and broken experiments + for (index = 0; index < dbeSession->nexps (); index++) + { + Experiment *exp = dbeSession->get_exp (index); + if (exp->broken) + continue; + + Collection_params *params = exp->get_params (); + if (!params->xhw_mode) + continue; + + char *expt_name = exp->get_expt_name (); + char *base_name = strrchr (expt_name, '/'); + base_name = base_name ? base_name + 1 : expt_name; + + // Determine mapping of experiment HWC metrics to hist_data metric list + int *xlate = new int[MAX_HWCOUNT]; + for (unsigned i = 0; i < MAX_HWCOUNT; i++) + { + xlate[i] = -1; + if (params->hw_interval[i] > 0) + { + const char *ctr_name = params->hw_aux_name[i]; + int mindex; + Metric *met; + Vec_loop (Metric*, mlist->get_items (), mindex, met) + { + if (dbe_strcmp (met->get_cmd (), ctr_name) == 0) + xlate[i] = mindex; + } + } + } + + // + // Process hardware profiling data + // + DataView *dview = dbev->get_filtered_events (index, DATA_HWC); + if (dview) + { + DataDescriptor *ddscr = dview ->getDataDescriptor (); + if (ddscr->getProp (PROP_HWCDOBJ) == NULL) + { + PropDescr *prop = new PropDescr (PROP_HWCDOBJ, NTXT ("HWCDOBJ")); + prop->uname = NULL; + prop->vtype = TYPE_OBJ; + ddscr->addProperty (prop); + } + } + if (dview && dview->getSize () != 0) + { + char *msg = NULL; + for (long i = 0; i < dview->getSize (); i++) + { + if (i % 5000 == 0) + { + int percent = (int) (100.0 * i / dview->getSize ()); + if (percent == 0 && msg == NULL) + msg = dbe_sprintf (GTXT ("Filtering HW Profile Address Data: %s"), base_name); + theApplication->set_progress (percent, (percent != 0) ? NULL : msg); + } + + uint32_t tag = dview->getIntValue (PROP_HWCTAG, i); + if (tag < 0 || tag >= MAX_HWCOUNT) + continue; // invalid HWC tag in the record; ignore it + int mHwcntr_idx = xlate[tag]; + if (mHwcntr_idx < 0) + continue; + + Vaddr leafVA = (Vaddr) dview->getLongValue (PROP_VADDR, i); + if (leafVA == ABS_UNSUPPORTED) + continue; // skip this record + Histable *obj = get_hist_obj (type, dview, i); + if (obj == NULL) + continue; + uint64_t interval = dview->getLongValue (PROP_HWCINT, i); + if (HWCVAL_HAS_ERR (interval)) + continue; + if (mode == Hist_data::ALL) + { // data_objects + hi = hist_data->append_hist_item (obj); + hi->value[mHwcntr_idx].ll += interval; + for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) + { + hi = hist_data->append_hist_item (dobj); + hi->value[mHwcntr_idx].ll += interval; + } + } + else if (mode == Hist_data::LAYOUT || mode == Hist_data::DETAIL) + { // data_single + { + // for data layout, insert elements that have no metrics yet + DataObject *tmpParent = ((DataObject *) obj)->parent; + if (tmpParent && tmpParent->get_typename ()) + { + // dobj is an aggregate element + if (!hist_data->find_hist_item (tmpParent)) + { + // parent not yet a member of hist_data + // supply parent's children with 0 values for layout + Vector<DataObject*> *elements = dbeSession->get_dobj_elements (tmpParent); + for (long eli = 0, sz = elements->size (); eli < sz; eli++) + { + DataObject* element = elements->fetch (eli); + assert (!hist_data->find_hist_item (element)); + hi = hist_data->append_hist_item (element); + } + } + } + } + + // Same as for mode == Hist_data::ALL: + hi = hist_data->append_hist_item (obj); + hi->value[mHwcntr_idx].ll += interval; + for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) + { + hi = hist_data->append_hist_item (dobj); + hi->value[mHwcntr_idx].ll += interval; + } + } + else if (mode == Hist_data::SELF) + { // used by dbeGetSummary() + if (obj == sel_obj) + { + hi = hist_data->append_hist_item (obj); + hi->value[mHwcntr_idx].ll += interval; + } + else + { + for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) + { + if ((Histable*) dobj == sel_obj) + { + hi = hist_data->append_hist_item (dobj); + hi->value[mHwcntr_idx].ll += interval; + break; + } + } + } + } + // Update total + hist_data->total->value[mHwcntr_idx].ll += interval; + } + free (msg); + theApplication->set_progress (0, NTXT ("")); + } + delete[] xlate; + } + + // include a regular HistItem for <Total> -- for all DataObjects, and MemObjects + DataObject *dtot = dbeSession->get_Total_DataObject (); + if (mode == Hist_data::ALL || mode == Hist_data::DETAIL + || mode == Hist_data::LAYOUT || + sel_obj == dtot) + { + hi = hist_data->append_hist_item (dtot); + for (int mind = 0; mind < nmetrics; mind++) + hi->value[mind] = hist_data->total->value[mind]; + } + if (hist_data->get_status () != Hist_data::SUCCESS) + return hist_data; + theApplication->set_progress (0, GTXT ("Constructing Metrics")); + + // Determine by which metric to sort if any + bool rev_sort = mlist->get_sort_rev (); + + // Compute static metrics: SIZES, ADDRESS. + for (int mind = 0; mind < nmetrics; mind++) + { + Metric *mtr = mlist->get_items ()->fetch (mind); + if (mlist->get_sort_ref_index () == mind) + sort_ind = mind; + else if (!mtr->is_visible () && !mtr->is_tvisible () + && !mtr->is_pvisible ()) + continue; + Metric::Type mtype = mtr->get_type (); + if (mtype == Metric::SIZES) + { + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].tag = VT_LLONG; + hi->value[mind].ll = h ? h->get_size () : 0; + } + } + else if (mtype == Metric::ONAME + && (mode == Hist_data::SELF + || ((DataObject*) sel_obj == dbeSession->get_Total_DataObject ()))) + { + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = VT_OFFSET; // offset labels + } + } + else if (mtype == Metric::ADDRESS) + { // pseudo-address + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = VT_ADDRESS; + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].ll = h ? h->get_addr () : 0; + } + // force sort by offset // XXXX should visibility also be set? + if (mode == Hist_data::SELF) + { // used by dbeGetSummary() + sort_ind = mind; + //hist_data->metrics->fetch(mind)->set_visible(T); + } + } + else + { + ValueTag vtype = mtr->get_vtype (); + switch (vtype) + { + case VT_ULLONG: // most Data-derived HWC metrics are VT_ULLONG + hist_data->total->value[mind].tag = vtype; + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + } + break; + case VT_DOUBLE: + { + double prec = mtr->get_precision (); + hist_data->total->value[mind].tag = vtype; + hist_data->total->value[mind].d = hist_data->total->value[mind].ll / prec; + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + hi->value[mind].d = hi->value[mind].ll / prec; + } + break; + } + default: + if (mtr->get_subtype () != Metric::STATIC) + abort (); + break; + } + } + } + hist_data->sort (sort_ind, rev_sort); + hist_data->compute_minmax (); + theApplication->set_progress (0, NTXT ("")); + return hist_data; +} + + +// generate annotated structure info for data_layout +// note: similar data traversal found in er_print_histogram::dump_detail() +Hist_data * +DataSpace::get_layout_data (Hist_data *sorted_data, + Vector<int> *marks, int /* _threshold */) +{ + Hist_data *data_items = NULL; + Hist_data::HistItem *new_item; + MetricList *mlist = new MetricList (sorted_data->get_metric_list ()); + int no_metrics = mlist->get_items ()->size (); + int index, addr_index = -1, name_index = -1; + Dprintf (DEBUG_DATAOBJ, NTXT ("DataSpace::get_layout_data(ALL)\n")); + + // Allocate a new Hist_data for the list, to be copied from the DataObect list + data_items = new Hist_data (mlist, Histable::DOBJECT, Hist_data::MODL); + data_items->set_status (sorted_data->get_status ()); + + // suppress threshold setting + // XXX this threshold should probably not be used + sorted_data->set_threshold ((double) 75. / 100.0); + TValue* all_empty = new TValue[no_metrics]; + memset (all_empty, 0, sizeof (TValue) * no_metrics); + + Metric *mitem; + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + // new data items have same total as original items + data_items->total->value[index] = sorted_data->total->value[index]; + // empty metric items need matching types + all_empty[index].tag = mitem->get_vtype (); + if (mitem->get_type () == Metric::ONAME) name_index = index; + if (mitem->get_type () == Metric::ADDRESS) addr_index = index; + } + + int64_t next_elem_offset = 0; + for (long i = 0; i < sorted_data->size (); i++) + { + Hist_data::HistItem* ditem = sorted_data->fetch (i); + DataObject* dobj = (DataObject*) (ditem->obj); + if (!dobj->get_parent ()) + { // doesn't have a parent; top level item + next_elem_offset = 0; + if (i > 0) + { // add a blank line as separator + // fixme xxxxx, is it really ok to create a DataObject just for this? + DataObject* empty = new DataObject (); + empty->size = 0; + empty->offset = 0; + empty->set_name (NTXT ("")); + new_item = sorted_data->new_hist_item (empty, Module::AT_EMPTY, all_empty); + new_item->value[name_index].tag = VT_LABEL; + new_item->value[name_index].l = NULL; + data_items->append_hist_item (new_item); + } + // then add the aggregate + new_item = sorted_data->new_hist_item (dobj, Module::AT_SRC, ditem->value); + new_item->value[name_index].tag = VT_OFFSET; + new_item->value[name_index].l = dbe_strdup (dobj->get_name ()); + data_items->append_hist_item (new_item); + } + else + { // is a child + if (dobj->get_parent ()->get_typename ()) + { // typed sub-element that has offset + if (dobj->offset > next_elem_offset) + { // filler entry + // hole in offsets + // fixme xxxxx, is it really ok to create a DataObject just for this? + DataObject* filler = new DataObject (); + filler->set_name (PTXT (DOBJ_ANON)); + filler->size = (dobj->offset - next_elem_offset); + filler->offset = next_elem_offset; + new_item = sorted_data->new_hist_item (filler, Module::AT_EMPTY, all_empty); + new_item->value[name_index].tag = VT_OFFSET; + new_item->value[name_index].l = dbe_strdup (filler->get_offset_name ()); + if (addr_index >= 0) + { + new_item->value[addr_index].tag = VT_ADDRESS; + new_item->value[addr_index].ll = (dobj->get_addr () - filler->size); + } + data_items->append_hist_item (new_item); + } + next_elem_offset = dobj->offset + dobj->size; + } + // then add the aggregate's subelement + if (marks) + if (sorted_data->above_threshold (ditem)) + marks->append (data_items->size ()); + new_item = sorted_data->new_hist_item (dobj, Module::AT_DIS, ditem->value); + new_item->value[name_index].tag = VT_OFFSET; + new_item->value[name_index].l = dbe_strdup (dobj->get_offset_name ()); + data_items->append_hist_item (new_item); + } + } + delete[] all_empty; + return data_items; +} diff --git a/gprofng/src/DataSpace.h b/gprofng/src/DataSpace.h new file mode 100644 index 0000000..c4d80fb --- /dev/null +++ b/gprofng/src/DataSpace.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DATASPACE_H +#define _DATASPACE_H + +#include <stdio.h> + +#include "dbe_structs.h" +#include "vec.h" +#include "Exp_Layout.h" +#include "Hist_data.h" +#include "Histable.h" +#include "Metric.h" + +class DbeView; +class DataView; + +class DataSpace +{ +public: + DataSpace (DbeView *_dbev, int picked = 0); + ~DataSpace (); + void reset (); + Hist_data *compute_metrics (MetricList *, Histable::Type, + Hist_data::Mode, Histable*); + Hist_data *get_layout_data (Hist_data *sorted_data, Vector<int> *marks, + int threshold); + + static char *status_str (); + +private: + Histable *get_hist_obj (Histable::Type, DataView*, long); + + DbeView *dbev; +}; + +#endif /* _DATASPACE_H */ diff --git a/gprofng/src/DataStream.cc b/gprofng/src/DataStream.cc new file mode 100644 index 0000000..8e244e2 --- /dev/null +++ b/gprofng/src/DataStream.cc @@ -0,0 +1,55 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "util.h" +#include "DataStream.h" +#include "debug.h" + +DataStream::DataStream (char *filename) : Data_window (filename) +{ + set_span (0, -1); +} + +DataStream::~DataStream () { } + +void +DataStream::set_span (int64_t f_offset, int64_t sz) +{ + span_offset = 0; + span_fileoffset = f_offset; + int64_t fsz = get_fsize (); + span_size = sz == -1 ? fsz : sz; + if (span_fileoffset >= fsz) + span_fileoffset = fsz; + if (span_size > fsz - span_fileoffset) + span_size = fsz - span_fileoffset; +} + +int64_t +DataStream::read (void *buf, int64_t len) +{ + if (len > span_size - span_offset) + len = span_size - span_offset; + int64_t off = span_offset + span_fileoffset; + span_offset += len; + get_data (off, len, buf); + return len; +} diff --git a/gprofng/src/DataStream.h b/gprofng/src/DataStream.h new file mode 100644 index 0000000..59ee293 --- /dev/null +++ b/gprofng/src/DataStream.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DATASTREAM_H +#define _DATASTREAM_H + +#include "Data_window.h" + +// sequential access to the file +class DataStream : public Data_window +{ +public: + // Create an empty data window. + DataStream (char *file_name); + ~DataStream (); + void set_span (int64_t f_offset, int64_t sz); + int64_t read (void *buf, int64_t len); + + template <typename Key_t> inline int64_t + read (Key_t &val) + { + int64_t sz = read (&val, sizeof (val)); + if (need_swap_endian && sz == sizeof (val)) + swapByteOrder (&val, sizeof (val)); + return sz; + } + +private: + int64_t span_offset; + int64_t span_size; // the window size + int64_t span_fileoffset; // the window begin on the file +}; + +#endif /* _DATASTREAM_H */ diff --git a/gprofng/src/Data_window.cc b/gprofng/src/Data_window.cc new file mode 100644 index 0000000..d9b0067 --- /dev/null +++ b/gprofng/src/Data_window.cc @@ -0,0 +1,241 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <sys/types.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <unistd.h> // for close(); + +#include "util.h" +#include "Data_window.h" +#include "debug.h" + +enum +{ + MINBUFSIZE = 1 << 16, + WIN_ALIGN = 8 +}; + +Data_window::Data_window (char *file_name) +{ + Dprintf (DEBUG_DATA_WINDOW, NTXT ("Data_window:%d %s\n"), (int) __LINE__, STR (file_name)); + page_size = sysconf (_SC_PAGESIZE); + need_swap_endian = false; + opened = false; + fsize = 0; + base = NULL; + woffset = 0; + wsize = 0; + basesize = 0; + fname = dbe_strdup (file_name); + mmap_on_file = false; + use_mmap = false; +#if DEBUG + if (DBE_USE_MMAP) + use_mmap = true; +#endif /* DEBUG */ + fd = open64 (fname, O_RDONLY); + if (fd == -1) + return; + fsize = lseek (fd, 0, SEEK_END); + if (fsize == 0) + { + close (fd); + fd = -1; + return; + } + opened = true; + if (use_mmap) + { + if (fsize != -1) + { + base = (void*) mmap (NULL, (size_t) fsize, PROT_READ, MAP_PRIVATE, fd, 0); + close (fd); + fd = -1; + if (base == MAP_FAILED) + { + base = NULL; + use_mmap = false; + return; + } + mmap_on_file = true; + wsize = fsize; + } + } +} + +void * +Data_window::bind (int64_t file_offset, int64_t minSize) +{ + Span span; + span.length = fsize - file_offset; + span.offset = file_offset; + return bind (&span, minSize); +} + +void * +Data_window::bind (Span *span, int64_t minSize) +{ + // Do any necessary mapping to access the desired span of data + // and return a pointer to the first byte. + Dprintf (DEBUG_DATA_WINDOW, NTXT ("Data_window:bind:%d offset=%llx:%lld minSize=%lld \n"), + (int) __LINE__, (long long) span->offset, (long long) span->length, (long long) minSize); + if (minSize == 0 || span->length < minSize) + return NULL; + + if (span->offset < woffset || span->offset + minSize > woffset + wsize) + { + // Remap the window + if (span->offset + minSize > fsize) + return NULL; + int myfd = fd; + if (myfd == -1) + { + if (fname) + myfd = open64 (fname, O_RDONLY, 0); + if (myfd == -1) + return NULL; + } + bool remap_failed = true; + if (use_mmap) + { + if (base) + { + munmap ((caddr_t) base, (size_t) wsize); + base = NULL; + } + woffset = span->offset & ~(page_size - 1); + wsize = page_size * ((MINBUFSIZE + page_size - 1) / page_size); + if (span->offset + minSize > woffset + wsize) + // Extend a window + wsize += page_size * ((span->offset + minSize - + woffset - wsize + page_size - 1) / page_size); + base = (void *) mmap (0, (size_t) wsize, PROT_READ, MAP_SHARED, fd, woffset); + if (base == MAP_FAILED) + { + base = NULL; + use_mmap = false; + } + remap_failed = (base == NULL); + } + if (remap_failed) + { + remap_failed = false; + woffset = span->offset & ~(WIN_ALIGN - 1); + wsize = minSize + (span->offset % WIN_ALIGN); + if (wsize < MINBUFSIZE) + wsize = MINBUFSIZE; + if (wsize > fsize) + wsize = fsize; + if (basesize < wsize) + { // Need to realloc 'base' + free (base); + basesize = wsize; + base = (void *) malloc (basesize); + Dprintf (DEBUG_DATA_WINDOW, + NTXT ("Data_window:bind:%d realloc basesize=%llx woffset=%lld \n"), + (int) __LINE__, (long long) basesize, (long long) woffset); + if (base == NULL) + { + basesize = 0; + remap_failed = true; + } + } + if (wsize > fsize - woffset) + wsize = fsize - woffset; + off_t woff = (off_t) woffset; + if (base == NULL || woff != lseek (myfd, woff, SEEK_SET) + || wsize != read_from_file (myfd, base, wsize)) + remap_failed = true; + } + if (fd == -1) + close (myfd); + if (remap_failed) + { + woffset = 0; + wsize = 0; + return NULL; + } + } + return (void *) ((char*) base + span->offset - woffset); +} + +void * +Data_window::get_data (int64_t offset, int64_t size, void *datap) +{ + if (size <= 0) + return NULL; + void *buf = bind (offset, size); + if (buf == NULL) + return NULL; + if (datap == NULL && !mmap_on_file) + // Can be remmaped or reallocated. Need to make a copy + datap = (void *) malloc (size); + if (datap) + { + memcpy (datap, buf, (size_t) size); + return datap; + } + return buf; +} + +Data_window::~Data_window () +{ + free (fname); + if (fd != -1) + close (fd); + if (base) + { + if (use_mmap) + munmap ((caddr_t) base, (size_t) wsize); + else + free (base); + } +} + +int64_t +Data_window::get_buf_size () +{ + int64_t sz = MINBUFSIZE; + if (sz < basesize) + sz = basesize; + if (sz > fsize) + sz = fsize; + return sz; +} + +int64_t +Data_window::copy_to_file (int f, int64_t offset, int64_t size) +{ + long long bsz = get_buf_size (); + for (long long n = 0; n < size;) + { + long long sz = (bsz <= (size - n)) ? bsz : (size - n); + void *b = bind (offset + n, sz); + if (b == NULL) + return n; + long long len = write (f, b, sz); + if (len <= 0) + return n; + n += len; + } + return size; +} diff --git a/gprofng/src/Data_window.h b/gprofng/src/Data_window.h new file mode 100644 index 0000000..a3e98c0 --- /dev/null +++ b/gprofng/src/Data_window.h @@ -0,0 +1,99 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DATA_WINDOW_H +#define _DATA_WINDOW_H + +// The Data_window class hiearchy is used to access raw data in the +// experiment record. +// +// The Data_window base class implements a set of windows into a raw data file. +// It is responsible for mapping and unmapping regions of the file as +// requested by other levels inside of the DBE. + +#include "util.h" + +class Data_window +{ +public: + + // Span in data file + typedef struct + { + int64_t offset; // file offset + int64_t length; // span length + } Span; + + Data_window (char *filename); + ~Data_window (); + + // Return address of "offset" byte of window for "length" bytes. + // Return 0 on error or locked. + void *bind (Span *span, int64_t minSize); + void *bind (int64_t file_offset, int64_t minSize); + void *get_data (int64_t offset, int64_t size, void *datap); + int64_t get_buf_size (); + int64_t copy_to_file (int f, int64_t offset, int64_t size); + + bool not_opened () { return !opened; } + off64_t get_fsize () { return fsize; } + + template <typename Key_t> inline Key_t + get_align_val (Key_t *vp) + { + if (sizeof (Key_t) <= sizeof (int)) + return *vp; + // 64-bit value can have a wrong alignment + Key_t val = (Key_t) 0; + uint32_t *p1 = (uint32_t *) vp; + uint32_t *p2 = (uint32_t*) (&val); + p2[0] = p1[0]; + p2[1] = p1[1]; + return val; + } + + template <typename Key_t> inline Key_t + decode (Key_t &v) + { + Key_t val = get_align_val (&v); + if (need_swap_endian) + swapByteOrder (&val, sizeof (val)); + return val; + } + + bool need_swap_endian; + char *fname; // file name + +protected: + int fd; // file descriptor + bool mmap_on_file; + +private: + long page_size; // used in mmap() + bool use_mmap; + bool opened; + int64_t fsize; // file size + void *base; // current window + int64_t woffset; // offset of current window + int64_t wsize; // size of current window + int64_t basesize; // size of allocated window +}; + +#endif /* _DATA_WINDOW_H */ diff --git a/gprofng/src/Dbe.cc b/gprofng/src/Dbe.cc new file mode 100644 index 0000000..1a6e521 --- /dev/null +++ b/gprofng/src/Dbe.cc @@ -0,0 +1,10371 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <errno.h> +#include <sys/types.h> // open, chmod +#include <signal.h> +#include <fcntl.h> // open +#include <strings.h> +#include <unistd.h> + +#include "util.h" +#include "Histable.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "BaseMetric.h" +#include "CallStack.h" +#include "collctrl.h" +#include "Command.h" +#include "Dbe.h" +#include "DbeApplication.h" +#include "DefaultMap.h" +#include "LoadObject.h" +#include "Experiment.h" +#include "IndexObject.h" +#include "IOActivity.h" +#include "PreviewExp.h" +#include "Function.h" +#include "Hist_data.h" +#include "MetricList.h" +#include "Module.h" +#include "DataSpace.h" +#include "MemorySpace.h" +#include "DataObject.h" +#include "MemObject.h" +#include "Filter.h" +#include "FilterSet.h" +#include "FilterExp.h" +#include "Sample.h" +#include "Print.h" +#include "StringBuilder.h" +#include "dbe_types.h" +#include "ExpGroup.h" +#include "vec.h" +#include "UserLabel.h" +#include "DbeFile.h" +#include "PathTree.h" + +// Data structures for managing the collector control info for Collection GUI +static Coll_Ctrl *col_ctr = NULL; + +template<> VecType Vector<int>::type () +{ + return VEC_INTEGER; +} + +template<> VecType Vector<unsigned>::type () +{ + return VEC_INTEGER; +} + +template<> VecType Vector<char>::type () +{ + return VEC_CHAR; +} + +template<> VecType Vector<bool>::type () +{ + return VEC_BOOL; +} + +template<> VecType Vector<double>::type () +{ + return VEC_DOUBLE; +} + +template<> VecType Vector<long long>::type () +{ + return VEC_LLONG; +} + +template<> VecType Vector<uint64_t>::type () +{ + return VEC_LLONG; +} + +template<> VecType Vector<void*>::type () +{ + return VEC_VOIDARR; +} + +template<> VecType Vector<char*>::type () +{ + return VEC_STRING; +} + +template<> VecType Vector<Vector<int>*>::type () +{ + return VEC_INTARR; +} + +template<> VecType Vector<Vector<char*>*>::type () +{ + return VEC_STRINGARR; +} + +template<> VecType Vector<Vector<long long>*>::type () +{ + return VEC_LLONGARR; +} + +// gcc won't instantiate Vector<unsigned>::type() without it +Vector<unsigned> __dummy_unsigned_vector; + +#define CASE_S(x) case x: return #x +static const char * +dsp_type_to_string (int t) +{ + switch (t) + { + CASE_S (DSP_FUNCTION); + CASE_S (DSP_LINE); + CASE_S (DSP_PC); + CASE_S (DSP_SOURCE); + CASE_S (DSP_DISASM); + CASE_S (DSP_SELF); + CASE_S (DSP_CALLER); + CASE_S (DSP_CALLEE); + CASE_S (DSP_CALLTREE); + CASE_S (DSP_TIMELINE); + CASE_S (DSP_STATIS); + CASE_S (DSP_EXP); + CASE_S (DSP_LEAKLIST); + CASE_S (DSP_MEMOBJ); + CASE_S (DSP_DATAOBJ); + CASE_S (DSP_DLAYOUT); + CASE_S (DSP_SRC_FILE); + CASE_S (DSP_IFREQ); + CASE_S (DSP_RACES); + CASE_S (DSP_INDXOBJ); + CASE_S (DSP_DUALSOURCE); + CASE_S (DSP_SOURCE_DISASM); + CASE_S (DSP_DEADLOCKS); + CASE_S (DSP_SOURCE_V2); + CASE_S (DSP_DISASM_V2); + CASE_S (DSP_IOACTIVITY); + CASE_S (DSP_OVERVIEW); + CASE_S (DSP_IOCALLSTACK); + CASE_S (DSP_HEAPCALLSTACK); + CASE_S (DSP_SAMPLE); + default: + break; + } + return NTXT ("ERROR"); +} + +enum +{ + COMPARE_BIT = 1 << 8, + MTYPE_MASK = (1 << 8) - 1, + GROUP_ID_SHIFT = 16 +}; + +static DbeView * +getDbeView (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return dbev; +} + + +Vector<char*> * +dbeGetInitMessages () +{ + // If any comments from the .rc files, send them to the GUI + Emsg *msg = theDbeApplication->fetch_comments (); + int size = 0; + while (msg != NULL) + { + size++; + msg = msg->next; + } + + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(size); + msg = theDbeApplication->fetch_comments (); + size = 0; + int i = 0; + while (msg != NULL) + { + char *str = msg->get_msg (); + list->store (i, dbe_strdup (str)); + i++; + msg = msg->next; + } + + // now delete the comments + theDbeApplication->delete_comments (); + return list; +} + +Vector<char*> * +dbeGetExpPreview (int /*dbevindex*/, char *exp_name) +{ + PreviewExp *preview = new PreviewExp (); + preview->experiment_open (exp_name); + preview->open_epilogue (); + + // Initialize Java String array + Vector<char*> *info = preview->preview_info (); + int size = info->size (); + Vector<char*> *list = new Vector<char*>(size); + + // Get experiment names + for (int i = 0; i < size; i++) + { + char *str = info->fetch (i); + if (str == NULL) + str = GTXT ("N/A"); + list->store (i, dbe_strdup (str)); + } + delete info; + delete preview; + return list; +} + +char * +dbeGetExpParams (int /*dbevindex*/, char *exp_name) +{ + PreviewExp *preview = new PreviewExp (); + preview->experiment_open (exp_name); + + // Initialize Java String array + char *arg_list = dbe_strdup (preview->getArgList ()); + delete preview; + return arg_list; +} + +/** + * Gets File Attributes according to the specified format + * Supported formats: + * "/bin/ls -dl " - see 'man ls' for details + * @param filename + * @param format + * @return char * attributes + */ +char * +dbeGetFileAttributes (const char *filename, const char *format) +{ + if (format != NULL) + { + if (!strcmp (format, NTXT ("/bin/ls -dl "))) + { + // A kind of "/bin/ls -dl " simulation + struct stat64 sbuf; + sbuf.st_mode = 0; + dbe_stat (filename, &sbuf); + if (S_IREAD & sbuf.st_mode) + { // Readable + if (S_ISDIR (sbuf.st_mode) != 0) + return dbe_sprintf (NTXT ("%s %s\n"), NTXT ("drwxrwxr-x"), filename); + else if (S_ISREG (sbuf.st_mode) != 0) + return dbe_sprintf (NTXT ("%s %s\n"), NTXT ("-rwxrwxr-x"), filename); + } + } + } + return dbe_strdup (NTXT ("")); +} + +/** + * Gets list of files for specified directory according to the specified format + * Supported formats: + * "/bin/ls -a" - see 'man ls' for details + * "/bin/ls -aF" - see 'man ls' for details + * @param dirname + * @param format + * @return char * files + */ +char * +dbeGetFiles (const char *dirname, const char *format) +{ + if (format != NULL) + return dbe_read_dir (dirname, format); + return dbe_strdup (NTXT ("")); +} + +/** + * Creates the directory named by this full path name, including any + * necessary but nonexistent parent directories. + * @param dirname + * @return result + */ +char * +dbeCreateDirectories (const char *dirname) +{ + if (dirname != NULL) + { + char *res = dbe_create_directories (dirname); + if (res != NULL) + return res; + } + return dbe_strdup (NTXT ("")); +} + +/** + * Deletes the file or the directory named by the specified path name. + * If this pathname denotes a directory, then the directory must be empty in order to be deleted. + * @param const char *pathname + * @return int result + */ +char * +dbeDeleteFile (const char *pathname) +{ + // return unlink(pathname); + if (pathname != NULL) + { + char *res = dbe_delete_file (pathname); + if (res != NULL) + return res; + } + return dbe_strdup (NTXT ("")); +} + +/** + * Reads the file named by the specified path name. + * Temporary limitation: file should be "text only" and its size should be less than the 1 MB limit. + * If the operation was successful, the contents is in the first element, and second element is NULL. + * If the operation failed, then first element is NULL, and second element contains the error message. + * @param const char *pathname + * @return Vector<char*> *result + */ +Vector<char*> * +dbeReadFile (const char *pathname) +{ + Vector<char*> *result = new Vector<char*>(2); + int limit = 1024 * 1024; // Temporary limit: 1 MB + char * contents = (char *) malloc (limit); + StringBuilder sb; + if (NULL == contents) + { + sb.sprintf (NTXT ("\nError: Cannot allocate %d bytes\n"), limit); + result->store (0, NULL); + result->store (1, sb.toString ()); // failure + return result; + } + int fd = open (pathname, O_RDONLY); + if (fd >= 0) + { + int64_t bytes = read_from_file (fd, contents, limit); + close (fd); + if (bytes >= limit) + { + sb.sprintf (NTXT ("\nError: file size is greater than the limit (%d bytes)\n"), limit); + result->store (0, NULL); + result->store (1, sb.toString ()); // failure + } + else + { + contents[bytes] = '\0'; // add string terminator + result->store (0, contents); + result->store (1, NULL); // success + } + } + else + { + sb.sprintf (NTXT ("\nError: Cannot open file %s\n"), pathname); + result->store (0, NULL); + result->store (1, sb.toString ()); // failure + free (contents); + } + return result; +} + +/** + * Writes the file named by the specified path name. + * Temporary limitation: file should be "text only" and its size should be less than the 1 MB limit. + * If the operation failed, then -1 is returned. + * @param const char *pathname + * @return int result (written bytes) + */ +int +dbeWriteFile (const char *pathname, const char *contents) +{ + int result = -1; // error + size_t len = 0; + if (NULL != contents) + len = strlen (contents); + size_t limit = 1024 * 1024; // Temporary limit: 1 MB + if (len > limit) return result; + unlink (pathname); + mode_t mode = S_IRUSR | S_IWUSR; + int fd = open (pathname, O_WRONLY | O_CREAT | O_TRUNC, mode); + if (fd >= 0) + { // replace file contents + chmod (pathname, /*S_IRUSR || S_IWUSR*/ 0600); // rw for owner only + ssize_t bytes = 0; + if (len > 0) + bytes = write (fd, contents, len); + close (fd); + result = (int) bytes; + } + return result; +} + +/** + * Gets list of running processes according to the specified format + * Supported formats: + * "/bin/ps -ef" - see 'man ps' for details + * @param format + * @return char * processes + */ +char * +dbeGetRunningProcesses (const char *format) +{ + if (format != NULL) + return dbe_get_processes (format); + return dbe_strdup (NTXT ("")); +} + +// +// Open experiment +// +char * +dbeOpenExperimentList (int /* dbevindex */, Vector<Vector<char*>*> *groups, + bool sessionRestart) +{ + if (sessionRestart) + dbeSession->reset (); + char *errstr; + // Open experiments + try + { + errstr = dbeSession->setExperimentsGroups (groups); + } + catch (ExperimentLoadCancelException *) + { + errstr = dbe_strdup (NTXT ("Experiment Load Cancelled")); + } + return errstr; +} + +// +// Drop experiments +// +char * +dbeDropExperiment (int /* dbevindex */, Vector<int> *drop_index) +{ + for (int i = drop_index->size () - 1; i >= 0; i--) + { + char *ret = dbeSession->drop_experiment (drop_index->fetch (i)); + if (ret != NULL) + return ret; + } + return NULL; +} + +/** + * Read .er.rc file from the specified location + * @param path + * @return + */ +char * +dbeReadRCFile (int dbevindex, char* path) +{ + DbeView *dbev = getDbeView (dbevindex); + char *err_msg = dbev->get_settings ()->read_rc (path); + return err_msg; +} + +char * +dbeSetExperimentsGroups (Vector<Vector<char*>*> *groups) +{ + int cmp_mode = dbeSession->get_settings ()->get_compare_mode (); + if (groups->size () < 2) + cmp_mode = CMP_DISABLE; + else if (cmp_mode == CMP_DISABLE) + cmp_mode = CMP_ENABLE; + for (int i = 0;; i++) + { + DbeView *dbev = dbeSession->getView (i); + if (dbev == NULL) + break; + dbev->get_settings ()->set_compare_mode (cmp_mode); + } + char *err_msg = dbeSession->setExperimentsGroups (groups); + + // automatically load machine model if applicable + dbeDetectLoadMachineModel (0); + return err_msg; +} + +Vector<Vector<char*>*> * +dbeGetExperimensGroups () +{ + Vector<Vector<char*>*> *grops = dbeSession->getExperimensGroups (); + return grops; +} + +Vector<int> * +dbeGetFounderExpId (Vector<int> *expIds) +{ + Vector<int> *ret = new Vector<int>(expIds->size ()); + for (int i = 0; i < expIds->size (); i++) + { + int expId = expIds->fetch (i); + Experiment *exp = dbeSession->get_exp (expId); + if (exp != NULL) + { + int founderExpId = exp->getBaseFounder ()->getExpIdx (); + ret->store (i, founderExpId); + } + else + ret->store (i, -1); + } + return ret; +} + +Vector<int> * +dbeGetUserExpId (Vector<int> *expIds) +{ + // returns "User Visible" ids used for EXPID filters and timeline processes + Vector<int> *ret = new Vector<int>(expIds->size ()); + for (int i = 0; i < expIds->size (); i++) + { + int expId = expIds->fetch (i); + Experiment *exp = dbeSession->get_exp (expId); + if (exp != NULL) + { + int userExpId = exp->getUserExpId (); + ret->store (i, userExpId); + } + else + ret->store (i, -1); + } + return ret; +} + +// +// Get experiment groupid +// +Vector<int> * +dbeGetExpGroupId (Vector<int> *expIds) +{ + Vector<int> *ret = new Vector<int>(expIds->size ()); + for (int i = 0; i < expIds->size (); i++) + { + int expId = expIds->fetch (i); + Experiment *exp = dbeSession->get_exp (expId); + if (exp != NULL) + { + int gId = exp->groupId; + ret->store (i, gId); + } + else + ret->store (i, -1); + } + return ret; +} + +Vector<char*> * +dbeGetExpsProperty (const char *prop_name) +{ + long nexps = dbeSession->nexps (); + if (prop_name == NULL || nexps == 0) + return NULL; + Vector<char*> *list = new Vector<char*>(nexps); + StringBuilder sb; + int empty = 1; + int prop = 99; + if (strcasecmp (prop_name, NTXT ("ERRORS")) == 0) + prop = 1; + else if (strcasecmp (prop_name, NTXT ("WARNINGS")) == 0) + prop = 2; + if (prop < 3) + { + for (long i = 0; i < nexps; i++) + { + Experiment *exp = dbeSession->get_exp (i); + char *nm = exp->get_expt_name (); + sb.setLength (0); + for (Emsg *emsg = (prop == 1) ? exp->fetch_errors () : exp->fetch_warnings (); + emsg; emsg = emsg->next) + sb.appendf (NTXT ("%s: %s\n"), STR (nm), STR (emsg->get_msg ())); + char *s = NULL; + if (sb.length () > 0) + { + s = sb.toString (); + empty = 0; + } + list->append (s); + } + } + if (empty) + { + delete list; + list = NULL; + } + return list; +} + +// +// Get experiment names +// +Vector<char*> * +dbeGetExpName (int /*dbevindex*/) +{ + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(size); + + // Get experiment names + for (int i = 0; i < size; i++) + { + Experiment *texp = dbeSession->get_exp (i); + char *buf = dbe_sprintf (NTXT ("%s [%s]"), texp->get_expt_name (), + texp->utargname != NULL ? texp->utargname : GTXT ("(unknown)")); + list->store (i, buf); + } + return list; +} + +// +// Get experiment state +// +Vector<int> * +dbeGetExpState (int /* dbevindex */) +{ + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + // Initialize Java array + Vector<int> *state = new Vector<int>(size); + + // Get experiment state + for (int i = 0; i < size; i++) + { + Experiment *exp = dbeSession->get_exp (i); + int set = EXP_SUCCESS; + if (exp->get_status () == Experiment::FAILURE) + set |= EXP_FAILURE; + if (exp->get_status () == Experiment::INCOMPLETE) + set |= EXP_INCOMPLETE; + if (exp->broken) + set |= EXP_BROKEN; + if (exp->obsolete) + set |= EXP_OBSOLETE; + state->store (i, set); + } + return state; +} + +// +// Get enabled experiment indices +// +Vector<bool> * +dbeGetExpEnable (int dbevindex) +{ + DbeView *dbev = getDbeView (dbevindex); + int size = dbeSession->nexps (); + if (dbev == NULL || size == 0) + return NULL; + + // Get enabled experiment + Vector<bool> *enable = new Vector<bool>(size); + for (int i = 0; i < size; i++) + { + bool val = dbev->get_exp_enable (i) && !dbeSession->get_exp (i)->broken; + enable->store (i, val); + } + return enable; +} + +// +// Get enabled experiment indices +// +bool +dbeSetExpEnable (int dbevindex, Vector<bool> *enable) +{ + DbeView *dbev = getDbeView (dbevindex); + bool ret = false; + int size = dbeSession->nexps (); + if (dbev == NULL || size == 0) + return false; + + // set enable, as per input vector + for (int i = 0; i < size; i++) + if (!dbeSession->get_exp (i)->broken + && dbev->get_exp_enable (i) != enable->fetch (i)) + { + dbev->set_exp_enable (i, enable->fetch (i)); + ret = true; + } + return ret; +} + +// +// Get experiment info +// +Vector<char*> * +dbeGetExpInfo (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(size * 2 + 1); + + // Get experiment names + Vector<LoadObject*> *text_segments = dbeSession->get_text_segments (); + char *msg = pr_load_objects (text_segments, NTXT ("")); + delete text_segments; + list->store (0, msg); + int k = 1; + for (int i = 0; i < size; i++) + { + Experiment *exp = dbeSession->get_exp (i); + char *msg0 = pr_mesgs (exp->fetch_notes (), NTXT (""), NTXT ("")); + char *msg1 = pr_mesgs (exp->fetch_errors (), GTXT ("No errors\n"), NTXT ("")); + char *msg2 = pr_mesgs (exp->fetch_warnings (), GTXT ("No warnings\n"), NTXT ("")); + char *msg3 = pr_mesgs (exp->fetch_comments (), NTXT (""), NTXT ("")); + char *msg4 = pr_mesgs (exp->fetch_pprocq (), NTXT (""), NTXT ("")); + msg = dbe_sprintf (NTXT ("%s%s%s%s"), msg1, msg2, msg3, msg4); + list->store (k++, msg0); + list->store (k++, msg); + free (msg1); + free (msg2); + free (msg3); + free (msg4); + } + return list; +} + +bool +dbeGetViewModeEnable () +{ + return dbeSession->has_ompavail () || dbeSession->has_java (); +} + +bool +dbeGetJavaEnable () +{ + return dbeSession->has_java (); +} + +int +dbeUpdateNotes (int dbevindex, int exp_id, int type, char* text, bool handle_file) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + int size = dbeSession->nexps (); + if (size == 0) + return -1; + Experiment *exp = dbeSession->get_exp (exp_id); + return (type == 0) ? exp->save_notes (text, handle_file) : exp->delete_notes (handle_file); +} + +// +// Get load object names +// +Vector<char*> * +dbeGetLoadObjectName (int /* dbevindex */) +{ + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + int size = lobjs->size (); + + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(size); + + // Get load object names + LoadObject *lo; + int index; + Vec_loop (LoadObject*, lobjs, index, lo) + { + list->store (index, dbe_strdup (lo->get_name ())); + } + delete lobjs; + return list; +} + +// XXX Will use later when order has to be passed too, +// Get complete List of tabs +// +Vector<void*> * +dbeGetTabList (int /* dbevindex */) +{ + //DbeView *dbev = getDbeView (dbevindex); + //Vector<void*> *tabs = dbeSession->get_TabList(); + //return tabs; + return NULL; +} + +// +// Returns list of available tabs +// +Vector<void*> * +dbeGetTabListInfo (int dbevindex) +{ + int index; + DispTab *dsptab; + DbeView *dbev = getDbeView (dbevindex); + + // make sure the tabs are initialized properly + dbev->get_settings ()->proc_tabs (theDbeApplication->rdtMode); + Vector<DispTab*> *tabs = dbev->get_TabList (); + + // Get number of available tabs + int size = 0; + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + size++; + } + Vector<void*> *data = new Vector<void*>(2); + Vector<int> *typelist = new Vector<int>(size); + Vector<char*> *cmdlist = new Vector<char*>(size); + Vector<int> *ordlist = new Vector<int>(size); + + // Build list of avaliable tabs + int i = 0; + + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + typelist->store (i, dsptab->type); + cmdlist->store (i, dbe_strdup (Command::get_cmd_str (dsptab->cmdtoken))); + ordlist->store (i, dsptab->order); + i++; + } + data->store (0, typelist); + data->store (1, cmdlist); + data->store (2, ordlist); + return data; +} + +// Return visibility state for all available tabs +// +Vector<bool> * +dbeGetTabSelectionState (int dbevindex) +{ + int index; + DispTab *dsptab; + DbeView *dbev = getDbeView (dbevindex); + Vector<DispTab*> *tabs = dbev->get_TabList (); + + // Get number of available tabs + int size = 0; + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + size++; + } + Vector<bool> *states = new Vector<bool>(size); + + // Get visibility bit for all available tabs + int i = 0; + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + states->store (i++, dsptab->visible); + } + return states; +} + +// Set visibility bit for a tab +void +dbeSetTabSelectionState (int dbevindex, Vector<bool> *selected) +{ + int index; + DispTab *dsptab; + DbeView *dbev = getDbeView (dbevindex); + Vector<DispTab*> *tabs = dbev->get_TabList (); + int i = 0; + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + dsptab->visible = selected->fetch (i++); + } +} + +// Return visibility state for all available MemObj tabs +Vector<bool> * +dbeGetMemTabSelectionState (int dbevindex) +{ + int index; + bool dsptab; + DbeView *dbev = getDbeView (dbevindex); + Vector<bool> *memtabs = dbev->get_MemTabState (); + + // set the output vector + int size = memtabs->size (); + Vector<bool> *states = new Vector<bool>(size); + + // Get visibility bit for all available tabs + int i = 0; + Vec_loop (bool, memtabs, index, dsptab) + { + states->store (i++, dsptab); + } + return states; +} + +// Set visibility bit for a memory tab +// +void +dbeSetMemTabSelectionState (int dbevindex, Vector<bool> *selected) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_MemTabState (selected); +} + +// Return visibility state for all available index tabs +Vector<bool> * +dbeGetIndxTabSelectionState (int dbevindex) +{ + int index; + bool dsptab; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<bool> *indxtabs = dbev->get_IndxTabState (); + + // set the output vector + int size = indxtabs->size (); + Vector<bool> *states = new Vector<bool>(size); + + // Get visibility bit for all available tabs + int i = 0; + Vec_loop (bool, indxtabs, index, dsptab) + { + states->store (i++, dsptab); + } + return states; +} + +// Set visibility bit for a index tab +void +dbeSetIndxTabSelectionState (int dbevindex, Vector<bool> *selected) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_IndxTabState (selected); +} + +// +// Get search path +// +Vector<char*> * +dbeGetSearchPath (int /*dbevindex*/) +{ + Vector<char*> *path = dbeSession->get_search_path (); + int size = path->size (); + Vector<char*> *list = new Vector<char*>(size); + int index; + char *name; + Vec_loop (char*, path, index, name) + { + list->store (index, dbe_strdup (name)); + } + return list; +} + +// +// Set search path +// +void +dbeSetSearchPath (int /*dbevindex*/, Vector<char*> *path) +{ + dbeSession->set_search_path (path, true); + return; +} + +// +// Get pathmaps +// +Vector<void*> * +dbeGetPathmaps (int /*dbevindex*/) +{ + int index; + pathmap_t *pthmap; + Vector<pathmap_t*> *path = dbeSession->get_pathmaps (); + int size = path->size (); + Vector<void*> *data = new Vector<void*>(2); + Vector<char*> *oldlist = new Vector<char*>(size); + Vector<char*> *newlist = new Vector<char*>(size); + + int i = 0; + Vec_loop (pathmap_t*, path, index, pthmap) + { + oldlist->store (i, dbe_strdup (pthmap->old_prefix)); + newlist->store (i, dbe_strdup (pthmap->new_prefix)); + i++; + } + data->store (0, oldlist); + data->store (1, newlist); + return data; +} // dbeGetPathmaps + +char * +dbeSetPathmaps (Vector<char*> *from, Vector<char*> *to) +{ + if (from == NULL || to == NULL || from->size () != to->size ()) + return dbe_strdup ("dbeSetPathmaps: size of 'from' does not match for size of 'to'\n"); + Vector<pathmap_t*> *newPath = new Vector<pathmap_t*>(from->size ()); + for (int i = 0, sz = from->size (); i < sz; i++) + { + char *err = Settings::add_pathmap (newPath, from->get (i), to->get (i)); + if (err) + { + newPath->destroy (); + delete newPath; + return err; + } + } + dbeSession->set_pathmaps (newPath); + return NULL; +} + +// +// Add pathmap +char * +dbeAddPathmap (int /* dbevindex */, char *from, char *to) +{ + Vector<pathmap_t*> *pmp = dbeSession->get_pathmaps (); + char *err = Settings::add_pathmap (pmp, from, to); + return err; +} + +// +// Get error/warning string of data +char * +dbeGetMsg (int dbevindex, int type) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + char *msgstr = NULL; + if (type == ERROR_MSG) + msgstr = dbev->get_error_msg (); + else if (type == WARNING_MSG) + msgstr = dbev->get_warning_msg (); + else if (type == PSTAT_MSG) + msgstr = dbev->get_processor_msg (PSTAT_MSG); + else if (type == PWARN_MSG) + msgstr = dbev->get_processor_msg (PWARN_MSG); + return msgstr ? dbe_strdup (msgstr) : NULL; +} + +// Create a DbeView, given new index, and index of view to clone +int +dbeInitView (int id, int cloneid) +{ + return dbeSession->createView (id, cloneid); +} + + +// Delete a DbeView +void +dbeDeleteView (int dbevindex) +{ + dbeSession->dropView (dbevindex); + return; +} // dbeDeleteView + +MetricList * +dbeGetMetricListV2 (int dbevindex, MetricType mtype, + Vector<int> *type, Vector<int> *subtype, Vector<bool> *sort, + Vector<int> *vis, Vector<char*> *cmd, + Vector<char*> *expr_spec, Vector<char*> *legends) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + MetricList *mlist = new MetricList (mtype); + for (int i = 0, msize = type->size (); i < msize; i++) + { + BaseMetric *bm = dbev->register_metric_expr ((BaseMetric::Type) type->fetch (i), + cmd->fetch (i), + expr_spec->fetch (i)); + Metric *m = new Metric (bm, (Metric::SubType) subtype->fetch (i)); + m->set_raw_visbits (vis->fetch (i)); + if (m->legend == NULL) + m->legend = dbe_strdup (legends->fetch (i)); + mlist->append (m); + if (sort->fetch (i)) + { + mlist->set_sort_ref_index (i); + } + } + return mlist; +} + +static Vector<void*> * +dbeGetMetricList (MetricList *mlist) +{ + int clock_val = dbeSession->get_clock (-1); + Vector<Metric*> *items = mlist->get_items (); + int size = items->size (); + + Vector<int> *type = new Vector<int>(size); + Vector<int> *subtype = new Vector<int>(size); + Vector<int> *clock = new Vector<int>(size); + Vector<int> *flavors = new Vector<int>(size); + Vector<int> *vis = new Vector<int>(size); + Vector<bool> *sorted = new Vector<bool>(size); + Vector<int> *value_styles = new Vector<int>(size); + Vector<char*> *aux = new Vector<char*>(size); + Vector<char*> *name = new Vector<char*>(size); + Vector<char*> *abbr = new Vector<char*>(size); + Vector<char*> *comd = new Vector<char*>(size); + Vector<char*> *unit = new Vector<char*>(size); + Vector<char*> *user_name = new Vector<char*>(size); + Vector<char*> *expr_spec = new Vector<char*>(size); + Vector<char*> *legend = new Vector<char*>(size); + Vector<int> *valtype = new Vector<int>(size); + Vector<char*> *data_type_name = new Vector<char*>(size); + Vector<char*> *data_type_uname = new Vector<char*>(size); + Vector<char*> *short_desc = new Vector<char*>(size); + + int sort_index = mlist->get_sort_ref_index (); + // Fill metric elements + for (int i = 0; i < size; i++) + { + Metric *m = items->fetch (i); + type->append (m->get_type ()); + subtype->append (m->get_subtype ()); + flavors->append (m->get_flavors ()); + abbr->append (dbe_strdup (m->get_abbr ())); + char *s = m->get_abbr_unit (); + if ((m->get_visbits () & VAL_RATIO) != 0) + s = NULL; + unit->append (dbe_strdup (s ? s : NTXT (""))); + value_styles->append (m->get_value_styles ()); + vis->append (m->get_visbits ()); + sorted->append (i == sort_index); + clock->append (m->get_type () == Metric::HWCNTR ? clock_val + : m->get_clock_unit ()); + aux->append (dbe_strdup (m->get_aux ())); + name->append (dbe_strdup (m->get_name ())); + comd->append (dbe_strdup (m->get_cmd ())); + user_name->append (dbe_strdup (m->get_username ())); + expr_spec->append (dbe_strdup (m->get_expr_spec ())); + legend->append (dbe_strdup (m->legend)); + valtype->append (m->get_vtype2 ()); + + char* _data_type_name = NULL; + char* _data_type_uname = NULL; + int data_type = m->get_packet_type (); + if (data_type >= 0 && data_type < DATA_LAST) + { + _data_type_name = dbe_strdup (get_prof_data_type_name (data_type)); + _data_type_uname = dbe_strdup (get_prof_data_type_uname (data_type)); + } + data_type_name->append (_data_type_name); + data_type_uname->append (_data_type_uname); + + char* _short_desc = NULL; + if (m->get_type () == Metric::HWCNTR) + { + Hwcentry * hwctr = m->get_hw_ctr (); + if (hwctr) + _short_desc = dbe_strdup (hwctr->short_desc); + } + short_desc->append (_short_desc); + } + + // Set Java array + Vector<void*> *data = new Vector<void*>(16); + data->append (type); + data->append (subtype); + data->append (clock); + data->append (flavors); + data->append (value_styles); + data->append (user_name); + data->append (expr_spec); + data->append (aux); + data->append (name); + data->append (abbr); + data->append (comd); + data->append (unit); + data->append (vis); + data->append (sorted); + data->append (legend); + data->append (valtype); + data->append (data_type_name); + data->append (data_type_uname); + data->append (short_desc); + return data; +} + +Vector<void*> * +dbeGetRefMetricsV2 () +{ + MetricList *mlist = new MetricList (MET_NORMAL); + Vector<BaseMetric*> *base_metrics = dbeSession->get_base_reg_metrics (); + for (long i = 0, sz = base_metrics->size (); i < sz; i++) + { + BaseMetric *bm = base_metrics->fetch (i); + Metric *m; + if (bm->get_flavors () & Metric::EXCLUSIVE) + { + m = new Metric (bm, Metric::EXCLUSIVE); + m->enable_all_visbits (); + mlist->append (m); + } + else if (bm->get_flavors () & BaseMetric::STATIC) + { + m = new Metric (bm, BaseMetric::STATIC); + m->enable_all_visbits (); + mlist->append (m); + } + } + Vector<void*> *data = dbeGetMetricList (mlist); + delete mlist; + return data; +} + +Vector<void*> * +dbeGetCurMetricsV2 (int dbevindex, MetricType mtype) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + MetricList *mlist = dbev->get_metric_list (mtype); + Vector<void*> *data = dbeGetMetricList (mlist); + return data; +} + +// YXXX we should refactor Metrics/BaseMetrics so that it no longer uses VAL_VALUE to enable time. +static int +convert_visbits_to_gui_checkbox_bits (BaseMetric *bm, const int visbits) +{ + // The purpose of this function is to handle the following case: + // When bm->get_value_styles() supports VAL_TIMEVAL but not VAL_VALUE + // Metric and BaseMetric use (visbits&VAL_VALUE) to enable time. + // However, the Overview expects the VAL_TIMEVAL bit to enable time. + // Inputs: visbits as returned by BaseMetric->get_default_visbits(); + // Returns: valuebits, as used for checks in GUI checkboxes + int valuebits = visbits; + const int value_styles = bm->get_value_styles (); + if ((value_styles & VAL_TIMEVAL) && // supports time + !(value_styles & VAL_VALUE)) + { // but not value + unsigned mask = ~(VAL_VALUE | VAL_TIMEVAL); + valuebits = (unsigned) valuebits & mask; // clear bits + if (visbits & VAL_VALUE) + valuebits |= VAL_TIMEVAL; // set VAL_TIMEVAL + if (visbits & VAL_TIMEVAL) + valuebits |= VAL_TIMEVAL; // weird, this should never happen. + } + return valuebits; +} + +static Vector<void*> * +dbeGetMetricTreeNode (BaseMetricTreeNode* curr, MetricList *mlist, + bool include_unregistered, bool has_clock_profiling_data) +{ + Vector<void*> *data = new Vector<void*>(2); + + // ----- fields + Vector<void*> *fields = new Vector<void*>(); + Vector<char*> *name = new Vector<char*>(1); + Vector<char*> *username = new Vector<char*>(1); + Vector<char*> *description = new Vector<char*>(1); + Vector<int> * flavors = new Vector<int>(1); + Vector<int> * vtype = new Vector<int>(1); + Vector<int> * vstyles_capable = new Vector<int>(1); + + // Specifies which default styles should be enabled when a metric is enabled. + // Also, specifies if metric should start enabled + Vector<int> *vstyles_e_defaults = new Vector<int>(1); + Vector<int> *vstyles_i_defaults = new Vector<int>(1); + Vector<bool> *registered = new Vector<bool>(1); + Vector<bool> *aggregation = new Vector<bool>(1); + Vector<bool> *has_value = new Vector<bool>(1); + Vector<char*> *unit = new Vector<char*>(1); + Vector<char*> *unit_uname = new Vector<char*>(1); + + char *_name = NULL; + char *_username = NULL; + char *_description = dbe_strdup (curr->get_description ()); + + // BaseMetric fields + int _flavors = 0; // SubType bitmask: (e.g. EXCLUSIVE) + int _vtype = 0; // ValueTag: e.g. VT_INT, VT_FLOAT, ... + int _vstyles_capable = 0; // ValueType bitmask, e.g. VAL_TIMEVAL + int _vstyles_e_default_values = 0; // default visibility settings, exclusive/static + int _vstyles_i_derault_values = 0; // default visibility settings, inclusive + bool _registered = curr->is_registered () + || curr->get_num_registered_descendents () > 0; + bool _aggregation = curr->is_composite_metric () + && curr->get_num_registered_descendents () > 0; + bool _has_value = false; //not used yet; for nodes that don't have metrics + char *_unit = NULL; + char *_unit_uname = NULL; + + BaseMetric *bm = curr->get_BaseMetric (); + if (bm) + { + _name = dbe_strdup (bm->get_cmd ()); + _username = dbe_strdup (bm->get_username ()); + if (!include_unregistered && !curr->is_registered ()) + abort (); + _flavors = bm->get_flavors (); + _vtype = bm->get_vtype (); + _vstyles_capable = bm->get_value_styles (); + int e_visbits = bm->get_default_visbits (BaseMetric::EXCLUSIVE); + int i_visbits = bm->get_default_visbits (BaseMetric::INCLUSIVE); + _vstyles_e_default_values = convert_visbits_to_gui_checkbox_bits (bm, e_visbits); + _vstyles_i_derault_values = convert_visbits_to_gui_checkbox_bits (bm, i_visbits); + // not all metrics shown in er_print cmd line should be selected in the GUI at startup: + if (has_clock_profiling_data && bm->get_hw_ctr ()) + { + bool hide = true; // by default, hide HWCs + if (dbe_strcmp (bm->get_hw_ctr ()->name, NTXT ("c_stalls")) == 0 || + dbe_strcmp (bm->get_hw_ctr ()->name, NTXT ("K_c_stalls")) == 0) + { + bool is_time = (bm->get_value_styles () & VAL_TIMEVAL) != 0; + if (is_time) + // By default, show time variant of c_stalls + hide = false; + } + if (hide) + { + _vstyles_e_default_values |= VAL_HIDE_ALL; + _vstyles_i_derault_values |= VAL_HIDE_ALL; + } + } + } + else + { + // not a base metric + _name = dbe_strdup (curr->get_name ()); + _username = dbe_strdup (curr->get_user_name ()); + if (curr->get_unit ()) + { // represents a value + _has_value = true; + _unit = dbe_strdup (curr->get_unit ()); + _unit_uname = dbe_strdup (curr->get_unit_uname ()); + } + } + name->append (_name); // unique id string (dmetrics cmd) + username->append (_username); // user-visible name + description->append (_description); + flavors->append (_flavors); // SubType bitmask: (e.g. EXCLUSIVE) + vtype->append (_vtype); // ValueTag: e.g. VT_INT, VT_FLOAT, ... + vstyles_capable->append (_vstyles_capable); // ValueType bitmask, e.g. VAL_TIMEVAL + vstyles_e_defaults->append (_vstyles_e_default_values); + vstyles_i_defaults->append (_vstyles_i_derault_values); + registered->append (_registered); // is a "live" metric + aggregation->append (_aggregation); // value derived from children nodes + has_value->append (_has_value); // value generated from other source + unit->append (_unit); // See BaseMetric.h, e.g. UNIT_SECONDS + unit_uname->append (_unit_uname); //See BaseMetric.h, + + fields->append (name); + fields->append (username); + fields->append (description); + fields->append (flavors); + fields->append (vtype); + fields->append (vstyles_capable); + fields->append (vstyles_e_defaults); + fields->append (vstyles_i_defaults); + fields->append (registered); + fields->append (aggregation); + fields->append (has_value); + fields->append (unit); + fields->append (unit_uname); + data->append (fields); + + // ----- children + Vector<BaseMetricTreeNode*> *children = curr->get_children (); + int num_children = children->size (); + Vector<void*> *children_list = new Vector<void*>(num_children); + BaseMetricTreeNode *child_node; + int index; + + Vec_loop (BaseMetricTreeNode*, children, index, child_node) + { + if (include_unregistered /* fetch everything */ + || child_node->is_registered () + || child_node->get_num_registered_descendents () > 0) + { + //Special case for metrics that aren't registered + // but have registered children + // Linux example: Total Time is unregistered, CPU Time is registered + if (!include_unregistered && /* not fetching everything */ + !child_node->is_registered () && + (child_node->get_BaseMetric () != NULL || + child_node->is_composite_metric ())) + { + Vector<BaseMetricTreeNode*> *registered_descendents = + new Vector<BaseMetricTreeNode*>(); + child_node->get_nearest_registered_descendents (registered_descendents); + int idx2; + BaseMetricTreeNode*desc_node; + Vec_loop (BaseMetricTreeNode*, registered_descendents, idx2, desc_node) + { + Vector<void*> *desc_data; + desc_data = dbeGetMetricTreeNode (desc_node, mlist, + include_unregistered, has_clock_profiling_data); + children_list->append (desc_data); + } + delete registered_descendents; + continue; + } + Vector<void*> *child_data; + child_data = dbeGetMetricTreeNode (child_node, mlist, + include_unregistered, has_clock_profiling_data); + children_list->append (child_data); + } + } + data->append (children_list); + return data; +} + +Vector<void*> * +dbeGetRefMetricTree (int dbevindex, bool include_unregistered) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + MetricList *mlist = dbev->get_metric_list (MET_NORMAL); + bool has_clock_profiling_data = false; + for (long i = 0, sz = mlist->get_items ()->size (); i < sz; i++) + { + Metric *m = mlist->get_items ()->fetch (i); + if (m->get_packet_type () == DATA_CLOCK) + { + has_clock_profiling_data = true; + break; + } + } + BaseMetricTreeNode *curr = dbeSession->get_reg_metrics_tree (); + return dbeGetMetricTreeNode (curr, mlist, include_unregistered, has_clock_profiling_data); +} + +static Vector<void*> * +dbeGetTableDataV2Data (DbeView *dbev, Hist_data *data); + +static Vector<void*> *dbeGetTableDataOneColumn (Hist_data *data, int met_ind); +static Vector<void*> * +dbeGetTableDataOneColumn (DbeView *dbev, Vector<Hist_data::HistItem*> *data, + ValueTag vtype, int metricColumnNumber); + +static hrtime_t +dbeCalcGroupDuration (int grInd) +{ + int thisGroupSize = 1; + hrtime_t max_time = 0; + Experiment *exp; + if (dbeSession->expGroups->size () > 0) + { + ExpGroup *grp = dbeSession->expGroups->fetch (grInd); + thisGroupSize = grp->exps->size (); + for (int ii = 0; ii < thisGroupSize; ii++) + { + exp = grp->exps->fetch (ii); + Vector<DataDescriptor*> *ddscr = exp->getDataDescriptors (); + delete ddscr;// getDataDescriptors() forces reading of experiment data + if (exp != NULL) + { + hrtime_t tot_time = exp->getLastEvent () - exp->getStartTime () + + exp->getRelativeStartTime (); + if (max_time < tot_time) + max_time = tot_time; + } + } + } + else + { + exp = dbeSession->get_exp (0); + if (exp != NULL) + max_time = exp->getLastEvent () - exp->getStartTime (); + } + return max_time; //nanoseconds +} + +static hrtime_t +dbeCalcGroupGCDuration (int grInd) +{ + int thisGroupSize = 1; + hrtime_t tot_time = 0; + Experiment *exp; + if (dbeSession->expGroups->size () > 0) + { + ExpGroup *grp = dbeSession->expGroups->fetch (grInd); + thisGroupSize = grp->exps->size (); + for (int ii = 0; ii < thisGroupSize; ii++) + { + exp = grp->exps->fetch (ii); + Vector<DataDescriptor*> *ddscr = exp->getDataDescriptors (); + delete ddscr; // getDataDescriptors() forces reading of experiment data + if (exp != NULL) + tot_time += exp->getGCDuration (); + } + } + else + { + exp = dbeSession->get_exp (0); + if (exp != NULL) + tot_time = exp->getGCDuration (); + } + return tot_time; //nanoseconds +} + +Vector<void*> * +dbeGetRefMetricTreeValues (int dbevindex, Vector<char *> *metric_cmds, + Vector<char *> *non_metric_cmds) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + // valueTable will have N "columns" of values, where N is the number of + // requested metrics and non-metrics. + // Each column will be a vector with M "rows", where M is the number of + // compare groups. + // highlightTable mirrors the structure of valueTable. Each cell indicates + // if the corresponding valueTable cell is "hot" (interesting) + int numMetrics = metric_cmds->size (); + int numNonMetrics = non_metric_cmds->size (); + int totalColumns = numMetrics + numNonMetrics; // Columns + Vector<void*> *valueTable = new Vector<void*>(totalColumns); + Vector<void*> *highlightTable = new Vector<void*>(totalColumns); + + // the return value consists of the two tables discussed above. + Vector<void*> *rc = new Vector<void*>(2); + rc->append (valueTable); + rc->append (highlightTable); + if (dbeSession->nexps () == 0) + { // no experiments are loaded + for (int jj = 0; jj < totalColumns; jj++) + { + Vector<void *> *columnData = new Vector<void *>(); + valueTable->append (columnData); + highlightTable->append (columnData); + } + return rc; + } + + int ngroups = dbeSession->expGroups->size (); // Rows (one per compare group) + if (ngroups == 0 || !dbev->comparingExperiments ()) + ngroups = 1; + + Vector<double> *groupTotalTime = new Vector<double>(ngroups); + Vector<double> *groupCpuTime = new Vector<double>(ngroups); + // initialize highlight table + for (int ii = 0; ii < totalColumns; ii++) + { // metrics + Vector<bool> *columnData = new Vector<bool>(ngroups); + highlightTable->append (columnData); + for (int grInd = 0; grInd < ngroups; grInd++) + columnData->store (grInd, false); // non-highlight + } + + if (numMetrics > 0) + { + MetricList *bmlist; + // set bmlist to list of requested base metrics + BaseMetricTreeNode *root = dbeSession->get_reg_metrics_tree (); + int index; + char *mcmd; + Vector<BaseMetric*> *base_metrics = new Vector<BaseMetric*>(); + Vec_loop (char *, metric_cmds, index, mcmd) + { + BaseMetricTreeNode *bmt_node = root->find (mcmd); + if (!bmt_node) + abort (); //YXXX weird + BaseMetric * baseNetric = bmt_node->get_BaseMetric (); + if (!baseNetric) + abort (); + base_metrics->append (baseNetric); + } + + // MET_INDX will create MetricList of Exclusive metrics + bmlist = new MetricList (base_metrics, MET_SRCDIS); + + // Use the Function List to fetch <Total> values + // A temporary table, v_totals, stores <total> by group + Vector<Hist_data::HistItem *> *v_totals = new Vector<Hist_data::HistItem *>(ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + MetricList *mlist; + if (ngroups > 1) + mlist = dbev->get_compare_mlist (bmlist, grInd); + else + mlist = bmlist; + if (mlist->size () != numMetrics) + abort (); + + Hist_data *data; + data = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::ALL); + Hist_data::HistItem * totals = data->get_totals (); + v_totals->append (totals); + } + + // store the Hist_data totals in valueTable + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + Vector<void*> * columnData = dbeGetTableDataOneColumn (dbev, + v_totals, mitem->get_vtype (), index); + valueTable->append (columnData); + } + } + + // 7207285: hack for hwc profiling cycles conversion: + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + if (mitem->is_time_val () + && mitem->get_vtype () == VT_ULLONG) + { + Vector<long long> *cycleValues = (Vector<long long> *)valueTable->fetch (index); + Vector<double> *timeValues = new Vector<double>(ngroups); + assert (cycleValues->size () == ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + long long cycles = cycleValues->fetch (grInd); + int expId; + if (dbeSession->expGroups->size () > 0) + { + ExpGroup *gr = dbeSession->expGroups->fetch (grInd); + Experiment *exp = gr->exps->fetch (0); + expId = exp->getExpIdx (); + } + else + expId = -1; + int clock = dbeSession->get_clock (expId); + double time; + if (clock) + time = cycles / (1.e+6 * clock); + else + time = cycles; //weird + timeValues->store (grInd, time); + } + delete cycleValues; + valueTable->store (index, timeValues); + } + } + } + + // Scan metrics for best measure of CPU time + int bestCpuTimeIndx = -1; + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + BaseMetric::Type type = mitem->get_type (); + if (type == BaseMetric::CP_KERNEL_CPU) + { + bestCpuTimeIndx = index; + break; // CP_KERNEL_CPU trumps other measures + } + if (type == BaseMetric::CP_TOTAL_CPU) + { + // clock profiling CPU time + bestCpuTimeIndx = index; + // keep looking in case CP_KERNEL_CPU also exists + continue; + } + + bool isTime = ((mitem->get_value_styles () & VAL_TIMEVAL) != 0); + bool isHwcCycles = (type == BaseMetric::HWCNTR + && (dbe_strcmp (mitem->get_aux (), "cycles") == 0) + && isTime); + if (isHwcCycles) + if (bestCpuTimeIndx < 0) + bestCpuTimeIndx = index; + } + if (bestCpuTimeIndx >= 0) + { + Vector<double> *timeValues = (Vector<double> *)valueTable->fetch (bestCpuTimeIndx); + if (timeValues->type () == VEC_DOUBLE) + for (int grInd = 0; grInd < ngroups; grInd++) + { + double time = timeValues->fetch (grInd); + groupCpuTime->append (time); + } + } + } + + // Scan metrics for Total Thread time + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + BaseMetric::Type type = mitem->get_type (); + if (type == BaseMetric::CP_TOTAL) + { + Vector<double> *timeValues = (Vector<double> *)valueTable->fetch (index); + if (timeValues->type () != VEC_DOUBLE) + continue; // weird + for (int grInd = 0; grInd < ngroups; grInd++) + { + double time = timeValues->fetch (grInd); + groupTotalTime->append (time); + } + break; + } + } + } + + // highlight metrics based on cpu time +#define CPUSEC_PERCENT_THRESHOLD 10.0 +#define HWC_OVERFLOWS_PER_CPUSEC_THRESHOLD 15 + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + BaseMetric::Type type = mitem->get_type (); + Vector<bool> * columnHilites = (Vector<bool> *)highlightTable->fetch (index); + + // always highlight the following + if (index == bestCpuTimeIndx) + { + for (int grInd = 0; grInd < ngroups; grInd++) + columnHilites->store (grInd, true); + continue; + } + + // skip certain types + bool typeIsCycles = (type == BaseMetric::HWCNTR + && dbe_strcmp (mitem->get_aux (), NTXT ("cycles")) == 0); + bool typeIsInsts = (type == BaseMetric::HWCNTR + && dbe_strcmp (mitem->get_aux (), NTXT ("insts")) == 0); + if (type == BaseMetric::CP_TOTAL + || type == BaseMetric::CP_TOTAL_CPU + || type == BaseMetric::CP_LMS_USER + || type == BaseMetric::CP_LMS_SYSTEM + || type == BaseMetric::CP_LMS_TRAP + || type == BaseMetric::CP_LMS_USER_LOCK + || type == BaseMetric::CP_LMS_SLEEP + || type == BaseMetric::CP_KERNEL_CPU + || type == BaseMetric::OMP_WORK + || typeIsCycles + || typeIsInsts + // || type == BaseMetric::CP_TOTAL_WAIT + ) + continue; // types we never highlight + + // for time values, compare against CPUSEC_PERCENT_THRESHOLD + bool isTime = ((mitem->get_value_styles () & VAL_TIMEVAL) != 0); + if (isTime) + { + if (groupCpuTime->size () == 0) + continue; // no time to use as reference + Vector<double> *timeValues = (Vector<double> *)valueTable->fetch (index); + if (timeValues->type () != VEC_DOUBLE) + continue; // weird + for (int grInd = 0; grInd < ngroups; grInd++) + { + double thistime = timeValues->fetch (grInd); + double usertime = groupCpuTime->fetch (grInd); + if (thistime / (CPUSEC_PERCENT_THRESHOLD / 100) > usertime) + columnHilites->store (grInd, true); + } + continue; + } + + // for HWC event counts, look at rate of events + if (type == BaseMetric::HWCNTR) + { + Hwcentry *hwctr = mitem->get_hw_ctr (); + if (!hwctr) + continue; // weird + if (!hwctr->metric) + continue; // raw counter + if (groupCpuTime->size () == 0) + continue; // no time to use as reference + if (mitem->get_base_metric ()->get_dependent_bm ()) + continue; // has a derived time metric, only flag time version + Vector<long long> *llValues = (Vector<long long> *)valueTable->fetch (index); + if (llValues->type () != VEC_LLONG) + continue; // weird + int overflowVal = hwctr->val; //overflow count + if (!overflowVal) + continue; // weird + if (overflowVal > (4000000)) + // cut off events that are very frequent like loads/stores + // 4Ghz * (0.01 seconds/event) / (4000000 events/overflow) = 10 cycles + continue; + // for HWCs we could base it on the overflow rate + for (int grInd = 0; grInd < ngroups; grInd++) + { + double thisVal = llValues->fetch (grInd); + thisVal /= overflowVal; + double usertime = groupCpuTime->fetch (grInd); + if (thisVal > usertime * HWC_OVERFLOWS_PER_CPUSEC_THRESHOLD) + columnHilites->store (grInd, true); + } + continue; + } + + // check for non-zero counts of the following + if (type == BaseMetric::DEADLOCKS || + type == BaseMetric::RACCESS || + type == BaseMetric::HEAP_ALLOC_BYTES || + type == BaseMetric::HEAP_LEAK_BYTES) + { + Vector<long long> *llValues = (Vector<long long> *)valueTable->fetch (index); + if (llValues->type () != VEC_LLONG) + continue; // weird + for (int grInd = 0; grInd < ngroups; grInd++) + { + long long thisVal = llValues->fetch (grInd); + if (thisVal) + columnHilites->store (grInd, true); + } + continue; + } + // continue adding cases as needed + } + } + } + + if (numNonMetrics > 0) + { + int index; + char *mcmd; + Vec_loop (char *, non_metric_cmds, index, mcmd) + { + if (dbe_strcmp (mcmd, NTXT ("YXXX_TOTAL_TIME_PLUS_THREADS")) == 0 + && groupCpuTime->size () == ngroups) + { + Vector<char *> *columnData = new Vector<char *>(ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + double totaltime = groupTotalTime->fetch (grInd); + columnData->append (dbe_sprintf (NTXT ("%0.3f %s"), totaltime, GTXT ("Seconds"))); + } + valueTable->append (columnData); + } + else if (dbe_strcmp (mcmd, L1_DURATION) == 0) + { + Vector<double> *columnData = new Vector<double>(ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + hrtime_t duration = dbeCalcGroupDuration (grInd); + double seconds = duration * 1.e-9; + columnData->append (seconds); + } + valueTable->append (columnData); + } + else if (dbe_strcmp (mcmd, L1_GCDURATION) == 0) + { + Vector<double> *columnData = new Vector<double>(ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + hrtime_t duration = dbeCalcGroupGCDuration (grInd); + double seconds = duration * 1.e-9; + columnData->append (seconds); + } + valueTable->append (columnData); + } + else + { + Vector<char *> *columnData = new Vector<char *>(ngroups); + char * valueString = NTXT ("<unknown>"); + for (int grInd = 0; grInd < ngroups; grInd++) + columnData->append (dbe_strdup (valueString)); + valueTable->append (columnData); + } + } + } + return rc; +} + +Vector<char*> * +dbeGetOverviewText (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Vector<char*> *info = new Vector<char*>; + char *field; + int ngroups = dbeSession->expGroups->size (); // Rows (one per compare group) + if (ngroups == 0 || !dbev->comparingExperiments ()) + ngroups = 1; + for (int grInd = 0; grInd < ngroups; grInd++) + { + int thisGroupSize = 1; + Experiment *exp; + if (dbeSession->expGroups->size () > 0) + { + ExpGroup *gr = dbeSession->expGroups->fetch (grInd); + exp = gr->exps->fetch (0); + thisGroupSize = gr->exps->size (); + } + else + { + if (dbeSession->nexps () == 0) + return info; + exp = dbeSession->get_exp (0); + } + char * expHeader; + if (ngroups == 1) + expHeader = dbe_strdup (GTXT ("Experiment :")); + else if (grInd == 0) + expHeader = dbe_strdup (GTXT ("Base Group : ")); + else if (ngroups == 2) + expHeader = dbe_strdup (GTXT ("Compare Group : ")); + else + expHeader = dbe_sprintf (GTXT ("Compare Group %d : "), grInd); + if (thisGroupSize == 1) + info->append (dbe_sprintf ("%s%s", expHeader, exp->get_expt_name ())); + else + info->append (dbe_sprintf ("%s%s (plus %d more)", + expHeader, exp->get_expt_name (), thisGroupSize - 1)); + free (expHeader); + field = exp->uarglist; + if (field && field[0]) + info->append (dbe_sprintf (GTXT (" Target : '%s'"), field)); + field = exp->hostname; + if (field && field[0]) + info->append (dbe_sprintf (NTXT (" %s %s (%s, %s)"), + GTXT ("Host :"), + field, + exp->architecture ? exp->architecture + : GTXT ("<CPU architecture not recorded>"), + exp->os_version ? exp->os_version + : GTXT ("<OS version not recorded>"))); + long start_sec = exp->start_sec; + char *p = ctime (&start_sec); // does this need to be freed? YXXX + hrtime_t tot_time = dbeCalcGroupDuration (grInd); + double seconds = tot_time * 1.e-9; + info->append (dbe_sprintf (NTXT (" %s %s %s %0.3f %s"), + GTXT ("Start Time :"), p, + GTXT ("Duration :"), seconds, + GTXT ("Seconds"))); + // Number of descendants/processes would be nice + info->append (dbe_strdup (NTXT (""))); + } + return info; +} + +//-------------------------------------------------------------------------- +// Set Sort by index +// +void +dbeSetSort (int dbevindex, int sort_index, MetricType mtype, bool reverse) +{ + DbeView *dbev; + + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->setSort (sort_index, mtype, reverse); + return; +} + +// +// Get annotation setting +// +Vector<int> * +dbeGetAnoValue (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<int> *set = new Vector<int>(9); + set->store (0, dbev->get_src_compcom ()); + set->store (1, dbev->get_dis_compcom ()); + set->store (2, dbev->get_thresh_src ()); + set->store (3, dbev->get_thresh_src ()); + set->store (4, dbev->get_src_visible ()); + set->store (5, (int) dbev->get_srcmetric_visible ()); + set->store (6, (int) dbev->get_hex_visible ()); + set->store (7, (int) dbev->get_cmpline_visible ()); + set->store (8, (int) dbev->get_func_scope ()); + return set; +} + +// +// Set annotation setting +// +void +dbeSetAnoValue (int dbevindex, Vector<int> *set) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (set->size () != 10) + return; + dbev->set_src_compcom (set->fetch (0)); + dbev->set_dis_compcom (set->fetch (1)); + dbev->set_thresh_src (set->fetch (2)); + dbev->set_thresh_dis (set->fetch (3)); + dbev->set_src_visible (set->fetch (4)); + dbev->set_srcmetric_visible ((bool)set->fetch (5)); + dbev->set_hex_visible ((bool)set->fetch (6)); + dbev->set_cmpline_visible ((bool)set->fetch (7)); + dbev->set_func_scope (set->fetch (8)); + dbev->set_funcline_visible ((bool)set->fetch (9)); + return; +} + +// +// Get name formats +// +int +dbeGetNameFormat (int dbevindex) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable::NameFormat fmt = dbev->get_name_format (); + return Histable::fname_fmt (fmt); +} + +bool +dbeGetSoName (int dbevindex) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable::NameFormat fmt = dbev->get_name_format (); + return Histable::soname_fmt (fmt); +} + +// +// Set name formats +// +void +dbeSetNameFormat (int dbevindex, int nformat, bool soname) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_name_format (nformat, soname); +} + +// +// Get View mode +// +int +dbeGetViewMode (int dbevindex) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return (int) dbev->get_view_mode (); +} + +// Set View mode +void +dbeSetViewMode (int dbevindex, int nmode) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_view_mode ((VMode) nmode); + return; +} + +// Get timeline setting +// +Vector<void*> * +dbeGetTLValue (int dbevindex) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<char *> *strings = new Vector<char *>(); + char *tldata_cmd = dbev->get_tldata (); + strings->store (0, tldata_cmd); + + Vector<int> *ints = new Vector<int>(3); + int val; + val = dbev->get_tlmode (); + ints->store (0, val); + val = dbev->get_stack_align (); + ints->store (1, val); + val = dbev->get_stack_depth (); + ints->store (2, val); + + Vector<void*> *objs = new Vector<void*>(2); + objs->store (0, strings); + objs->store (1, ints); + return objs; +} + +// +// Set timeline setting +// +void +dbeSetTLValue (int dbevindex, const char *tldata_cmd, + int entitiy_prop_id, int stackalign, int stackdepth) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_tldata (tldata_cmd); + dbev->set_tlmode (entitiy_prop_id); + dbev->set_stack_align (stackalign); + dbev->set_stack_depth (stackdepth); + return; +} + +// +// Get founder experiments and their descendants +// +Vector<void*> * +dbeGetExpFounderDescendants () +{ + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + Vector<void*> *table = new Vector<void*>(2); + Vector<int> *founderExpIds = new Vector<int>(); + Vector<Vector<int> *> *subExpIds = new Vector<Vector<int>*>(); + for (int index = 0; index < size; index++) + { + Experiment *exp = dbeSession->get_exp (index); + if (exp->founder_exp == NULL) + { + founderExpIds->append (exp->getExpIdx ()); + Vector<int> *subExps = new Vector<int>(); + for (int i = 0; i < exp->children_exps->size (); i++) + { + Experiment * subExp = exp->children_exps->fetch (i); + subExps->append (subExp->getExpIdx ()); + } + subExpIds->append (subExps); + } + } + table->store (0, founderExpIds); + table->store (1, subExpIds); + return table; +} + +// +// Get experiment selection +// +Vector<void*> * +dbeGetExpSelection (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + Vector<void*> *table = new Vector<void*>(3); + Vector<char*> *names = new Vector<char*>(size); + Vector<bool> *enable = new Vector<bool>(size); + Vector<int> *userExpIds = new Vector<int>(size); + + // Get experiment names + for (int index = 0; index < size; index++) + { + Experiment *exp = dbeSession->get_exp (index); + char *buf = dbeGetName (dbevindex, index); + names->store (index, buf); + bool val; + val = dbev->get_exp_enable (index); + enable->store (index, val); + userExpIds->store (index, exp->getUserExpId ()); + } + table->store (0, names); + table->store (1, enable); + table->store (2, userExpIds); + return table; +} + +int +dbeValidateFilterExpression (char *str_expr) +{ + if (str_expr == NULL) + return 0; + Expression *expr = dbeSession->ql_parse (str_expr); + if (expr == NULL) + return 0; + delete expr; + return 1; +} + +Vector<void*> * +dbeGetFilterKeywords (int /* dbevindex */) +{ + Vector <char*> *kwCategory = new Vector<char *>(); + Vector <char*> *kwCategoryI18N = new Vector<char *>(); + Vector <char*> *kwDataType = new Vector<char *>(); + Vector <char*> *kwKeyword = new Vector<char *>(); + Vector <char*> *kwFormula = new Vector<char *>(); + Vector <char*> *kwDescription = new Vector<char *>(); + Vector <void*> *kwEnumDescs = new Vector<void *>(); + + Vector<void*> *res = new Vector<void*>(7); + res->append (kwCategory); + res->append (kwCategoryI18N); + res->append (kwDataType); + res->append (kwKeyword); + res->append (kwFormula); + res->append (kwDescription); + res->append (kwEnumDescs); + + char *vtypeNames[] = VTYPE_TYPE_NAMES; + // section header for global definitions + kwCategory->append (dbe_strdup (NTXT ("FK_SECTION"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Global Definitions"))); + kwDataType->append (NULL); + kwKeyword->append (NULL); + kwFormula->append (NULL); + kwDescription->append (NULL); + kwEnumDescs->append (NULL); + dbeSession->get_filter_keywords (res); + MemorySpace::get_filter_keywords (res); + + // loop thru all founder experiments + int nexp = dbeSession->nexps (); + for (int ii = 0; ii < nexp; ++ii) + { + Experiment* fexp = dbeSession->get_exp (ii); + if (fexp->founder_exp != NULL) + continue; // is a child; should be covered when we get to founder + + // section header for each founder + // section header for founder experiment + kwCategory->append (dbe_strdup (NTXT ("FK_SECTION"))); + kwCategoryI18N->append (dbe_sprintf (NTXT ("%s [EXPGRID==%d]"), + fexp->get_expt_name (), + fexp->groupId)); + kwDataType->append (NULL); + kwKeyword->append (NULL); + kwFormula->append (NULL); + kwDescription->append (NULL); + kwEnumDescs->append (NULL); + + int nchildren = fexp->children_exps->size (); + Experiment *exp; + // category header: Experiments + { + char *propUName = dbeSession->getPropUName (PROP_EXPID); + + // store list of subexperiments in kwEnumDescs + Vector <char*> *enumDescs = new Vector<char *>(); + int jj = 0; + exp = fexp; + while (1) + { + char * expBasename = get_basename (exp->get_expt_name ()); + char * targetName = exp->utargname ? exp->utargname + : (char *) GTXT ("(unknown)"); + enumDescs->append (dbe_sprintf (NTXT ("(%d) -> %s [%s, PID %d]"), + exp->getUserExpId (), expBasename, + targetName, exp->getPID ())); + if (jj >= nchildren) + break; + exp = fexp->children_exps->fetch (jj); + jj++; + } + kwCategory->append (dbe_strdup (NTXT ("FK_EXPLIST"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Experiments"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_INT32])); + kwKeyword->append (dbe_strdup (NTXT ("EXPID"))); + kwFormula->append (NULL); + kwDescription->append (propUName); + kwEnumDescs->append (enumDescs); + } + + // select representative experiment + if (nchildren == 0) + exp = fexp; // founder + else + exp = fexp->children_exps->fetch (0); // first child + int expIdx = exp->getExpIdx (); + Vector<void*> *data = dbeGetDataDescriptorsV2 (expIdx); + if (data == NULL) + continue; + Vector<int> *dataId = (Vector<int>*)data->fetch (0); + Vector<char*> *dataName = (Vector<char*>*)data->fetch (1); + Vector<char*> *dataUName = (Vector<char*>*)data->fetch (2); + if (dataId == NULL || dataName == NULL) + { + destroy (data); + continue; + } + // loop thru data descriptors + int ndata = dataId->size (); + for (int j = 0; j < ndata; ++j) + { + // category: data name (e.g. Clock Profiling) + char * catName = dataName->fetch (j); + char * dUname = dataUName ? dataUName->fetch (j) : catName; + char * catUname = dUname ? dUname : catName; + + Vector<void*> *props = dbeGetDataPropertiesV2 (expIdx, dataId->fetch (j)); + if (props == NULL) + continue; + Vector<char*> *propUName = (Vector<char*>*)props->fetch (1); + Vector<int> *propTypeId = (Vector<int> *)props->fetch (2); + Vector<char*> *propType = (Vector<char*>*)props->fetch (3); + Vector<char*> *propName = (Vector<char*>*)props->fetch (5); + Vector<Vector<char*>*> *propStateNames = + (Vector<Vector<char*>*> *)props->fetch (6); + Vector<Vector<char*>*> *propStateUNames = + (Vector<Vector<char*>*> *)props->fetch (7); + if (propName == NULL || propUName == NULL || propType == NULL + || propName->size () <= 0) + { + destroy (props); + continue; + } + int nprop = propName->size (); + for (int k = 0; k < nprop; ++k) + { + if (propTypeId->fetch (k) == TYPE_OBJ) + continue; + if (dbe_strcmp (propName->fetch (k), NTXT ("FRINFO")) == 0) + continue; + + // store list of states in kwEnumDescs + Vector<char*> *enumDescs = new Vector<char *>(); + Vector<char*>* stateNames = propStateNames->fetch (k); + Vector<char*>* stateUNames = propStateUNames->fetch (k); + int nStates = stateNames ? stateNames->size () : 0; + for (int kk = 0; kk < nStates; ++kk) + { + const char *stateName = stateNames->fetch (kk); + if (stateName == NULL || strlen (stateName) == 0) + continue; + const char *stateUName = stateUNames->fetch (kk); + if (stateUName == NULL || strlen (stateUName) == 0) + stateUName = stateName; + enumDescs->append (dbe_sprintf (NTXT ("(%d) -> %s"), kk, stateUName)); + } + kwCategory->append (dbe_strdup (catName)); + kwCategoryI18N->append (dbe_strdup (catUname)); + kwDataType->append (dbe_strdup (propType->fetch (k))); + kwKeyword->append (dbe_strdup (propName->fetch (k))); + kwFormula->append (NULL); + kwDescription->append (dbe_strdup (propUName->fetch (k))); + kwEnumDescs->append (enumDescs); + } + destroy (props); + } + destroy (data); + } + return (res); +} + +// GetFilters -- returns the list of filters for the indexed experiment +// returns false if there's a problem; true otherwise +// +Vector<void*> * +dbeGetFilters (int dbevindex, int nexp) +{ + FilterNumeric *filt; + int index; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<FilterNumeric *>*filters = dbev->get_all_filters (nexp); + if (filters == NULL) + return NULL; + + // return an array of filter data for that experiment + Vector <int> *findex = new Vector<int>(); // index of the filters + Vector <char*> *shortname = new Vector<char *>(); + // short name of filter + Vector <char*> *i18n_name = new Vector<char *>(); + // External I18N'd name of filter + Vector <char*> *pattern = new Vector<char *>(); + // current setting string + Vector <char*> *status = new Vector<char *>(); + // current status of filter (%, range, etc.) + + Vec_loop (FilterNumeric *, filters, index, filt) + { + findex->append (index); + shortname->append (dbe_strdup (filt->get_cmd ())); + i18n_name->append (dbe_strdup (filt->get_name ())); + pattern->append (dbe_strdup (filt->get_pattern ())); + status->append (dbe_strdup (filt->get_status ())); + } + Vector<void*> *res = new Vector<void*>(5); + res->store (0, findex); + res->store (1, shortname); + res->store (2, i18n_name); + res->store (3, pattern); + res->store (4, status); + return (res); +} + +// Set a filter string for a view +// Returns NULL if OK, error message if not + +char * +dbeSetFilterStr (int dbevindex, char *filter_str) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->clear_error_msg (); + dbev->clear_warning_msg (); + char *ret = dbev->set_filter (filter_str); + return ret; +} + +// Get the current filter setting for the view +char * +dbeGetFilterStr (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + char *ret = dbev->get_filter (); + return ret; +} + +// Update a filters for a single experiment +// Returns true if any filter->set_pattern() returns true, +// implying rereading the data is needed (i.e., a filter changed) +// +bool +dbeUpdateFilters (int dbevindex, Vector<bool> *selected, Vector<char *> *pattern_str) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->clear_error_msg (); + dbev->clear_warning_msg (); + + // Get index of first selected experiment + int size = selected->size (); + int nselexp = -1; + for (int index = 0; index < size; index++) + { + if (selected->fetch (index) == true) + { + nselexp = index; + break; + } + } + if (nselexp == -1) // No experiment selected + return false; + + bool ret = false; + for (int j = 0; j < size; j++) + { + if (selected->fetch (j) == false) + continue; + bool error; + if (dbev->set_pattern (j, pattern_str, &error)) + ret = true; + } + dbev->update_advanced_filter (); + return ret; +} + +char * +dbeComposeFilterClause (int dbevindex, int type, int subtype, Vector<int> *selections) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + // ask the cached data to generate the string + Hist_data *data; + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + data = dbev->get_indxobj_data (subtype); + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_SOURCE: + data = dbev->src_data; + break; + case DSP_DISASM: + data = dbev->dis_data; + break; + case DSP_IOACTIVITY: + data = dbev->iofile_data; + break; + case DSP_IOVFD: + data = dbev->iovfd_data; + break; + case DSP_IOCALLSTACK: + data = dbev->iocs_data; + break; + case DSP_HEAPCALLSTACK: + data = dbev->heapcs_data; + break; + default: + return NULL; + } + if (data == NULL) + return NULL; + + // Get array of object indices, and compose filter string + Vector<uint64_t> *obj_ids = data->get_object_indices (selections); + if (obj_ids == NULL || obj_ids->size () == 0) + return NULL; + + uint64_t sel; + int index; + int found = 0; + char buf[128]; + StringBuilder sb; + sb.append ('('); + switch (type) + { + case DSP_LINE: + case DSP_PC: + case DSP_SOURCE: + case DSP_DISASM: + case DSP_FUNCTION: + sb.append (NTXT ("LEAF IN ")); + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + sb.append (dbeSession->getIndexSpaceName (subtype)); + sb.append (NTXT (" IN ")); + break; + } + Vec_loop (uint64_t, obj_ids, index, sel) + { + if (found == 0) + { + found = 1; + sb.append ('('); + } + else + sb.append (NTXT (", ")); + snprintf (buf, sizeof (buf), NTXT ("%llu"), (long long) sel); + sb.append (buf); + } + if (found == 1) + sb.append (')'); + + switch (type) + { + case DSP_DLAYOUT: + case DSP_DATAOBJ: + sb.append (NTXT (" SOME IN DOBJ")); + break; + } + sb.append (')'); + return sb.toString (); +} + +// +// Get load object states +// +Vector<void *> * +dbeGetLoadObjectList (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + int size = lobjs->size (); + + // Initialize Java boolean array + Vector<char *> *names = new Vector<char *>(size); + Vector<int> *states = new Vector<int>(size); + Vector<int> *indices = new Vector<int>(size); + Vector<char *> *paths = new Vector<char *>(size); + Vector<int> *isJava = new Vector<int>(size); + + // Get load object states + int index; + LoadObject *lo; + char *lo_name; + + // lobjectsNoJava is a trimmed list of indices provided to front-end skipping the Java + // classes. lobjectsNoJava preserves the mapping of the index into the complete lobjs + // vector. What front-end sees as lobj[i] is really lobj[lobjectsNoJava[i]]; + + // This list is constructed every time GetLoadObjectList() or GetLoadObjectState() is + // called. Possibility of further optimization by making it more persistent. + // Only consumer of this list is dbeSetLoadObjectState + int new_index = 0; + if (dbev->lobjectsNoJava == NULL) + dbev->lobjectsNoJava = new Vector<int>(1); + else + dbev->lobjectsNoJava->reset (); + + Vec_loop (LoadObject*, lobjs, index, lo) + { + // Set 0, 1, or 2 for show/hide/api + enum LibExpand expand = dbev->get_lo_expand (lo->seg_idx); + + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + isJava->store (new_index, 1); + else + isJava->store (new_index, 0); + } + else + isJava->store (new_index, 0); + dbev->lobjectsNoJava->append (index); + + names->store (new_index, dbe_sprintf (NTXT ("%s"), lo_name)); + states->store (new_index, (int) expand); + indices->store (new_index, (int) lo->seg_idx); + paths->store (new_index, dbe_sprintf (NTXT ("%s"), lo->get_pathname ())); + new_index++; + } + Vector<void*> *res = new Vector<void*>(5); + res->store (0, names); + res->store (1, states); + res->store (2, indices); + res->store (3, paths); + res->store (4, isJava); + delete lobjs; + return res; +} + +Vector<int> * +dbeGetLoadObjectState (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + int size = lobjs->size (); + + // Initialize Java boolean array + Vector<int> *states = new Vector<int>(size); + char *lo_name; + + // lobjectsNoJava is a trimmed list of indices provided to front-end skipping the Java + // classes. lobjectsNoJava preserves the mapping of the index into the complete lobjs + // vector. What front-end sees as lobj[i] is really lobj[lobjectsNoJava[i]]; + + // This list is constructed every time GetLoadObjectList() or GetLoadObjectState() is + // called. Possibility of further optimization by making it more persistent. + // Only consumer of this list is dbeSetLoadObjectState + int new_index = 0; + if (dbev->lobjectsNoJava == NULL) + dbev->lobjectsNoJava = new Vector<int>(1); + else + dbev->lobjectsNoJava->reset (); + + // Get load object states + int index; + LoadObject *lo; + + Vec_loop (LoadObject*, lobjs, index, lo) + { + // Set 0, 1, or 2 for show/hide/api + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + continue; + } + else + dbev->lobjectsNoJava->append (index); + + enum LibExpand expand = dbev->get_lo_expand (lo->seg_idx); + states->store (new_index, (int) expand); + new_index++; + } + delete lobjs; + return states; +} + +// Set load object states +void +dbeSetLoadObjectState (int dbevindex, Vector<int> *selected) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + + int index; + bool changed = false; + + LoadObject *lo; + int new_index = 0; + dbev->setShowAll (); + Vec_loop (LoadObject*, lobjs, index, lo) + { + if (dbev->lobjectsNoJava != NULL) + { + // This loadobject is a java class and was skipped + if (dbev->lobjectsNoJava->fetch (new_index) != index) + continue; + } + // Get array of settings + enum LibExpand expand = (enum LibExpand) selected->fetch (new_index); + if (expand == LIBEX_HIDE) + { + dbev->resetShowAll (); + dbeSession->set_lib_visibility_used (); + } + changed = changed | dbev->set_libexpand (lo->get_pathname (), expand); + new_index++; + } + delete lobjs; + if (changed == true) + { + dbev->setShowHideChanged (); + dbev->update_lo_expands (); + } + + return; +} + +// Reset load object states +void +dbeSetLoadObjectDefaults (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_libdefaults (); +} + +// Get Machine model +Vector<char*>* +dbeGetCPUVerMachineModel (int dbevindex) +{ + Vector<char*>* table = new Vector<char*>(); + DbeView *dbev = dbeSession->getView (dbevindex); + char * mach_model = dbev->get_settings ()->get_machinemodel (); + if (mach_model != NULL) + { + table->append (mach_model); + return table; + } + int grsize = dbeSession->expGroups->size (); + for (int j = 0; j < grsize; j++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (j); + Vector<Experiment*> *exps = gr->exps; + for (int i = 0, sz = exps->size (); i < sz; i++) + { + Experiment *exp = exps->fetch (i); + char *model = exp->machinemodel; + if (model != NULL) + table->append (dbe_strdup (model)); + } + } + return table; +} + +// automatically load machine model if applicable +void +dbeDetectLoadMachineModel (int dbevindex) +{ + if (dbeSession->is_datamode_available ()) + { + char *model = dbeGetMachineModel (); + if (model == NULL) + { + Vector<char*>* models = dbeGetCPUVerMachineModel (dbevindex); + char * machineModel = NTXT ("generic"); + if (models->size () > 0) + { + machineModel = models->get (0); + for (int i = 1; i < models->size (); i++) + { + if (strncmp (models->get (i), machineModel, strlen (machineModel)) == 0) + { + machineModel = NTXT ("generic"); + break; + } + } + dbeLoadMachineModel (machineModel); + } + delete models; + } + } +} + +// Managing Memory Objects +char * +dbeDefineMemObj (char *name, char *index_expr, char *machinemodel, + char *sdesc, char *ldesc) +{ + return MemorySpace::mobj_define (name, index_expr, machinemodel, sdesc, ldesc); +} + +char * +dbeDeleteMemObj (char *name) +{ + return MemorySpace::mobj_delete (name); +} + +Vector<void*> * +dbeGetMemObjects (int /*dbevindex*/) +{ + Vector<void*> *res = MemorySpace::getMemObjects (); + return res; +} + +// Managing machine model +char * +dbeLoadMachineModel (char *name) +{ + return dbeSession->load_mach_model (name); +} + +char * +dbeGetMachineModel () +{ + return dbeSession->get_mach_model (); +} + +Vector <char *> * +dbeListMachineModels () +{ + return dbeSession->list_mach_models (); +} + +// Managing Index Objects +char * +dbeDefineIndxObj (char *name, char *index_expr, char *sdesc, char *ldesc) +{ + return dbeSession->indxobj_define (name, NULL, index_expr, sdesc, ldesc); +} + +Vector<void*> * +dbeGetIndxObjDescriptions (int /*dbevindex*/) +{ + Vector<void*> *res = dbeSession->getIndxObjDescriptions (); + return res; +} + +Vector<void*> * +dbeGetCustomIndxObjects (int /*dbevindex*/) +{ + Vector<void*> *res = dbeSession->getCustomIndxObjects (); + return res; +} + +void +dbeSetSelObj (int dbevindex, Obj sel_obj_or_ind, int type, int subtype) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *sel_obj; + Hist_data *data; + int sel_ind = (int) sel_obj_or_ind; + + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_CALLER: + data = dbev->callers; + break; + case DSP_CALLEE: + data = dbev->callees; + break; + case DSP_SOURCE: + data = dbev->src_data; + break; + case DSP_DISASM: + data = dbev->dis_data; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + if (data == NULL) + { + dbev->sel_binctx = NULL; + return; + } + if (sel_ind >= 0 && sel_ind < dbev->dlay_data->size ()) + dbev->sel_dobj = dbev->dlay_data->fetch (sel_ind)->obj; + return; + case DSP_DATAOBJ: + data = dbev->dobj_data; + if (data == NULL) + { + dbev->sel_binctx = NULL; + return; + } + if (sel_ind >= 0 && sel_ind < dbev->dobj_data->size ()) + dbev->sel_dobj = dbev->dobj_data->fetch (sel_ind)->obj; + return; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + dbev->set_indxobj_sel (subtype, sel_ind); + sel_obj = dbev->get_indxobj_sel (subtype); + if (sel_obj && sel_obj->get_type () == Histable::INDEXOBJ) + dbev->set_sel_obj (((IndexObject*) sel_obj)->get_obj ()); + return; + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + case DSP_TIMELINE: + case DSP_LEAKLIST: + case DSP_RACES: + case DSP_DEADLOCKS: + case DSP_DUALSOURCE: + case DSP_SOURCE_DISASM: + case DSP_IOACTIVITY: + case DSP_IOVFD: + case DSP_IOCALLSTACK: + case DSP_HEAPCALLSTACK: + case DSP_MINICALLER: + dbev->set_sel_obj ((Histable *) sel_obj_or_ind); + return; + default: + // abort(); + return; + } + if (type != DSP_SOURCE && type != DSP_DISASM && type != DSP_SOURCE_V2 + && type != DSP_DISASM_V2) + dbev->sel_binctx = NULL; + + if (data == NULL || data->get_status () != Hist_data::SUCCESS + || sel_ind >= data->size ()) + return; + + if (sel_ind >= 0 && sel_ind < data->size ()) + dbev->set_sel_obj (data->fetch (sel_ind)->obj); +} + +void +dbeSetSelObjV2 (int dbevindex, uint64_t id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_sel_obj (dbeSession->findObjectById (id)); +} + +Obj +dbeGetSelObj (int dbevindex, int type, int subtype) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Histable *sel_obj = NULL; + switch (type) + { + case DSP_FUNCTION: + sel_obj = dbev->get_sel_obj (Histable::FUNCTION); + break; + case DSP_LINE: + case DSP_SOURCE: + case DSP_SOURCE_V2: + sel_obj = dbev->get_sel_obj (Histable::LINE); + break; + case DSP_PC: + case DSP_DISASM: + case DSP_DISASM_V2: + sel_obj = dbev->get_sel_obj (Histable::INSTR); + break; + case DSP_SRC_FILE: + sel_obj = dbev->get_sel_obj (Histable::SOURCEFILE); + break; + case DSP_DATAOBJ: + case DSP_DLAYOUT: + if (dbev->sel_dobj) + sel_obj = dbev->sel_dobj->convertto (Histable::DOBJECT); + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + sel_obj = dbev->get_indxobj_sel (subtype); + break; + default: + abort (); + } + Dprintf (DEBUG_DBE, NTXT ("### dbeGetSelObj: Dbe.cc:%d %s (%d) returns %s\n"), + __LINE__, dsp_type_to_string (type), type, sel_obj ? sel_obj->dump () : "NULL"); + return (Obj) sel_obj; +} + +Obj +dbeConvertSelObj (Obj obj, int type) +{ + Histable *sel_obj = (Histable *) obj; + Dprintf (DEBUG_DBE, NTXT ("### dbeConvertSelObj: Dbe.cc:%d %s (%d) sel_obj=%s\n"), + __LINE__, dsp_type_to_string (type), type, sel_obj ? sel_obj->dump () + : "NULL"); + if (sel_obj == NULL) + return (Obj) NULL; + switch (type) + { + case DSP_FUNCTION: + return (Obj) sel_obj->convertto (Histable::FUNCTION); + case DSP_LINE: + return (Obj) sel_obj->convertto (Histable::LINE); + case DSP_SOURCE: + case DSP_SOURCE_V2: + { + SourceFile* srcCtx = NULL; + if (sel_obj->get_type () == Histable::INSTR) + { + DbeInstr* dbei = (DbeInstr *) sel_obj; + srcCtx = (SourceFile*) dbei->convertto (Histable::SOURCEFILE); + } + else if (sel_obj->get_type () == Histable::LINE) + { + DbeLine * dbel = (DbeLine *) sel_obj; + srcCtx = dbel->sourceFile; + } + sel_obj = sel_obj->convertto (Histable::LINE, srcCtx); + Dprintf (DEBUG_DBE, NTXT ("### dbeConvertSelObj: Dbe.cc:%d %s (%d) returns %s\n"), + __LINE__, dsp_type_to_string (type), type, sel_obj ? sel_obj->dump () : "NULL"); + if (sel_obj && sel_obj->get_type () == Histable::LINE) + { + DbeLine * dbel = (DbeLine *) sel_obj; + return (Obj) dbel->dbeline_base; + } + return (Obj) sel_obj->convertto (Histable::LINE, srcCtx); + } + case DSP_PC: + case DSP_DISASM: + case DSP_DISASM_V2: + return (Obj) sel_obj->convertto (Histable::INSTR); + case DSP_SRC_FILE: + return (Obj) sel_obj->convertto (Histable::SOURCEFILE); + default: + abort (); + } + return (Obj) NULL; +} + +uint64_t +dbeGetSelObjV2 (int dbevindex, char *typeStr) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = NULL; + if (typeStr != NULL) + { + if (streq (typeStr, NTXT ("FUNCTION"))) + obj = dbev->get_sel_obj (Histable::FUNCTION); + else if (streq (typeStr, NTXT ("INSTRUCTION"))) + obj = dbev->get_sel_obj (Histable::INSTR); + else if (streq (typeStr, NTXT ("SOURCELINE"))) + obj = dbev->get_sel_obj (Histable::LINE); + else if (streq (typeStr, NTXT ("SOURCEFILE"))) + obj = dbev->get_sel_obj (Histable::SOURCEFILE); + } + Dprintf (DEBUG_DBE, NTXT ("### dbeGetSelObjV2: Dbe.cc:%d %s returns %s\n"), + __LINE__, STR (typeStr), obj ? obj->dump () : "NULL"); + return obj != NULL ? obj->id : (uint64_t) - 1; +} + +Vector<uint64_t> * +dbeGetSelObjsIO (int dbevindex, Vector<uint64_t> *ids, int type) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<uint64_t> *res = NULL; + Vector<uint64_t> *result = new Vector<uint64_t>(); + for (int i = 0; i < ids->size (); i++) + { + res = dbeGetSelObjIO (dbevindex, ids->fetch (i), type); + if (res != NULL) + { + result->addAll (res); + delete res; + } + } + return result; +} + +Vector<uint64_t> * +dbeGetSelObjIO (int dbevindex, uint64_t id, int type) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = NULL; + Vector<uint64_t> *res = NULL; + int size = 0; + switch (type) + { + case DSP_IOACTIVITY: + obj = dbev->get_sel_obj_io (id, Histable::IOACTFILE); + size = obj != NULL ? ((FileData*) obj)->getVirtualFds ()->size () : 0; + if (size) + { + res = new Vector<uint64_t>(); + Vector<int64_t> *vfds = ((FileData*) obj)->getVirtualFds (); + for (int i = 0; i < size; i++) + res->append (vfds->fetch (i)); + } + break; + case DSP_IOVFD: + obj = dbev->get_sel_obj_io (id, Histable::IOACTVFD); + if (obj) + { + res = new Vector<uint64_t>(); + res->append (obj->id); + } + break; + case DSP_IOCALLSTACK: + obj = dbev->get_sel_obj_io (id, Histable::IOCALLSTACK); + if (obj) + { + Vector<Obj> *instrs = dbeGetStackPCs (dbevindex, obj->id); + if (instrs == NULL) + return NULL; + int stsize = instrs->size (); + res = new Vector<uint64_t>(stsize); + for (int i = 0; i < stsize; i++) + { + Histable *objFunc = (DbeInstr*) (instrs->fetch (i)); + if (objFunc->get_type () != Histable::LINE) + { + objFunc = objFunc->convertto (Histable::FUNCTION); + res->insert (0, objFunc->id); + } + } + delete instrs; + } + break; + default: + break; + } + return res; +} + +uint64_t +dbeGetSelObjHeapTimestamp (int dbevindex, uint64_t id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = NULL; + uint64_t res = 0; + Vector<uint64_t> *peakStackIds; + Vector<hrtime_t> *peakTimestamps; + + // Find and return the timestamp for the peak + bool foundPeakId = false; + if (id > 0) + { + obj = dbev->get_sel_obj_heap (0); + if (obj != NULL) + { + peakStackIds = ((HeapData*) obj)->getPeakStackIds (); + peakTimestamps = ((HeapData*) obj)->getPeakTimestamps (); + for (int i = 0; i < peakStackIds->size (); i++) + { + if (id == peakStackIds->fetch (i)) + { + res = peakTimestamps->fetch (i); + foundPeakId = true; + break; + } + } + } + } + + // Return the first timestamp for the peak + // if the callstack id is zero or it + // doesn't match with the peak stack id + if (id == 0 || !foundPeakId) + { + obj = dbev->get_sel_obj_heap (0); + res = obj != NULL ? ((HeapData*) obj)->getPeakTimestamps ()->fetch (0) : 0; + } + return res; +} + +int +dbeGetSelObjHeapUserExpId (int dbevindex, uint64_t id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = NULL; + int res = 0; + obj = dbev->get_sel_obj_heap (id); + res = obj != NULL ? ((HeapData*) obj)->getUserExpId () : 0; + return res; +} + +// +// Get index of selected function/object +// +int +dbeGetSelIndex (int dbevindex, Obj sel_obj, int type, int subtype) +{ + Hist_data *data; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + data = dbev->get_indxobj_data (subtype); + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return -1; + + Histable *chk_obj = (Histable *) sel_obj; + Vector<Hist_data::HistItem*> *histItems = data->get_hist_items (); + if (histItems == NULL || chk_obj == NULL) + return -1; + for (int i = 0, sz = histItems->size (); i < sz; i++) + { + if (histItems->get (i)->obj == chk_obj) + return i; + if (histItems->get (i)->obj == NULL) + continue; + if (histItems->get (i)->obj->get_type () == Histable::LINE + && chk_obj->get_type () == Histable::LINE) + { + if (((DbeLine*) histItems->get (i)->obj)->convertto (Histable::FUNCTION) + == ((DbeLine*) chk_obj)->convertto (Histable::FUNCTION) + && ((DbeLine*) histItems->get (i)->obj)->lineno + == ((DbeLine*) chk_obj)->lineno) + return i; + } + else if (histItems->get (i)->obj->get_type () == Histable::INSTR + && chk_obj->get_type () == Histable::INSTR) + if (((DbeInstr*) histItems->get (i)->obj)->convertto (Histable::FUNCTION) + == ((DbeInstr*) chk_obj)->convertto (Histable::FUNCTION) + && ((DbeInstr*) histItems->get (i)->obj)->addr + == ((DbeInstr*) chk_obj)->addr) + return i; + } + + Histable *chk_obj1 = NULL; + switch (type) + { + case DSP_FUNCTION: + chk_obj1 = chk_obj->convertto (Histable::FUNCTION); + break; + case DSP_LINE: + case DSP_SOURCE: + case DSP_SOURCE_V2: + chk_obj1 = chk_obj->convertto (Histable::LINE); + break; + case DSP_PC: + case DSP_DISASM: + case DSP_DISASM_V2: + chk_obj1 = chk_obj->convertto (Histable::INSTR); + break; + } + if (chk_obj1 && chk_obj != chk_obj1) + for (int i = 0, sz = histItems->size (); i < sz; i++) + if (histItems->get (i)->obj == chk_obj1) + return i; + + if (type == DSP_LINE) + { + for (int i = 0, sz = histItems->size (); i < sz; i++) + if (histItems->get (i)->obj != NULL + && chk_obj->convertto (Histable::FUNCTION) + == histItems->get (i)->obj->convertto (Histable::FUNCTION)) + return i; + } + else if (type == DSP_PC) + { + for (int i = 0, sz = histItems->size (); i < sz; i++) + if (histItems->get (i)->obj != NULL + && (histItems->get (i)->obj)->convertto (Histable::FUNCTION) + == (chk_obj)->convertto (Histable::FUNCTION) + && ((DbeLine*) histItems->get (i)->obj->convertto (Histable::LINE))->lineno + == ((DbeLine*) chk_obj->convertto (Histable::LINE))->lineno) + return i; + for (int i = 0, sz = histItems->size (); i < sz; i++) + if (histItems->get (i)->obj != NULL + && (histItems->get (i)->obj)->convertto (Histable::FUNCTION) + == (chk_obj)->convertto (Histable::FUNCTION)) + return i; + } + + // If we clicked on an mfunction line in the called-by call mini in user mode for omp + // we might not find that function in func data + if (dbev->isOmpDisMode () && type == DSP_FUNCTION) + { + int p = dbeGetSelIndex (dbevindex, sel_obj, DSP_DISASM, subtype); + if (p != -1) + return p; + } + return -1; +} + +// Print data +// +char * +dbePrintData (int dbevindex, int type, int subtype, char *printer, + char *fname, FILE *outfile) +{ + Histable *current_obj; + Function *func; + Module *module; + MetricList *mlist_orig; + bool header; + Print_params params; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + // Set print parameters + if (printer != NULL) + { + params.dest = DEST_PRINTER; + params.name = printer; + } + else if (outfile != NULL) + { + params.dest = DEST_OPEN_FILE; + params.openfile = outfile; + params.name = NULL; + } + else + { + params.dest = DEST_FILE; + params.name = fname; + if (*(params.name) == '\0') + { + free (params.name); + return dbe_strdup (GTXT ("Please enter the name of the file to which to print")); + } + } + params.ncopies = 1; + if (outfile != NULL) + header = false; + else + header = !(type == DSP_SOURCE || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2); + + params.header = header; + + // figure out what kind of metrics to use + if (type == DSP_SELF || type == DSP_CALLER || type == DSP_CALLEE + || type == DSP_CALLTREE) + mlist_orig = dbev->get_metric_list (MET_CALL); + else if (type == DSP_DATAOBJ || type == DSP_DLAYOUT || type == DSP_MEMOBJ) + mlist_orig = dbev->get_metric_list (MET_DATA); + else if (type == DSP_INDXOBJ) + mlist_orig = dbev->get_metric_list (MET_INDX); + else if (type == DSP_IOACTIVITY || type == DSP_IOVFD + || type == DSP_IOCALLSTACK) + mlist_orig = dbev->get_metric_list (MET_IO); + else if (type == DSP_HEAPCALLSTACK) + mlist_orig = dbev->get_metric_list (MET_HEAP); + else + mlist_orig = dbev->get_metric_list (MET_NORMAL); + + // make a compacted version of the input list + // the list will either be moved to the generated data, + // or freed below if it wasn't needed + MetricList *mlist = new MetricList (mlist_orig); + Hist_data *data = NULL; + er_print_common_display *cd = NULL; + int ix; + // Set data + switch (type) + { + case DSP_FUNCTION: + case DSP_LINE: + case DSP_PC: + case DSP_MEMOBJ: + case DSP_INDXOBJ: + case DSP_DATAOBJ: + data = dbev->get_hist_data (mlist, + ((type == DSP_FUNCTION) ? Histable::FUNCTION : + (type == DSP_LINE) ? Histable::LINE : + (type == DSP_PC) ? Histable::INSTR : + (type == DSP_INDXOBJ) ? Histable::INDEXOBJ : + (type == DSP_MEMOBJ) ? Histable::MEMOBJ + : Histable::DOBJECT), + subtype, Hist_data::ALL); + if (data->get_status () != Hist_data::SUCCESS) + return DbeView::status_str (DbeView::DBEVIEW_NO_DATA); // no strdup() + + cd = new er_print_histogram (dbev, data, mlist, MODE_LIST, + dbev->get_limit (), + mlist->get_sort_name (), NULL, true, true); + break; + case DSP_DLAYOUT: + { + data = dbev->get_hist_data (mlist, Histable::DOBJECT, 0, Hist_data::LAYOUT); + if (data->get_status () != Hist_data::SUCCESS) + return DbeView::status_str (DbeView::DBEVIEW_NO_DATA); // no strdup() + cd = new er_print_histogram (dbev, data, mlist, MODE_ANNOTATED, + dbev->get_thresh_dis (), + mlist->get_sort_name (), NULL, true, true); + break; + } + + // source and disassembly + case DSP_SOURCE: + case DSP_DISASM: + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + if (dbev->sel_obj == NULL) + return NULL; + current_obj = dbev->sel_obj->convertto (Histable::FUNCTION); + if (current_obj->get_type () != Histable::FUNCTION) + return dbe_strdup (GTXT ("Not a real function; no source or disassembly available.")); + func = (Function*) current_obj->convertto (Histable::FUNCTION); + if (func->flags & FUNC_FLAG_SIMULATED) + return dbe_strdup (GTXT ("Not a real function; no source or disassembly available.")); + if (func->get_name () == NULL) + return dbe_strdup (GTXT ("Source location not recorded in experiment")); + module = func->module; + if (module == NULL || module->get_name () == NULL) + return dbe_strdup (GTXT ("Object name not recorded in experiment")); + ix = module->loadobject->seg_idx; + if (dbev->get_lo_expand (ix) == LIBEX_HIDE) + return dbe_strdup (GTXT ("No source or disassembly available for hidden object")); + cd = new er_print_histogram (dbev, dbev->func_data, mlist, MODE_ANNOTATED, + type == DSP_DISASM || type == DSP_DISASM_V2, + mlist->get_sort_name (), + func, false, false); + break; + + // callers-callees + case DSP_SELF: + case DSP_CALLER: + case DSP_CALLEE: + if (dbev->sel_obj == NULL) + return NULL; + current_obj = dbev->sel_obj->convertto (Histable::FUNCTION); + cd = new er_print_histogram (dbev, dbev->func_data, mlist, MODE_GPROF, 1, + mlist->get_sort_name (), current_obj, + false, false); + break; + + // statistics; this won't use the metric list copied above, so delete it + case DSP_STATIS: + cd = new er_print_experiment (dbev, 0, dbeSession->nexps () - 1, + true, true, true, true, false); + delete mlist; + break; + case DSP_EXP: + cd = new er_print_experiment (dbev, 0, dbeSession->nexps () - 1, + true, true, false, false, false); + delete mlist; + break; + case DSP_LEAKLIST: + cd = new er_print_leaklist (dbev, true, true, dbev->get_limit ()); + delete mlist; + break; + case DSP_HEAPCALLSTACK: + cd = new er_print_heapactivity (dbev, Histable::HEAPCALLSTACK, false, + dbev->get_limit ()); + delete mlist; + break; + case DSP_IOACTIVITY: + cd = new er_print_ioactivity (dbev, Histable::IOACTFILE, false, + dbev->get_limit ()); + delete mlist; + break; + case DSP_IOVFD: + cd = new er_print_ioactivity (dbev, Histable::IOACTVFD, false, + dbev->get_limit ()); + delete mlist; + break; + + // the io call stack + case DSP_IOCALLSTACK: + cd = new er_print_ioactivity (dbev, Histable::IOCALLSTACK, false, + dbev->get_limit ()); + delete mlist; + break; + + // some unknown panel -- return an error string + default: + delete mlist; + return dbe_strdup (GTXT ("Print not available")); + } + + // Start printing + char *buf = NULL; + + // first open the file/device/whatever + if (cd->open (¶ms) == 0) + { + // now call the actual print routine + cd->data_dump (); + if (params.dest == DEST_PRINTER) + { + if (streq ((char *) params.name, NTXT ("-"))) + { + // Special case - return report to the GUI + int maxbytes = 2 * 1024 * 1024; // IPC large buffer limit + char *report = cd->get_output (maxbytes); + delete data; + delete cd; + return report; // TEMPORARY + } + } + if (cd->print_output () == false) + buf = dbe_sprintf (NTXT ("%s: %s"), + GTXT ("Unable to submit print request to"), + params.name); + } + else + // if unable to set up the print, return an error + buf = dbe_sprintf (NTXT ("%s: %s"), + GTXT ("Unable to open file"), + params.name); + + // dbe_free((void *) params.name); XXX when should this happen? + if (data) + if (data->isViewOwned () == false) + delete data; + delete cd; + return buf; +} + +// Set limit for print data +// +char * +dbeSetPrintLimit (int dbevindex, int limit) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return (dbev->set_limit (limit)); +} + +// get limit for print data +int +dbeGetPrintLimit (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + int limit = dbev->get_limit (); + return limit; +} + +// set printmode for data +char * +dbeSetPrintMode (int dbevindex, char * pmode) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + char *r = dbev->set_printmode (pmode); + return r; +} + +// get printmode for data +int +dbeGetPrintMode (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return (dbev->get_printmode ()); +} + +// get printmode for data +char * +dbeGetPrintModeString (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return ( dbev->get_printmode_str ()); +} + +// get print delimiter for csv data +char +dbeGetPrintDelim (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return (dbev->get_printdelimiter ()); +} + +// Set Source/Object/Load-Object file names +static void +set_file_names (Function *func, char *names[3]) +{ + Module *module = func->module; + LoadObject *loadobject = module->loadobject; + if (loadobject == NULL) + loadobject = dbeSession->get_Unknown_LoadObject (); + free (names[0]); + free (names[1]); + free (names[2]); + SourceFile *sf = func->getDefSrc (); + char *src_name = sf->dbeFile->get_location_info (); + DbeFile *df = module->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_JAVACLASS) == 0) + df = module->loadobject->dbeFile; + char *lo_name = df->get_location_info (); + char *dot_o_name = lo_name; + if (module->dot_o_file) + dot_o_name = module->dot_o_file->dbeFile->get_location_info (); + names[0] = dbe_sprintf (NTXT ("%s: %s"), GTXT ("Source File"), src_name); + names[1] = dbe_sprintf (NTXT ("%s: %s"), GTXT ("Object File"), dot_o_name); + names[2] = dbe_sprintf (NTXT ("%s: %s"), GTXT ("Load Object"), lo_name); +} + +// dbeSetFuncData +// Master function to generate all Tab data for the analyzer +// Returns the index of the selected item in the specified list +// +// After calling it to set up, the Analyzer calls dbeGetFuncList +// to format the generated data and return the table +// Most of the data is destined for a JTable +// +int +dbeSetFuncData (int dbevindex, Obj sel_obj, int type, int subtype) +{ + MetricList *_mlist; + Histable *org_obj; + Hist_data *data = NULL; + int index, sel_index; + Function *func; + char *name; + int ix; + + DbeView *dbev = dbeSession->getView (dbevindex); + sel_index = -1; + dbev->resetOmpDisMode (); + dbev->error_msg = dbev->warning_msg = NULL; + + // get metric list, make a compact duplicate + _mlist = dbev->get_metric_list (MET_NORMAL); + MetricList *mlist = new MetricList (_mlist); + + // Remove old function/obj list data & Get new function/obj list data + org_obj = (Histable *) sel_obj; + + // Figure out which "function" data is being asked for, i.e., + // which of the analyzer displays is asking for data + switch (type) + { + // the various tables: functions, lines, PCs, DataObjects, IndexObjects + case DSP_FUNCTION: + case DSP_LINE: + case DSP_PC: + case DSP_DATAOBJ: + case DSP_MEMOBJ: + case DSP_INDXOBJ: + switch (type) + { + case DSP_FUNCTION: + if (dbev->func_data) + delete dbev->func_data; + dbev->func_data = data = dbev->get_hist_data (mlist, + Histable::FUNCTION, subtype, Hist_data::ALL); + break; + case DSP_LINE: + if (dbev->line_data) + delete dbev->line_data; + dbev->line_data = data = dbev->get_hist_data (mlist, + Histable::LINE, subtype, Hist_data::ALL); + break; + case DSP_PC: + if (dbev->pc_data) + delete dbev->pc_data; + dbev->pc_data = data = dbev->get_hist_data (mlist, + Histable::INSTR, subtype, Hist_data::ALL); + break; + case DSP_DATAOBJ: + if (dbev->dobj_data) + delete dbev->dobj_data; + mlist = dbev->get_metric_list (MET_DATA); + dbev->dobj_data = data = dbev->get_hist_data (mlist, + Histable::DOBJECT, subtype, Hist_data::ALL); + break; + case DSP_MEMOBJ: + mlist = dbev->get_metric_list (MET_DATA); + data = dbev->get_hist_data (mlist, Histable::MEMOBJ, subtype, + Hist_data::ALL); + dbev->indx_data->store (subtype, data); + break; + case DSP_INDXOBJ: + mlist = dbev->get_metric_list (MET_INDX); + data = dbev->get_hist_data (mlist, Histable::INDEXOBJ, subtype, + Hist_data::ALL); + dbev->indx_data->store (subtype, data); + break; + default: + break; + } + + // Set the selection of row item + if (data->get_status () == Hist_data::SUCCESS) + { + // otherwise, look for it + sel_index = -1; + if (org_obj) + { + Hist_data::HistItem *hi; + Vec_loop (Hist_data::HistItem*, data->get_hist_items (), index, hi) + { + if (hi->obj == org_obj) + { + sel_index = index; + break; + } + } + if (sel_index == -1 && (type == DSP_LINE || type == DSP_PC)) + { + Vec_loop (Hist_data::HistItem*, data->get_hist_items (), index, hi) + { + name = hi->obj->get_name (); + if (strcmp (name, NTXT ("<Total>")) && + strcmp (name, GTXT ("<Unknown>"))) + { + int done = 0; + switch (type) + { + case DSP_LINE: + if (org_obj->convertto (Histable::FUNCTION) + == hi->obj->convertto (Histable::FUNCTION)) + { + sel_index = index; + done = 1; + } + break; + case DSP_PC: + if (hi->obj->convertto (Histable::FUNCTION) + == org_obj->convertto (Histable::FUNCTION) + && ((DbeLine*) hi->obj->convertto (Histable::LINE))->lineno + == ((DbeLine*) org_obj->convertto (Histable::LINE))->lineno) + { + sel_index = index; + done = 1; + } + break; + } + if (done) + break; + } + } + } + if (sel_index == -1 && type == DSP_PC) + { + Vec_loop (Hist_data::HistItem*, data->get_hist_items (), index, hi) + { + name = hi->obj->get_name (); + if (strcmp (name, NTXT ("<Total>")) && + strcmp (name, GTXT ("<Unknown>"))) + { + int done = 0; + if (hi->obj->convertto (Histable::FUNCTION) == + org_obj->convertto (Histable::FUNCTION)) + { + sel_index = index; + done = 1; + } + if (done) + break; + } + } + } + } + if (sel_index == -1) + { + Hist_data::HistItem *hi; + Vec_loop (Hist_data::HistItem*, data->get_hist_items (), index, hi) + { + name = hi->obj->get_name (); + if (strcmp (name, NTXT ("<Total>")) && + strcmp (name, GTXT ("<Unknown>"))) + { + sel_index = index; + break; + } + } + } + } + else + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + // the end of the code for the regular tables + + // Data Layout + case DSP_DLAYOUT: + if (dbev->dlay_data) + delete dbev->dlay_data; + dbev->marks->reset (); + mlist = dbev->get_metric_list (MET_DATA); + + // initial dobj list ... + data = dbev->get_hist_data (mlist, Histable::DOBJECT, subtype, + Hist_data::LAYOUT); + // .. provides metric data for layout + dbev->dlay_data = data = dbev->get_data_space ()->get_layout_data (data, + dbev->marks, dbev->get_thresh_dis ()); + if (data->get_status () != Hist_data::SUCCESS) + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + + // Source or disassembly + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + case DSP_SOURCE: + case DSP_DISASM: + { + if (org_obj == NULL) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_SEL_OBJ); + return sel_index; + } + if (org_obj->get_type () != Histable::FUNCTION) + { + dbev->error_msg = dbe_strdup ( + GTXT ("Not a real function; no source or disassembly available.")); + return sel_index; + } + func = (Function *) org_obj; + if (func->flags & FUNC_FLAG_SIMULATED) + { + dbev->error_msg = dbe_strdup ( + GTXT ("Not a real function; no source or disassembly available.")); + return sel_index; + } + if (func->get_name () == NULL) + { + dbev->error_msg = dbe_strdup ( + GTXT ("Source location not recorded in experiment")); + return sel_index; + } + Module *module = func->module; + if ((module == NULL) || (module->get_name () == NULL)) + { + dbev->error_msg = dbe_strdup ( + GTXT ("Object name not recorded in experiment")); + return sel_index; + } + ix = module->loadobject->seg_idx; + if (dbev->get_lo_expand (ix) == LIBEX_HIDE) + { + dbev->error_msg = dbe_strdup ( + GTXT ("No source or disassembly available for hidden object")); + return sel_index; + } + + if ((type == DSP_DISASM || type == DSP_DISASM_V2) + && dbev->get_view_mode () == VMODE_USER + && dbeSession->is_omp_available ()) + dbev->setOmpDisMode (); + + dbev->marks->reset (); + SourceFile *srcContext = NULL; + switch (dbev->sel_obj->get_type ()) + { + case Histable::FUNCTION: + { + Function *f = (Function *) dbev->sel_obj; + srcContext = f->getDefSrc (); + dbev->sel_binctx = f->module; + break; + } + case Histable::LINE: + { + DbeLine *dl = (DbeLine *) dbev->sel_obj; + srcContext = dl->sourceFile; + Function *f = dl->func; + if (f) + dbev->sel_binctx = f; + break; + } + case Histable::INSTR: + { + Function *f = (Function *) dbev->sel_obj->convertto (Histable::FUNCTION); + if (f) + { + dbev->sel_binctx = f; + srcContext = f->getDefSrc (); + } + break; + } + default: + break; + } + mlist = dbev->get_metric_list (MET_SRCDIS); + + // for source and disassembly the name needs to be invisible, + // but that's handled in the module code + if (type == DSP_SOURCE) + { + if (dbev->src_data) + delete dbev->src_data; + + // func_data computation needed for get_totals + if (dbev->func_data == NULL) + dbev->func_data = data = dbev->get_hist_data (mlist, + Histable::FUNCTION, subtype, Hist_data::ALL); + dbev->marks2dsrc->reset (); + dbev->marks2dsrc_inc->reset (); + data = dbev->src_data = module->get_data (dbev, mlist, + Histable::LINE, dbev->func_data->get_totals ()->value, + srcContext, func, dbev->marks, + dbev->get_thresh_src (), dbev->get_src_compcom (), + dbev->get_src_visible (), dbev->get_hex_visible (), + false, false, dbev->marks2dsrc, dbev->marks2dsrc_inc); + set_file_names (func, dbev->names_src); + if (srcContext) + { + free (dbev->names_src[0]); + dbev->names_src[0] = dbe_sprintf (GTXT ("Source File: %s"), + srcContext->dbeFile->get_location_info ()); + } + Obj obj = (Obj) func->convertto (Histable::LINE, srcContext); + sel_index = dbeGetSelIndex (dbevindex, obj, type, subtype); + } + else + { /* type == DSP_DISASM */ + if (dbev->dis_data) + delete dbev->dis_data; + + // func_data computation needed for get_totals + if (dbev->func_data == NULL) + dbev->func_data = data = dbev->get_hist_data (mlist, + Histable::FUNCTION, subtype, Hist_data::ALL); + dbev->marks2ddis->reset (); + dbev->marks2ddis_inc->reset (); + data = dbev->dis_data = module->get_data (dbev, mlist, + Histable::INSTR, dbev->func_data->get_totals ()->value, + srcContext, func, dbev->marks, dbev->get_thresh_dis (), + dbev->get_dis_compcom (), dbev->get_src_visible (), + dbev->get_hex_visible (), dbev->get_func_scope (), + false, dbev->marks2ddis, dbev->marks2ddis_inc); + set_file_names (func, dbev->names_dis); + if (srcContext) + { + free (dbev->names_dis[0]); + dbev->names_dis[0] = dbe_sprintf (GTXT ("Source File: %s"), + srcContext->dbeFile->get_location_info ()); + } + Obj obj = (Obj) func->convertto (Histable::INSTR); + sel_index = dbeGetSelIndex (dbevindex, obj, type, subtype); + } + return sel_index; + } + + // the three cases for caller-callee + case DSP_SELF: + case DSP_CALLER: + case DSP_CALLEE: + if (org_obj == NULL) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_SEL_OBJ); + return sel_index; + } + + // Caller data + if (dbev->callers) + delete dbev->callers; + mlist = dbev->get_metric_list (MET_CALL); + dbev->callers = dbev->get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::CALLERS, org_obj); + if (dbev->callers->get_status () != Hist_data::SUCCESS) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + } + + // Callee data + if (dbev->callees) + delete dbev->callees; + dbev->callees = dbev->get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::CALLEES, org_obj); + if (dbev->callees->get_status () != Hist_data::SUCCESS) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + } + + // Center Function item + if (dbev->fitem_data) + delete dbev->fitem_data; + dbev->fitem_data = dbev->get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::SELF, org_obj); + if (dbev->fitem_data->get_status () != Hist_data::SUCCESS) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + } + return sel_index; + default: + abort (); + } + return sel_index; +} + +Vector<void*>* +dbeGetTotals (int dbevindex, int dsptype, int subtype) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + MetricList *mlist = dbev->get_metric_list (dsptype, subtype); + Hist_data *data = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::ALL); + Hist_data::HistItem *totals = data->get_totals (); + Vector<void*> *tbl = new Vector<void*>(mlist->size ()); + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + switch (m->get_vtype ()) + { + case VT_DOUBLE: + { + Vector<double> *lst = new Vector<double>(1); + lst->append (totals->value[i].d); + tbl->append (lst); + break; + } + case VT_INT: + { + Vector<int> *lst = new Vector<int>(1); + lst->append (totals->value[i].i); + tbl->append (lst); + break; + } + case VT_LLONG: + case VT_ULLONG: + case VT_ADDRESS: + { + Vector<long long> *lst = new Vector<long long>(1); + lst->append (totals->value[i].ll); + tbl->append (lst); + break; + } + case VT_LABEL: + { + Vector<char *> *lst = new Vector<char *>(1); + Histable::NameFormat nfmt = dbev->get_name_format (); + lst->append (dbe_strdup (totals->obj->get_name (nfmt))); + tbl->append (lst); + break; + } + default: + abort (); + } + } + Vector<void*> *res = new Vector<void*>(2); + res->append (dbeGetMetricList (mlist)); + res->append (tbl); + return res; +} + +Vector<void*>* +dbeGetHotMarks (int dbevindex, int type) +{ + Vector<void*>* table = new Vector<void*>(2); + Vector<int>* table0 = new Vector<int> (); + Vector<int>* table1 = new Vector<int> (); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + return NULL; + + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + for (int i = 0; i < dbev->marks2dsrc->size (); i++) + { + table0->append (dbev->marks2dsrc->fetch (i).index1); + table1->append (dbev->marks2dsrc->fetch (i).index2); + } + break; + case DSP_DISASM: + case DSP_DISASM_V2: + for (int i = 0; i < dbev->marks2ddis->size (); i++) + { + table0->append (dbev->marks2ddis->fetch (i).index1); + table1->append (dbev->marks2ddis->fetch (i).index2); + } + break; + default: + break; + } + table->store (0, table0); + table->store (1, table1); + return table; +} + +Vector<void*>* +dbeGetHotMarksInc (int dbevindex, int type) +{ + Vector<void*>* table = new Vector<void*>(2); + Vector<int>* table0 = new Vector<int> (); + Vector<int>* table1 = new Vector<int> (); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + return NULL; + + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + for (int i = 0; i < dbev->marks2dsrc_inc->size (); i++) + { + table0->append (dbev->marks2dsrc_inc->fetch (i).index1); + table1->append (dbev->marks2dsrc_inc->fetch (i).index2); + } + break; + case DSP_DISASM: + case DSP_DISASM_V2: + for (int i = 0; i < dbev->marks2ddis_inc->size (); i++) + { + table0->append (dbev->marks2ddis_inc->fetch (i).index1); + table1->append (dbev->marks2ddis_inc->fetch (i).index2); + } + break; + default: + break; + } + table->store (0, table0); + table->store (1, table1); + return table; +} + +Vector<void*>* +dbeGetSummaryHotMarks (int dbevindex, Vector<Obj> *sel_objs, int type) +{ + Vector<void*>* table = new Vector<void*>(2); + Vector<int>* table0 = new Vector<int> (); + Vector<int>* table1 = new Vector<int> (); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + return NULL; + if (sel_objs == NULL || sel_objs->size () == 0) + return NULL; + + Hist_data *data; + Vector<int_pair_t> *marks2d; + Vector<int_pair_t>* marks2d_inc; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + marks2d = dbev->marks2dsrc; + marks2d_inc = dbev->marks2dsrc_inc; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + marks2d = dbev->marks2ddis; + marks2d_inc = dbev->marks2ddis_inc; + break; + default: + data = NULL; + marks2d = NULL; + marks2d_inc = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS + || marks2d_inc == NULL || marks2d == NULL) + return NULL; + + MetricList *orig_mlist = data->get_metric_list (); + MetricList *prop_mlist = new MetricList (dbev->get_metric_ref (MET_NORMAL)); + if (prop_mlist && dbev->comparingExperiments ()) + prop_mlist = dbev->get_compare_mlist (prop_mlist, 0); + Metric *mitem; + int index, index2; + index2 = 0; + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + + for (int i = 0; i < marks2d_inc->size (); i++) + { + int found = 0; + for (int j = 0; j < sel_objs->size (); j++) + { + int sel_index = (int) sel_objs->fetch (j); + int marked_index = marks2d_inc->fetch (i).index1; + if (sel_index == marked_index) + { + found = 1; + break; + } + } + if (!found) + continue; + int mindex = marks2d_inc->fetch (i).index2; + Metric *orig_metric = orig_mlist->get_items ()->fetch (mindex); + if (orig_metric->get_id () == mitem->get_id () + && mitem->get_subtype () == Metric::INCLUSIVE) + { + table0->append (index2); + table1->append (1); + } + } + + for (int i = 0; i < marks2d->size (); i++) + { + int found = 0; + for (int j = 0; j < sel_objs->size (); j++) + { + int sel_index = (int) sel_objs->fetch (j); + int marked_index = marks2d->fetch (i).index1; + if (sel_index == marked_index) + { + found = 1; + break; + } + } + if (!found) + continue; + int mindex = marks2d->fetch (i).index2; + Metric *orig_metric = orig_mlist->get_items ()->fetch (mindex); + if (orig_metric->get_id () == mitem->get_id () + && mitem->get_subtype () == Metric::EXCLUSIVE) + { + table0->append (index2); + table1->append (0); + } + } + if (!(mitem->get_subtype () == Metric::EXCLUSIVE + || mitem->get_subtype () == Metric::DATASPACE)) + index2++; + } + table->store (0, table0); + table->store (1, table1); + return table; +} + +// Get a vector of function ids of data(begin, begin + length - 1) +// Currently only support source/disassembly view +Vector<uint64_t>* +dbeGetFuncId (int dbevindex, int type, int begin, int length) +{ + Vector<uint64_t>* table = new Vector<uint64_t > (); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + Hist_data *data; + Function* given_func = NULL; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + default: + data = NULL; + abort (); + } + + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + if (begin < 0 || begin + length > data->size ()) + return NULL; + + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + case DSP_DISASM: + case DSP_DISASM_V2: + { + for (int i = begin; i < begin + length; i++) + { + given_func = NULL; + Histable * sel_obj = data->fetch (i)->obj; + if (sel_obj != NULL) + given_func = (Function*) (sel_obj)->convertto (Histable::FUNCTION, (Histable*) dbev); + if (given_func == NULL) + table->append (0); + else + table->append (given_func->id); + } + } + break; + default: + abort (); + } + return table; +} + +Vector<void*>* +dbeGetFuncCallerInfo (int dbevindex, int type, Vector<int>* idxs, int groupId) +{ + Vector<void*>* data = new Vector<void*>(); + if (type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + Obj sel_func = dbeGetSelObj (dbevindex, DSP_FUNCTION, 0); + if (sel_func == 0) + return data; + Vector<Obj> * cmpObjs = dbeGetComparableObjsV2 (dbevindex, sel_func, type); + if (cmpObjs == NULL) + return data; + DbeView *dbev = dbeSession->getView (dbevindex); + int mtype = MET_COMMON | COMPARE_BIT | ((groupId + 1) << GROUP_ID_SHIFT); + MetricList *mlist = dbev->get_metric_list ((MetricType) (mtype & MTYPE_MASK), + (mtype & COMPARE_BIT) != 0, + mtype >> GROUP_ID_SHIFT); + Histable *selObj = (Histable *) cmpObjs->fetch (groupId); + int subtype = 0; + Hist_data *hist_data = dbev->get_data (mlist, selObj, type, subtype); + if (hist_data == NULL) + return data; + } + for (int i = 0; i < idxs->size (); i++) + data->append (dbeGetFuncCallerInfoById (dbevindex, type, idxs->fetch (i))); + return data; +} + +// +// Get Table of Caller info: +// param: idx -- selected AT_FUNC row +// return: callsite_id, callsite_name (function: file: line) +Vector<void*>* +dbeGetFuncCallerInfoById (int dbevindex, int type, int idx) +{ + Vector<void*>* table = new Vector<void*>(3); + Vector<uint64_t>* table0 = new Vector<uint64_t> (); + Vector<int>* table1 = new Vector<int> (); + Vector<char*>* table2 = new Vector<char*>(); + + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Hist_data *data; + Function* given_func = NULL; + Vector<Histable*> *instr_info = NULL; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + default: + data = NULL; + abort (); + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + if (idx < 0 || idx >= data->size ()) + return NULL; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + case DSP_DISASM: + case DSP_DISASM_V2: + { + Histable * sel_obj = data->fetch (idx)->obj; + if (sel_obj == NULL) + return NULL; + given_func = (Function*) (sel_obj)->convertto (Histable::FUNCTION, (Histable*) dbev); + if (given_func == NULL) + return NULL; + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + instr_info = ptree->get_clr_instr (given_func); + DefaultMap<uint64_t, int> * line_seen = new DefaultMap<uint64_t, int>(); + for (int j = 0; j < ((Vector<Histable*>*)instr_info)->size (); j++) + { + Histable *instr = ((Vector<Histable*>*)instr_info)->fetch (j); + Function *cur_func = NULL; + if (instr->get_type () == Histable::INSTR) + cur_func = ((DbeInstr*) instr)->func; + else if (instr->get_type () == Histable::LINE) + cur_func = ((DbeLine*) instr)->func; + if (cur_func == NULL || (cur_func->flags & FUNC_FLAG_SIMULATED)) + continue; // skip functions like <Total> + Histable* line; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + if (cur_func != NULL) + { + SourceFile *sourceFile = cur_func->getDefSrc (); + if (sourceFile == NULL || + (sourceFile->flags & SOURCE_FLAG_UNKNOWN) != 0) + continue; // skip functions like <Function: %s, instructions without line numbers> + } + line = instr->convertto (Histable::LINE, NULL); + break; + case DSP_DISASM: + case DSP_DISASM_V2: + line = instr->convertto (Histable::INSTR, NULL); + break; + default: + abort (); + } + uint64_t func_id = cur_func->id; + uint64_t line_id = instr->id; + int is_null = 0; + int line_no = -1; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + is_null = (((DbeLine*) line)->func == NULL) ? 1 : 0; + if (is_null) + ((DbeLine*) line)->func = cur_func; + line_no = ((DbeLine*) line)->lineno; + if (line_seen->get (line_id) == 0) + { + line_seen->put (line_id, 1); + table0->append (func_id); + table1->append (line_no); + Histable::NameFormat nfmt = dbev->get_name_format (); + table2->append (dbe_strdup (line->get_name (nfmt))); + } + if (is_null) + ((DbeLine*) line)->func = NULL; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + is_null = (((DbeInstr*) line)->func == NULL) ? 1 : 0; + if (is_null) + ((DbeInstr*) line)->func = cur_func; + line_no = ((DbeInstr*) line)->addr; + if (line_seen->get (line_id) == 0) + { + line_seen->put (line_id, 1); + table0->append (func_id); + table1->append (line_no); + Histable::NameFormat nfmt = dbev->get_name_format (); + table2->append (dbe_strdup (line->get_name (nfmt))); + } + if (is_null) + ((DbeInstr*) line)->func = NULL; + break; + default: + abort (); + } + } + delete line_seen; + delete instr_info; + } + break; + default: + abort (); + } + table->store (0, table0); + table->store (1, table1); + table->store (2, table2); + return table; +} + +Vector<void*>* +dbeGetFuncCalleeInfo (int dbevindex, int type, Vector<int>* idxs, int groupId) +{ + Vector<void*>* data = new Vector<void*>(); + if (type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + Obj sel_func = dbeGetSelObj (dbevindex, DSP_FUNCTION, 0); + if (sel_func == 0) + return data; + Vector<Obj> * cmpObjs = dbeGetComparableObjsV2 (dbevindex, sel_func, type); + if (cmpObjs == NULL) + return data; + DbeView *dbev = dbeSession->getView (dbevindex); + int mtype = MET_COMMON | COMPARE_BIT | ((groupId + 1) << GROUP_ID_SHIFT); + MetricList *mlist = dbev->get_metric_list ((MetricType) (mtype & MTYPE_MASK), + (mtype & COMPARE_BIT) != 0, + mtype >> GROUP_ID_SHIFT); + Histable *selObj = (Histable *) cmpObjs->fetch (groupId); + int subtype = 0; + Hist_data *hist_data = dbev->get_data (mlist, selObj, type, subtype); + if (hist_data == NULL) + return data; + } + + for (int i = 0; i < idxs->size (); i++) + data->append (dbeGetFuncCalleeInfoById (dbevindex, type, idxs->fetch (i))); + return data; +} + +// +// Get Table of Callee info: +// param: idx -- selected AT_FUNC row +// return: callsite_row, callee_id, callee_name +// +Vector<void*>* +dbeGetFuncCalleeInfoById (int dbevindex, int type, int idx) +{ + Vector<void*>* table = new Vector<void*>(3); + Vector<int>* table0 = new Vector<int>(); + Vector<uint64_t>* table1 = new Vector<uint64_t > (); + Vector<char*>* table2 = new Vector<char*>(); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Hist_data *data; + Function* given_func = NULL; + Vector<Histable*> *instr_info = NULL; + Vector<void*> *func_info = NULL; + + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + default: + data = NULL; + abort (); + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + if (idx < 0 || idx >= data->size ()) + return NULL; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + case DSP_DISASM: + case DSP_DISASM_V2: + { + Histable * sel_obj = data->fetch (idx)->obj; + if (sel_obj == NULL) + return NULL; + given_func = (Function*) (sel_obj)->convertto (Histable::FUNCTION, (Histable*) dbev); + if (given_func == NULL) + return NULL; + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + Vector<Histable*> *instrs = NULL; + Vector<void*> *callee_instrs = ptree->get_cle_instr (given_func, instrs); + func_info = new Vector<void*>(); + instr_info = new Vector<Histable*>(); + for (long a = 0, sz_a = callee_instrs ? callee_instrs->size () : 0; a < sz_a; a++) + { + Vector<Histable*> *temp = ((Vector<Vector<Histable*>*>*)callee_instrs)->get (a); + DefaultMap<Function*, int> * func_seen = new DefaultMap<Function*, int>(); + Histable* instr0 = (Histable*) instrs->fetch (a); + for (long b = 0, sz_b = temp ? temp->size () : 0; b < sz_b; b++) + { + Histable *instr = temp->get (b); + if (instr->get_type () == Histable::INSTR) + { + Function* func1 = ((DbeInstr *) instr)->func; + func_seen->put (func1, 1); + } + else if (instr->get_type () == Histable::LINE) + { + Function* func1 = ((DbeLine *) instr)->func; + func_seen->put (func1, 1); + } + } + Vector<Function*> *funcs = func_seen->keySet (); + delete func_seen; + if (funcs->size () > 0) + { + instr_info->append (instr0); + func_info->append (funcs); + } + } + delete instrs; + destroy (callee_instrs); + + DefaultMap<uint64_t, Vector<int>* > * instr_idxs = new DefaultMap<uint64_t, Vector<int>* >(); + DefaultMap<uint64_t, int> * func_idxs = new DefaultMap<uint64_t, int>(); + for (long j = 0, sz_j = instr_info ? instr_info->size () : 0; j < sz_j; j++) + { + Histable *instr = instr_info->get (j); + Function *cur_func = NULL; + if (instr->get_type () == Histable::INSTR) + cur_func = ((DbeInstr*) instr)->func; + else if (instr->get_type () == Histable::LINE) + cur_func = ((DbeLine*) instr)->func; + if (cur_func != NULL && (cur_func->flags & FUNC_FLAG_SIMULATED)) + continue; // skip functions like <Total> + Histable* line; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + if (cur_func != NULL) + { + SourceFile *sourceFile = cur_func->getDefSrc (); + if (sourceFile == NULL || + (sourceFile->flags & SOURCE_FLAG_UNKNOWN) != 0) + // skip functions like <Function: %s, instructions without line numbers> + continue; + } + line = instr->convertto (Histable::LINE, NULL); + if (type == DSP_SOURCE_V2) + line = dbev->get_compare_obj (line); + break; + case DSP_DISASM: + case DSP_DISASM_V2: + line = instr; + if (type == DSP_DISASM_V2) + line = dbev->get_compare_obj (line); + break; + default: + abort (); + } + if (func_idxs->get (line->id) == 0) + { + func_idxs->put (line->id, 1); + Vector<int> *temp_idx = new Vector<int>(); + temp_idx->append (j); + instr_idxs->put (line->id, temp_idx); + } + else + { + Vector<int> *temp_idx = instr_idxs->get (line->id); + temp_idx->append (j); + } + } + for (long i = 0; i < data->size (); i++) + { + Histable* line = data->fetch (i)->obj; + if (line == NULL) + continue; + Vector<int> * instr_idx = instr_idxs->get (line->id); + if (instr_idx == NULL) + continue; + for (long j = 0; j < instr_idx->size (); j++) + { + Vector<void*>* callee_funcs_vec = (Vector<void*>*)func_info; + if (callee_funcs_vec->size () == 0) + continue; + Vector<Function*>* callee_funcs_value = (Vector<Function*>*)callee_funcs_vec->fetch (instr_idx->fetch (j)); + for (int k = 0; callee_funcs_value != NULL && k < callee_funcs_value->size (); k++) + { + uint64_t funcobj_id = ((Function*) callee_funcs_value->fetch (k))->id; + int old_size = table0->size (); + if (old_size > 0 && i == table0->fetch (old_size - 1) + && funcobj_id == table1->fetch (old_size - 1)) + continue; + table0->append (i); + table1->append (funcobj_id); + table2->append (dbe_strdup (((Function*) callee_funcs_value->fetch (k))->get_name ())); + } + } + } + delete instr_idxs; + delete func_idxs; + destroy (func_info); + delete instr_info; + } + break; + default: + abort (); + } + table->store (0, table0); + table->store (1, table1); + table->store (2, table2); + return table; +} + +// +// Get Table of Function List data with only total values +// +Vector<void*> * +dbeGetFuncListMini (int dbevindex, int type, int /*subtype*/) +{ + Hist_data *data; + DbeView *dbev = dbeSession->getView (dbevindex); + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + MetricList *mlist = data->get_metric_list (); + + // Get table size: count visible metrics + int nvisible = 0; + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + if (m->is_visible () || m->is_tvisible () || m->is_pvisible ()) + nvisible++; + } + Vector<void*> *table = new Vector<void*>(nvisible + 1); + + // Fill function list elements + Hist_data::HistItem *totals = data->get_totals (); + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + if (!m->is_visible () && !m->is_tvisible () && !m->is_pvisible ()) + continue; + TValue res; + TValue *v = data->get_value (&res, i, totals); + if ((m->get_visbits () & VAL_RATIO) != 0) + { + Vector<double> *col = new Vector<double>(1); + double d = (v->tag != VT_LABEL) ? v->to_double () : 100.; // NaN + col->append (d); + table->append (col); + continue; + } + switch (m->get_vtype ()) + { + case VT_INT: + { + Vector<int> *col = new Vector<int>(1); + col->append (v->i); + table->append (col); + break; + } + case VT_ADDRESS: + case VT_ULLONG: + case VT_LLONG: + { + Vector<long long> *col = new Vector<long long>(1); + col->append (v->ll); + table->append (col); + break; + } + case VT_LABEL: + { + Vector<char *> *col = new Vector<char *>(1); + col->append (dbe_strdup (v->l)); + table->append (col); + break; + } + case VT_DOUBLE: + default: + { + Vector<double> *col = new Vector<double>(1); + col->append (v->d); + table->append (col); + break; + } + } + } + table->append (NULL); + return table; +} + +// Get Table of Function List data +Vector<void*> * +dbeGetFuncList (int dbevindex, int type, int subtype) +{ + MetricList *mlist; + Metric *mitem; + int nitems, nvisible; + int index, index2, nv; + char *cell; + Vector<int> *ji_list; + Hist_data *data; + Hist_data::HistItem *item; + + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + // fprintf(stderr, NTXT("XXX dbeGetFuncList, FuncListDisp_type = %d\n"), type); + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + case DSP_SELF: + data = dbev->fitem_data; + break; + case DSP_CALLER: + data = dbev->callers; + break; + case DSP_CALLEE: + data = dbev->callees; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + data = dbev->get_indxobj_data (subtype); + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + mlist = data->get_metric_list (); + + // Get table size: count visible metrics + nitems = data->size (); + nvisible = 0; + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (mitem->is_visible () || mitem->is_tvisible () || mitem->is_pvisible ()) + nvisible++; + } + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(nvisible + 1); + + // Mark Hi-value metric items for annotated src/dis/layout + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_DLAYOUT + || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + ji_list = new Vector<int>(nitems); + + if (dbev->marks->size () > 0) + index = dbev->marks->fetch (0); + else + index = -1; + int mindex = 0; + for (index2 = 0; index2 < nitems; index2++) + { + item = data->fetch (index2); + if (index2 == index) + { + ji_list->store (index2, -item->type); + if (++mindex < dbev->marks->size ()) + index = dbev->marks->fetch (mindex); + else + index = -1; + } + else + ji_list->store (index2, item->type); + } + table->store (nvisible, ji_list); + } + else + table->store (nvisible, NULL); + + // Fill function list elements + nv = 0; + + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (!mitem->is_visible () && !mitem->is_tvisible () && + !mitem->is_pvisible ()) + continue; + + // Fill values + switch (mitem->get_vtype ()) + { + case VT_LABEL: + { + Vector<char*> *jobjects = new Vector<char*>(nitems); + char *buf = NULL; + size_t bufsz = 0; + int lspace = 0; + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 + || type == DSP_DISASM_V2) + { + // if this is source or disassembly, where we'll insert + // a preface into the output line, figure out how wide + // it needs to be + // first, scan all the lines, to get the maximum line number + bufsz = 1024; + buf = (char *) malloc (bufsz); + int max_lineno = 0; + int hidx; + Hist_data::HistItem *hitem; + Vec_loop (Hist_data::HistItem*, data, hidx, hitem) + { + if (!hitem->obj) + continue; + if (hitem->obj->get_type () == Histable::LINE && + ((DbeLine*) hitem->obj)->lineno > max_lineno) + max_lineno = ((DbeLine*) hitem->obj)->lineno; + else if (hitem->obj->get_type () == Histable::INSTR + && ((DbeInstr*) hitem->obj)->lineno > max_lineno) + max_lineno = ((DbeInstr*) hitem->obj)->lineno; + } + + // we have the maximum integer over all linenumbers in the file + // figure out how many digits are needed + lspace = snprintf (buf, bufsz, NTXT ("%d"), max_lineno); + } + for (index2 = 0; index2 < nitems; index2++) + { + item = data->fetch (index2); + if (type == DSP_DLAYOUT) + cell = dbe_strdup (((DataObject*) (item->obj))->get_offset_name ()); + else if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + // This code is duplicated in output.cc, yet it's + // intended for presentation purpose and thus is + // potentially different for er_print and analyzer. + switch (item->type) + { + case Module::AT_SRC_ONLY: + case Module::AT_SRC: + if (item->obj == NULL) + snprintf (buf, bufsz, NTXT (" %*c. "), lspace, ' '); + else + snprintf (buf, bufsz, NTXT (" %*d. "), lspace, ((DbeLine*) item->obj)->lineno); + break; + case Module::AT_FUNC: + case Module::AT_QUOTE: + snprintf (buf, bufsz, NTXT ("%*c"), lspace + 3, ' '); + break; + case Module::AT_DIS: + case Module::AT_DIS_ONLY: + if (item->obj == NULL || ((DbeInstr*) item->obj)->lineno == -1) + snprintf (buf, bufsz, NTXT ("%*c[%*s] "), lspace + 3, ' ', lspace, NTXT ("?")); + else + snprintf (buf, bufsz, NTXT ("%*c[%*d] "), lspace + 3, ' ', lspace, + ((DbeInstr*) item->obj)->lineno); + break; + case Module::AT_COM: + case Module::AT_EMPTY: + *buf = (char) 0; + break; + } + // get the line's text + char *s = item->value[index].l; + if (s != NULL) + { + // copy the string expanding all tabulations + // (JTable doesn't render them) + char *d = buf + strlen (buf); + char c; + size_t column = 0; + do + { + c = *s++; + if (c == '\t') + { + do + { + *d++ = ' '; + column++; + } + while (column & 07); + } + else + { + *d++ = c; + column++; + } + if (column + 32 > bufsz) + { + // Reallocate the buffer + size_t curlen = d - buf; + bufsz += 1024; + char *buf_new = (char *) malloc (bufsz); + strncpy (buf_new, buf, curlen); + buf_new[curlen] = '\0'; + free (buf); + buf = buf_new; + d = buf + curlen; + } + } + while (c != (char) 0); + } + cell = dbe_strdup (buf); + free (item->value[index].l); + item->value[index].l = NULL; //YXXX missing from dbeGetFuncListV2 + } + else + { + // omazur: why don't we have it as metric value + Histable::NameFormat nfmt = dbev->get_name_format (); + cell = dbe_strdup (item->obj->get_name (nfmt)); + } + jobjects->store (index2, cell); + } + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 + || type == DSP_DISASM_V2) + free (buf); + table->store (nv++, jobjects); + break; + } + default: + table->store (nv++, dbeGetTableDataOneColumn (data, index)); + break; + } + } + return table; +} + +Vector<Obj> * +dbeGetComparableObjsV2 (int /* dbevindex */, Obj sel_obj, int type) +{ + long grsize = dbeSession->expGroups->size (); + Vector<Obj> *res = new Vector<Obj> (grsize + 1); + for (long j = 0; j < grsize; j++) + res->append ((Obj) NULL); + res->append (sel_obj); + Histable *obj = (Histable *) sel_obj; + if (obj == NULL) + return res; + Function *func = (Function *) obj->convertto (Histable::FUNCTION); + if (func == NULL) + return res; + Vector<Histable *> *cmpObjs = func->get_comparable_objs (); + if (cmpObjs == NULL || cmpObjs->size () != grsize) + return res; + + Histable::Type conv_type = (type == DSP_SOURCE || type == DSP_SOURCE_V2) ? + Histable::LINE : Histable::INSTR; + switch (obj->get_type ()) + { + case Histable::FUNCTION: + for (long j = 0; j < grsize; j++) + res->store (j, (Obj) cmpObjs->get (j)); + return res; + case Histable::INSTR: + case Histable::LINE: + { + SourceFile *srcContext = (SourceFile *) obj->convertto (Histable::SOURCEFILE); + char *bname = get_basename (srcContext->get_name ()); + for (long j = 0; j < grsize; j++) + { + Function *func1 = (Function *) cmpObjs->get (j); + if (func == func1) + { + if (conv_type == Histable::LINE) + res->store (j, (Obj) obj); + else + res->store (j, (Obj) obj->convertto (conv_type, srcContext)); + continue; + } + if (func1 == NULL) + continue; + Vector<SourceFile*> *sources = func1->get_sources (); + SourceFile *sf = NULL; + for (long j1 = 0, sz1 = sources ? sources->size () : 0; j1 < sz1; j1++) + { + SourceFile *sf1 = sources->get (j1); + if (sf1 == srcContext) + { // the same file + sf = srcContext; + break; + } + else if (sf == NULL) + { + char *bname1 = get_basename (sf1->get_name ()); + if (dbe_strcmp (bname, bname1) == 0) + sf = sf1; + } + } + res->store (j, (Obj) func1->convertto (conv_type, srcContext)); + } + break; + } + default: + break; + } + return res; +} + +// Get Table of Function List data +Vector<void *> * +dbeGetFuncListV2 (int dbevindex, int mtype, Obj sel_obj, int type, int subtype) +{ + Metric *mitem; + int nitems, nvisible; + int index, index2, nv; + char *cell; + Hist_data::HistItem *item; + DbeView *dbev = dbeSession->getView (dbevindex); + dbev->error_msg = dbev->warning_msg = NULL; + MetricList *mlist = dbev->get_metric_list ((MetricType) (mtype & MTYPE_MASK), + (mtype & COMPARE_BIT) != 0, + mtype >> GROUP_ID_SHIFT); + Histable *selObj = (Histable *) sel_obj; + int old_compare_mode = dbev->get_compare_mode (); + if ((mtype & COMPARE_BIT) != 0) + dbev->reset_compare_mode (CMP_DISABLE); + Hist_data *data = dbev->get_data (mlist, selObj, type, subtype); + dbev->reset_compare_mode (old_compare_mode); + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + nitems = data->size (); + nvisible = mlist->get_items ()->size (); + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(nvisible + 3); + // Mark Hi-value metric items for annotated src/dis/layout + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_DLAYOUT + || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + Vector<int> *types = new Vector<int>(nitems); + Vector<Obj> *ids = new Vector<Obj > (nitems); + if (dbev->marks->size () > 0) + index = dbev->marks->fetch (0); + else + index = -1; + int mindex = 0; + for (int i = 0; i < nitems; i++) + { + item = data->fetch (i); + ids->store (i, (Obj) item->obj); + if (i == index) + { + types->store (i, -item->type); + if (++mindex < dbev->marks->size ()) + index = dbev->marks->fetch (mindex); + else + index = -1; + } + else + types->store (i, item->type); + } + table->store (nvisible, types); + table->store (nvisible + 1, ids); + } + else + { + table->store (nvisible, NULL); + table->store (nvisible + 1, NULL); + } + + // Fill function list elements + nv = 0; + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (!mitem->is_visible () && !mitem->is_tvisible () && + !mitem->is_pvisible ()) + continue; + + // Fill values + switch (mitem->get_vtype ()) + { + default: + table->store (nv++, dbeGetTableDataOneColumn (data, index)); + break; + case VT_LABEL: + Vector<char*> *jobjects = new Vector<char*>(nitems); + char *buf = NULL; + size_t bufsz = 0; + int lspace = 0; + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 + || type == DSP_DISASM_V2) + { + // if this is source or disassembly, where we'll insert + // a preface into the output line, figure out how wide + // it needs to be + // first, scan all the lines, to get the maximum line number + bufsz = 1024; + buf = (char *) malloc (bufsz); + int max_lineno = 0; + int hidx; + Hist_data::HistItem *hitem; + Vec_loop (Hist_data::HistItem*, data, hidx, hitem) + { + if (!hitem->obj) + continue; + if (hitem->obj->get_type () == Histable::LINE && + ((DbeLine*) hitem->obj)->lineno > max_lineno) + max_lineno = ((DbeLine*) hitem->obj)->lineno; + else if (hitem->obj->get_type () == Histable::INSTR + && ((DbeInstr*) hitem->obj)->lineno > max_lineno) + max_lineno = ((DbeInstr*) hitem->obj)->lineno; + } + + // we have the maximum integer over all linenumbers in the file + // figure out how many digits are needed + lspace = snprintf (buf, bufsz, NTXT ("%d"), max_lineno); + } + + for (index2 = 0; index2 < nitems; index2++) + { + item = data->fetch (index2); + if (type == DSP_DLAYOUT) + cell = dbe_strdup (((DataObject*) (item->obj))->get_offset_name ()); + else if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + // This code is duplicated in output.cc, yet it's + // intended for presentation purpose and thus is + // potentially different for er_print and analyzer. + switch (item->type) + { + case Module::AT_SRC_ONLY: + case Module::AT_SRC: + if (item->obj == NULL) + snprintf (buf, bufsz, NTXT (" %*c. "), lspace, ' '); + else + snprintf (buf, bufsz, NTXT (" %*d. "), lspace, + ((DbeLine*) item->obj)->lineno); + break; + case Module::AT_FUNC: + case Module::AT_QUOTE: + snprintf (buf, bufsz, NTXT ("%*c"), lspace + 3, ' '); + break; + case Module::AT_DIS: + case Module::AT_DIS_ONLY: + if (item->obj == NULL || ((DbeInstr*) item->obj)->lineno == -1) + snprintf (buf, bufsz, NTXT ("%*c[%*s] "), lspace + 3, ' ', + lspace, NTXT ("?")); + else + snprintf (buf, bufsz, NTXT ("%*c[%*d] "), lspace + 3, ' ', + lspace, + ((DbeInstr*) item->obj)->lineno); + break; + case Module::AT_COM: + case Module::AT_EMPTY: + *buf = (char) 0; + break; + } + // get the line's text + char *s = item->value[index].l; + if (s != NULL) + { + // copy the string expanding all tabulations + // (JTable doesn't render them) + char *d = buf + strlen (buf); + char c; + size_t column = 0; + do + { + c = *s++; + if (c == '\t') + { + do + { + *d++ = ' '; + column++; + } + while (column & 07); + } + else + { + *d++ = c; + column++; + } + if (column + 32 > bufsz) + { + // Reallocate the buffer + size_t curlen = d - buf; + bufsz += 1024; + char *buf_new = (char *) malloc (bufsz); + strncpy (buf_new, buf, curlen); + buf_new[curlen] = '\0'; + free (buf); + buf = buf_new; + d = buf + curlen; + } + } + while (c != (char) 0); + } + cell = dbe_strdup (buf); + } + else + { + Histable::NameFormat nfmt = dbev->get_name_format (); + cell = dbe_strdup (item->obj->get_name (nfmt)); + } + jobjects->store (index2, cell); + } + + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 + || type == DSP_DISASM_V2) + free (buf); + table->store (nv++, jobjects); + break; + } + } + table->append (dbeGetMetricList (mlist)); + return table; +} // dbeGetFuncListV2 + +// +// Get Table DataV2 +// +Vector<void*> * +dbeGetTableDataV2 (int dbevindex, char *mlistStr, char *modeStr, char *typeStr, + char *subtypeStr, Vector<uint64_t> *ids) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + // Process metric list specification + if (mlistStr == NULL) + return NULL; + bool met_call = false; + MetricList *mlist = NULL; + if (streq (mlistStr, NTXT ("MET_NORMAL"))) + mlist = dbev->get_metric_list (MET_NORMAL); + else if (streq (mlistStr, NTXT ("MET_CALL"))) + { + met_call = true; + mlist = dbev->get_metric_list (MET_CALL); + } + else if (streq (mlistStr, NTXT ("MET_CALL_AGR"))) + mlist = dbev->get_metric_list (MET_CALL_AGR); + else if (streq (mlistStr, NTXT ("MET_DATA"))) + mlist = dbev->get_metric_list (MET_DATA); + else if (streq (mlistStr, NTXT ("MET_INDX"))) + mlist = dbev->get_metric_list (MET_INDX); + else if (streq (mlistStr, NTXT ("MET_IO"))) + mlist = dbev->get_metric_list (MET_IO); + else if (streq (mlistStr, NTXT ("MET_HEAP"))) + mlist = dbev->get_metric_list (MET_HEAP); + else + return NULL; + + // Process mode specification + if (modeStr == NULL) + return NULL; + Hist_data::Mode mode = (Hist_data::Mode)0; + if (streq (modeStr, NTXT ("CALLERS"))) + mode = Hist_data::CALLERS; + else if (streq (modeStr, NTXT ("CALLEES"))) + mode = Hist_data::CALLEES; + else if (streq (modeStr, NTXT ("SELF"))) + mode = Hist_data::SELF; + else if (streq (modeStr, NTXT ("ALL"))) + mode = Hist_data::ALL; + else + return NULL; + + // Process type specification + if (typeStr == NULL) + return NULL; + Histable::Type type = Histable::OTHER; + if (streq (typeStr, NTXT ("FUNCTION"))) + type = Histable::FUNCTION; + else if (streq (typeStr, NTXT ("INDEXOBJ"))) + type = Histable::INDEXOBJ; + else if (streq (typeStr, NTXT ("IOACTFILE"))) + type = Histable::IOACTFILE; + else if (streq (typeStr, NTXT ("IOACTVFD"))) + type = Histable::IOACTVFD; + else if (streq (typeStr, NTXT ("IOCALLSTACK"))) + type = Histable::IOCALLSTACK; + else if (streq (typeStr, NTXT ("HEAPCALLSTACK"))) + type = Histable::HEAPCALLSTACK; + else if (streq (typeStr, NTXT ("LINE"))) + type = Histable::LINE; + else if (streq (typeStr, NTXT ("INSTR"))) + type = Histable::INSTR; + else + // XXX Accepting objects other than above may require a different + // implementation of the id -> Histable mapping below + return NULL; + + // Process subtype specification + int subtype = 0; + if (subtypeStr != NULL) + subtype = atoi (subtypeStr); + Vector<Histable*> *hobjs = NULL; + if (ids != NULL) + { + hobjs = new Vector<Histable*>(); + for (int i = 0; i < ids->size (); ++i) + { + Histable::Type obj_type = type; + if ((obj_type == Histable::LINE || obj_type == Histable::INSTR) + && subtype == 0) + obj_type = Histable::FUNCTION; + Histable *hobj = dbeSession->findObjectById (obj_type, subtype, ids->fetch (i)); + if ((obj_type == Histable::LINE || obj_type == Histable::INSTR) + && subtype == 0 && hobj == NULL) + return NULL; + hobjs->append (hobj); + } + } + + PathTree::PtreeComputeOption flag = PathTree::COMPUTEOPT_NONE; + if (dbev->isOmpDisMode () && type == Histable::FUNCTION + && mode == Hist_data::CALLEES && met_call) + flag = PathTree::COMPUTEOPT_OMP_CALLEE; + + Hist_data *data = dbev->get_hist_data (mlist, type, subtype, mode, hobjs, NULL, NULL, flag); + return dbeGetTableDataV2Data (dbev, data); +} + +static Vector<void*> * +dbeGetTableDataV2Data (DbeView * /*dbev*/, Hist_data *data) +{ + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + MetricList *mlist; + mlist = data->get_metric_list (); + int nitems = data->size (); + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(mlist->size () + 1); + + // Fill function list elements + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *mitem = mlist->get (i); + if (!mitem->is_visible () && !mitem->is_tvisible () && + !mitem->is_pvisible ()) + continue; + table->append (dbeGetTableDataOneColumn (data, i)); + } + + // Add an array of Histable IDs + Vector<uint64_t> *idList = new Vector<uint64_t>(nitems); + for (int i = 0; i < nitems; ++i) + { + Hist_data::HistItem *item = data->fetch (i); + if (item->obj->get_type () == Histable::LINE + || item->obj->get_type () == Histable::INSTR) + idList->store (i, (uint64_t) (item->obj)); + else + idList->store (i, item->obj->id); + } + table->append (idList); + return table; +} // dbeGetTableData + +//YXXX try to use the following to consolidate similar cut/paste code + +static Vector<void*> * +dbeGetTableDataOneColumn (Hist_data *data, int met_ind) +{ + // Allocates a vector and fills it with metric values for one column + TValue res; + Metric *m = data->get_metric_list ()->get (met_ind); + if ((m->get_visbits () & VAL_RATIO) != 0) + { + Vector<double> *col = new Vector<double>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + double d = (v->tag != VT_LABEL) ? v->to_double () : 100.; // NaN + col->append (d); + } + return (Vector<void*> *) col; + } + + switch (m->get_vtype ()) + { + case VT_DOUBLE: + { + Vector<double> *col = new Vector<double>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + col->append (v->d); + } + return (Vector<void*> *) col; + } + case VT_INT: + { + Vector<int> *col = new Vector<int>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + col->append (v->i); + } + return (Vector<void*> *) col; + } + case VT_ULLONG: + case VT_LLONG: + { + Vector<long long> *col = new Vector<long long>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + col->append (v->ll); + } + return (Vector<void*> *) col; + } + case VT_ADDRESS: + { + Vector<long long> *col = new Vector<long long>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + // set the highest bit to mark this jlong as + // a VT_ADDRESS (rather than a regular VT_LLONG) + col->append (v->ll | 0x8000000000000000ULL); + } + return (Vector<void*> *) col; + } + case VT_LABEL: + { + Vector<char *> *col = new Vector<char *>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + col->append (dbe_strdup (v->l)); + } + return (Vector<void*> *) col; + } + default: + return NULL; + } +} + +static Vector<void*> * +dbeGetTableDataOneColumn (DbeView *dbev, Vector<Hist_data::HistItem*> *data, + ValueTag vtype, int metricColumnNumber) +// Allocates a vector and fills it with metric values for one column +{ + Vector<void*> *column_data = NULL; + int nitems = data->size (); // number of rows + int index = metricColumnNumber; + switch (vtype) + { + case VT_DOUBLE: + { + Vector<double> *jd_list = new Vector<double>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + jd_list->store (index2, item->value[index].d); + } + column_data = (Vector<void*> *)jd_list; + break; + } + case VT_INT: + { + Vector<int> *ji_list = new Vector<int>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + ji_list->store (index2, item->value[index].i); + } + column_data = (Vector<void*> *)ji_list; + break; + } + case VT_ULLONG: + case VT_LLONG: + { + Vector<long long> *jl_list = new Vector<long long>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + jl_list->store (index2, item->value[index].ll); + } + column_data = (Vector<void*> *)jl_list; + break; + } + case VT_ADDRESS: + { + Vector<long long> *jl_list = new Vector<long long>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + + // set the highest bit to mark this jlong as + // a VT_ADDRESS (rather than a regular VT_LLONG) + uint64_t addr = item->value[index].ll; + addr |= 0x8000000000000000ULL; + jl_list->store (index2, addr); + } + column_data = (Vector<void*> *)jl_list; + break; + } + case VT_LABEL: + { + Vector<char*> *jobjects = new Vector<char*>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + + // omazur: why don't we have it as metric value + Histable::NameFormat nfmt = dbev->get_name_format (); + char *str = dbe_strdup (item->obj->get_name (nfmt)); + jobjects->store (index2, str); + } + column_data = (Vector<void*> *)jobjects; + break; + } + default: + abort (); + } + return column_data; +} + +int +dbeGetCallTreeNumLevels (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return 0; + return ptree->get_ftree_depth (); +} + +Vector<void*>* +dbeGetCallTreeLevel (int dbevindex, char *mcmd, int level) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + if (mcmd == NULL) + return NULL; + BaseMetric *bm = dbeSession->find_base_reg_metric (mcmd); + if (bm == NULL) + return NULL; + return ptree->get_ftree_level (bm, level); +} + +Vector<void*>* +dbeGetCallTreeLevels (int dbevindex, char *mcmd) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + if (mcmd == NULL) + return NULL; + BaseMetric *bm = dbeSession->find_base_reg_metric (mcmd); + if (bm == NULL) + return NULL; + + int depth = ptree->get_ftree_depth (); + Vector<void*> *results = new Vector<void*>(depth); + for (int ii = 0; ii < depth; ii++) + results->append (ptree->get_ftree_level (bm, ii)); + return results; +} + +Vector<void*>* +dbeGetCallTreeLevelFuncs (int dbevindex, int start_level, int end_level) +{ // (0,-1) -> all levels + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + + int depth = ptree->get_ftree_depth (); + if (start_level < 0) + start_level = 0; + if (end_level < 0 || end_level >= depth) + end_level = depth - 1; + + Histable::NameFormat nfmt = dbev->get_name_format (); //YXXX or fixed format? + Vector<char*> *funcNames = new Vector<char*>(); + Vector<long long> *funcIds = new Vector<long long>(); + Vector<Obj> *funcObjs = new Vector<Obj>(); + + if (start_level == 0 && end_level == depth - 1) + return dbeGetCallTreeFuncs (dbevindex); + else + { + for (int ii = start_level; ii <= end_level; ii++) + { + Vector<void*> *info = ptree->get_ftree_level (NULL, ii); /*no metric*/ + if (!info) + continue; + Vector<long long> *fids = (Vector<long long> *)info->get (2); + if (!fids) + continue; + int index; + long long fid; + Vec_loop (long long, fids, index, fid) + { + funcIds->append (fid); + Histable *obj = dbeSession->findObjectById (fid); + char * fname = obj ? dbe_strdup (obj->get_name (nfmt)) : NULL; + funcNames->append (fname); + funcObjs->append ((unsigned long) obj); // avoiding sign extension + } + destroy (info); + } + } + Vector<void*> *results = new Vector<void*>(3); + results->append (funcIds); + results->append (funcNames); + results->append (funcObjs); + return results; +} + +Vector<void*> * +dbeGetCallTreeFuncs (int dbevindex) +{ // does not require ptree->get_ftree_level() to be computed + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return 0; + Vector<Function*>* funcs = ptree->get_funcs (); // Unique functions in tree + if (funcs == NULL) + return NULL; + + long sz = funcs->size (); + Vector<void*> *results = new Vector<void*>(3); + Vector<long long> *funcIds = new Vector<long long>(sz); + Vector<char*> *funcNames = new Vector<char*>(sz); + Vector<Obj> *funcObjs = new Vector<Obj>(sz); + + int index; + Function * func; + Histable::NameFormat nfmt = dbev->get_name_format (); //YXXX or fixed format? + Vec_loop (Function *, funcs, index, func) + { + funcIds->append (func->id); // do we need IDs? + char *fname = dbe_strdup (func->get_name (nfmt)); + funcNames->append (fname); + funcObjs->append ((unsigned long) func); // avoiding sign extension + } + results->put (0, funcIds); + results->put (1, funcNames); + results->put (2, funcObjs); + destroy (funcs); + return results; +} + +Vector<void*>* +dbeGetCallTreeChildren (int dbevindex, char *mcmd, Vector<int /*NodeIdx*/>*node_idxs) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (node_idxs == NULL || node_idxs->size () == 0) + return NULL; + long sz = node_idxs->size (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + if (mcmd == NULL) + return NULL; + BaseMetric *bm = dbeSession->find_base_reg_metric (mcmd); + if (bm == NULL) + return NULL; + + Vector<void*> *results = new Vector<void*>(sz); + for (long ii = 0; ii < sz; ii++) + { + PathTree::NodeIdx nodeIdx = node_idxs->get (ii); // upcasted from int + results->append (ptree->get_ftree_node_children (bm, nodeIdx)); + } + return results; +} + +Vector<int> * +dbeGetGroupIds (int /*dbevindex*/) +{ + Vector<ExpGroup*> *groups = dbeSession->expGroups; + int sz = groups->size (); + Vector<int> *grIds = new Vector<int>(sz); + for (int i = 0; i < sz; i++) + grIds->store (i, groups->fetch (i)->groupId); + return grIds; +} + +// +// Get label for name column +// +Vector<char*> * +dbeGetNames (int dbevindex, int type, Obj sel_obj) +{ + char *s0, *s1, *s2; + bool need_strdup = true; + switch (type) + { + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + case DSP_SOURCE: + case DSP_DISASM: + { + if (sel_obj) + { + Histable *selObj = (Histable*) sel_obj; + Function *func = (Function *) selObj->convertto (Histable::FUNCTION); + if (func) + { + char *names[3] = {NULL, NULL, NULL}; + set_file_names (func, names); + s0 = names[0]; + s1 = names[1]; + s2 = names[2]; + need_strdup = false; + break; + } + } + DbeView *dbev = dbeSession->getView (dbevindex); + char **names = type == DSP_SOURCE || type == DSP_SOURCE_V2 ? dbev->names_src : dbev->names_dis; + s0 = names[0]; + s1 = names[1]; + s2 = names[2]; + break; + } + case DSP_LINE: + s0 = GTXT ("Lines"); + s1 = GTXT ("Function, line # in \"sourcefile\""); + s2 = NTXT (""); + break; + case DSP_PC: + s0 = GTXT ("PCs"); + s1 = GTXT ("Function + offset"); + s2 = NTXT (""); + break; + case DSP_DLAYOUT: + s0 = GTXT ("Name"); + s1 = GTXT ("* +offset .element"); + s2 = NTXT (""); + break; + default: + s0 = GTXT ("Name"); + s1 = s2 = NTXT (""); + break; + } + if (need_strdup) + { + s0 = dbe_strdup (s0); + s1 = dbe_strdup (s1); + s2 = dbe_strdup (s2); + } + Vector<char*> *table = new Vector<char*>(3); + table->store (0, s0); + table->store (1, s1); + table->store (2, s2); + return table; +} + +// +// Get Total/Maximum element of Function List +// +Vector<void*> * +dbeGetTotalMax (int dbevindex, int type, int subtype) +{ + Hist_data *data; + int index; + Hist_data::HistItem *total_item, *maximum_item; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + switch (type) + { + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_CALLER: + data = dbev->callers; + break; + case DSP_SELF: + case DSP_CALLEE: + data = dbev->callees; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + data = dbev->get_indxobj_data (subtype); + break; + case DSP_INDXOBJ: + data = dbev->get_indxobj_data (subtype); + break; + case DSP_FUNCTION: // annotated src/dis use func total/max + case DSP_SOURCE: + case DSP_DISASM: + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + data = dbev->func_data; + break; + default: + abort (); + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + // Get list size + // XXX -- the original list has all items, visible or not; + // XXX -- the one from Hist_data has only visible items, + // XXX -- and should be the only ones computed + // XXX -- Analyzer got confused (yesterday), when we used the shorter list + // XXX -- Why can we fetch total/max for metrics never + // XXX -- computed without core dumping? + MetricList *mlist2 = data->get_metric_list (); + int size = mlist2->get_items ()->size (); + + // Initialize Java array + Vector<void*> *total_max = new Vector<void*>(2); + Vector<double> *total = new Vector<double>(size); + Vector<double> *maximum = new Vector<double>(size); + + // Fill total/maximum element + total_item = data->get_totals (); + maximum_item = data->get_maximums (); + + for (index = 0; index < size; index++) + { + total->store (index, total_item->value[index].to_double ()); + maximum->store (index, maximum_item->value[index].to_double ()); + } + total_max->store (0, total); + total_max->store (1, maximum); + return total_max; +} + +// +// Get Table of Overview List +Vector<void*> * +dbeGetStatisOverviewList (int dbevindex) +{ + int size; + Ovw_data **data; + Ovw_data::Ovw_item labels, *totals; + int nitems; + int index, index2; + + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->error_msg = dbev->warning_msg = NULL; + + size = dbeSession->nexps (); + totals = new Ovw_data::Ovw_item[size + 1]; + data = new Ovw_data*[size + 1]; + data[0] = new Ovw_data (); + + for (index = 1; index <= size; index++) + { + data[index] = dbev->get_ovw_data (index - 1); + if (data[index] == NULL) + { + Ovw_data::reset_item (&totals[index]); // set contents to zeros + continue; + } + data[0]->sum (data[index]); + totals[index] = data[index]->get_totals (); //shallow copy! + } + totals[0] = data[0]->get_totals (); + + // Get table size + labels = data[0]->get_labels (); + nitems = labels.size + 4; + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(size + 4); + Vector<char*> *jobjects = new Vector<char*>(nitems); + + // Set the label + jobjects->store (0, dbe_strdup (GTXT ("Start Time (sec.)"))); + jobjects->store (1, dbe_strdup (GTXT ("End Time (sec.)"))); + jobjects->store (2, dbe_strdup (GTXT ("Duration (sec.)"))); + jobjects->store (3, dbe_strdup (GTXT ("Total Thread Time (sec.)"))); + jobjects->store (4, dbe_strdup (GTXT ("Average number of Threads"))); + + for (index2 = 5; index2 < nitems; index2++) + jobjects->store (index2, dbe_strdup (labels.values[index2 - 4].l)); + table->store (0, jobjects); + + // Set the data + for (index = 0; index <= size; index++) + { + Vector<double> *jd_list = new Vector<double>(nitems); + jd_list->store (0, tstodouble (totals[index].start)); + jd_list->store (1, tstodouble (totals[index].end)); + jd_list->store (2, tstodouble (totals[index].duration)); + jd_list->store (3, tstodouble (totals[index].tlwp)); + jd_list->store (4, totals[index].nlwp); + for (index2 = 5; index2 < nitems; index2++) + jd_list->store (index2, tstodouble (totals[index].values[index2 - 4].t)); + table->store (index + 1, jd_list); + } + for (index = 0; index <= size; index++) + delete data[index]; + delete[] data; + delete[] totals; + return table; +} + +// Get Table of Statistics List +Vector<void*> * +dbeGetStatisList (int dbevindex) +{ + int size; + Stats_data **data; + int nitems; + int index, index2; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->error_msg = dbev->warning_msg = NULL; + if ((size = dbeSession->nexps ()) == 0) + return NULL; + + // Get statistics data + data = (Stats_data **) malloc ((size + 1) * sizeof (Stats_data *)); + data[0] = new Stats_data (); + for (index = 1; index <= size; index++) + { + data[index] = dbev->get_stats_data (index - 1); + if (data[index] == NULL) + continue; + data[0]->sum (data[index]); + } + + // Get table size + nitems = data[0]->size (); + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(size + 2); + Vector<char*> *jobjects = new Vector<char*>(nitems); + + // Set the label + for (index2 = 0; index2 < nitems; index2++) + jobjects->store (index2, dbe_strdup (data[0]->fetch (index2).label)); + table->store (0, jobjects); + + // Set the data + for (index = 0; index <= size; index++) + { + Vector<double> *jd_list = new Vector<double>(nitems); + for (index2 = 0; index2 < nitems; index2++) + { + double val = 0; + if (data[index]) + val = data[index]->fetch (index2).value.to_double (); + jd_list->store (index2, val); + } + table->store (index + 1, jd_list); + } + if (data) + { + for (index = 0; index <= size; index++) + delete data[index]; + free (data); + } + return table; +} + + +// +// Set summary list +// +static void +setSummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, Vector<char*> *jvalues) +{ + char *sname = NULL, *oname = NULL, *lname = NULL, *alias = NULL, + *mangle = NULL, *address = NULL, *size = NULL, + *name_0 = NULL, *sname_0 = NULL, *oname_0 = NULL, *lname_0 = NULL, + *alias_0 = NULL, *mangle_0 = NULL; + Function *func, *last_func = NULL; + int one_func = 1; + + // Get the source/object/load-object files & aliases + long long ll_size = 0; + for (long i = 0; i < objs->size (); i++) + { + Histable *current_obj = objs->fetch (i); + Histable::Type htype = current_obj->get_type (); + if (htype == Histable::LOADOBJECT) + lname = ((LoadObject *) current_obj)->dbeFile->get_location_info (); + else if ((func = (Function*) current_obj->convertto (Histable::FUNCTION)) != NULL) + { + if (one_func && last_func != NULL && last_func != func) + one_func = 0; + last_func = func; + sname = NULL; + DbeLine *dbeline = (DbeLine*) current_obj->convertto (Histable::LINE); + if (dbeline) + { + SourceFile *sf; + if (dbeline->lineno == 0 && dbeline->include != NULL) + sf = dbeline->include; + else if (dbeline->sourceFile != NULL) + sf = dbeline->sourceFile; + else + sf = func->getDefSrc (); + if (sf) + sname = sf->dbeFile->get_location_info (); + } + char *func_name = func->get_name (); + mangle = func->get_mangled_name (); + if (mangle && streq (func_name, mangle)) + mangle = NULL; + Module *module = func->module; + if (module != NULL) + { + module->read_stabs (); + if (sname == NULL || strlen (sname) == 0) + { + SourceFile *sf = module->getMainSrc (); + sname = sf->dbeFile->get_location_info (); + } + DbeFile *df = module->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_JAVACLASS) == 0) + df = module->loadobject->dbeFile; + lname = df->get_location_info (); + oname = lname; + if (module->dot_o_file) + oname = module->dot_o_file->dbeFile->get_location_info (); + } + + if (htype == Histable::INSTR && dbeSession->is_datamode_available ()) + alias = ((DbeInstr*) current_obj)->get_descriptor (); + } + + char *name = current_obj->get_name (); + if (i == 0) + { + name_0 = name; + lname_0 = lname; + sname_0 = sname; + oname_0 = oname; + mangle_0 = mangle; + alias_0 = alias; + if (objs->size () == 1) + { + uint64_t addr = current_obj->get_addr (); + address = dbe_sprintf (NTXT ("%lld:0x%08llX"), + (long long) ADDRESS_SEG (addr), + (long long) ADDRESS_OFF (addr)); + } + } + else + { + if (name_0 != name) + name_0 = NULL; + if (lname_0 != lname) + lname_0 = NULL; + if (sname_0 != sname) + sname_0 = NULL; + if (oname_0 != oname) + oname_0 = NULL; + if (mangle_0 != mangle) + mangle_0 = NULL; + if (alias_0 != alias) + alias_0 = NULL; + } + if (current_obj->get_size () == -1) + { + if (size == NULL) + size = dbe_strdup (GTXT ("(Unknown)")); + } + else + ll_size += current_obj->get_size (); + } + if (size == NULL) + size = dbe_sprintf (NTXT ("%lld"), ll_size); + if (name_0 == NULL) + { + if (objs->size () > 1) + { + char *func_name = last_func == NULL ? NULL : + (one_func == 0 ? NULL : last_func->get_name ()); + name_0 = dbe_sprintf (NTXT ("%s%s%s (%lld %s)"), + func_name == NULL ? "" : func_name, + func_name == NULL ? "" : ": ", + GTXT ("Multiple Selection"), + (long long) objs->size (), + GTXT ("objects")); + } + } + else + name_0 = dbe_strdup (name_0); + + // Set the name area + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'N'); + jlabels->store (0, dbe_strdup (GTXT ("Name"))); + jvalues->store (0, name_0); + + saligns->store (1, TEXT_LEFT); + mnemonic->store (1, 'P'); + jlabels->store (1, dbe_strdup (GTXT ("PC Address"))); + jvalues->store (1, address); + + saligns->store (2, TEXT_LEFT); + mnemonic->store (2, 'z'); + jlabels->store (2, dbe_strdup (GTXT ("Size"))); + jvalues->store (2, size); + + saligns->store (3, TEXT_RIGHT); + mnemonic->store (3, 'r'); + jlabels->store (3, dbe_strdup (GTXT ("Source File"))); + jvalues->store (3, dbe_strdup (sname_0)); + + saligns->store (4, TEXT_RIGHT); + mnemonic->store (4, 'b'); + jlabels->store (4, dbe_strdup (GTXT ("Object File"))); + jvalues->store (4, dbe_strdup (oname_0)); + + saligns->store (5, TEXT_LEFT); + mnemonic->store (5, 'j'); + jlabels->store (5, dbe_strdup (GTXT ("Load Object"))); + jvalues->store (5, dbe_strdup (lname_0)); + + saligns->store (6, TEXT_LEFT); + mnemonic->store (6, 'm'); + jlabels->store (6, dbe_strdup (GTXT ("Mangled Name"))); + jvalues->store (6, dbe_strdup (mangle_0)); + + saligns->store (7, TEXT_LEFT); + mnemonic->store (7, 'A'); + jlabels->store (7, dbe_strdup (GTXT ("Aliases"))); + jvalues->store (7, dbe_strdup (alias_0)); +} + +// Set memory-object summary list +// +static void +setMemSummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'M'); + jlabels->store (0, dbe_strdup (GTXT ("Memory Object"))); + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + jvalues->store (0, dbe_strdup (current_obj->get_name ())); + } + else + { + char *name = dbe_sprintf (NTXT ("%s (%lld %s)"), + GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + jvalues->store (0, name); + } +} + +// Set index-object summary list +// +static void +setIndxSummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'I'); + jlabels->store (0, dbe_strdup (GTXT ("Index Object"))); + + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + jvalues->store (0, dbe_strdup (current_obj->get_name ())); + } + else + { + char *name = dbe_sprintf (NTXT ("%s (%lld %s)"), GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + jvalues->store (0, name); + } +} + +// Set I/O activity summary list +// +static void +setIOActivitySummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'O'); + jlabels->store (0, dbe_strdup (GTXT ("I/O Activity"))); + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + jvalues->store (0, dbe_strdup (current_obj->get_name ())); + } + else + { + char *name = dbe_sprintf (NTXT ("%s (%lld %s)"), GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + jvalues->store (0, name); + } +} + +// Set heap activity summary list +// +static void +setHeapActivitySummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'O'); + jlabels->store (0, dbe_strdup (GTXT ("Heap Activity"))); + + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + jvalues->store (0, dbe_strdup (current_obj->get_name ())); + } + else + { + char *name = dbe_sprintf (NTXT ("%s (%lld %s)"), GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + jvalues->store (0, name); + } +} + +// +// Set data-object summary list +// +static void +setDataSummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + char *name, *type, *member, *elist; + DataObject *dobj; + Vector<DataObject *> *delem; + Histable *scope; + int index; + char *size, *offset, *elements, *scopename; + + // Get the data object elements + member = elist = type = size = offset = elements = scopename = NULL; + + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + name = dbe_strdup (current_obj->get_name ()); + dobj = (DataObject *) current_obj; + type = dobj->get_typename (); + scope = dobj->get_scope (); + delem = dbeSession->get_dobj_elements (dobj); + if (type == NULL) + type = GTXT ("(Synthetic)"); + if (!scope) + scopename = dbe_strdup (GTXT ("(Global)")); + else + { + switch (scope->get_type ()) + { + case Histable::FUNCTION: + scopename = dbe_sprintf (NTXT ("%s(%s)"), + ((Function*) scope)->module->get_name (), + scope->get_name ()); + break; + case Histable::LOADOBJECT: + case Histable::MODULE: + default: + scopename = dbe_strdup (scope->get_name ()); + break; + } + } + + if (dobj->get_offset () != -1) + { + if (dobj->get_parent ()) + member = dbe_strdup (dobj->get_parent ()->get_name ()); + offset = dbe_sprintf (NTXT ("%lld"), (long long) dobj->get_offset ()); + } + size = dbe_sprintf ("%lld", (long long) dobj->get_size ()); + + if (delem->size () > 0) + { + elements = dbe_sprintf (NTXT ("%lld"), (long long) delem->size ()); + StringBuilder sb_tmp, sb; + sb.append (GTXT ("Offset Size Name\n")); + for (index = 0; index < delem->size (); index++) + { + DataObject *ditem = delem->fetch (index); + sb_tmp.sprintf (NTXT ("%6lld %5lld %s\n"), + (long long) ditem->get_offset (), + (long long) ditem->get_size (), ditem->get_name ()); + sb.append (&sb_tmp); + } + if (sb.charAt (sb.length () - 1) == '\n') + sb.setLength (sb.length () - 1); + elist = sb.toString (); + } + } + else + name = dbe_sprintf (NTXT ("%s (%lld %s)"), GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'D'); + jlabels->store (0, dbe_strdup (GTXT ("Data Object"))); + jvalues->store (0, name); + + saligns->store (1, TEXT_LEFT); + mnemonic->store (1, 'S'); + jlabels->store (1, dbe_strdup (GTXT ("Scope"))); + jvalues->store (1, scopename); + + saligns->store (2, TEXT_LEFT); + mnemonic->store (2, 'T'); + jlabels->store (2, dbe_strdup (GTXT ("Type"))); + jvalues->store (2, dbe_strdup (type)); + + saligns->store (3, TEXT_LEFT); + mnemonic->store (3, 'M'); + jlabels->store (3, dbe_strdup (GTXT ("Member of"))); + jvalues->store (3, member); + + saligns->store (4, TEXT_LEFT); + mnemonic->store (4, 'O'); + jlabels->store (4, dbe_strdup (GTXT ("Offset"))); + jvalues->store (4, offset); + + saligns->store (5, TEXT_LEFT); + mnemonic->store (5, 'z'); + jlabels->store (5, dbe_strdup (GTXT ("Size"))); + jvalues->store (5, size); + + saligns->store (6, TEXT_LEFT); + mnemonic->store (6, 'E'); + jlabels->store (6, dbe_strdup (GTXT ("Elements"))); + jvalues->store (6, elements); + + saligns->store (7, TEXT_LEFT); + mnemonic->store (7, 'L'); + jlabels->store (7, dbe_strdup (GTXT ("List"))); + jvalues->store (7, elist); +} + +#define SUMMARY_NAME 8 +#define DSUMMARY_NAME 8 +#define LSUMMARY_NAME 7 +#define IMSUMMARY_NAME 1 + +Vector<void*> * +dbeGetSummaryV2 (int dbevindex, Vector<Obj> *sel_objs, int type, int subtype) +{ + if (sel_objs == NULL || sel_objs->size () == 0) + return NULL; + DbeView *dbev = dbeSession->getView (dbevindex); + Vector<Histable*>*objs = new Vector<Histable*>(sel_objs->size ()); + for (int i = 0; i < sel_objs->size (); i++) + { + Histable *obj = (Histable *) sel_objs->fetch (i); + if (obj == NULL) + continue; + char *nm = obj->get_name (); + if (streq (nm, NTXT ("<Total>"))) + { + // Special case for 'Total'. + // Multi selection which includes 'Total' is only 'Total' + objs->reset (); + objs->append (obj); + break; + } + objs->append (obj); + } + if (objs->size () == 0) + return NULL; + + // Set name area + int nname = SUMMARY_NAME; + Vector<int> *saligns = new Vector<int>(nname); + Vector<char>*mnemonic = new Vector<char>(nname); + Vector<char*> *jlabels = new Vector<char*>(nname); + Vector<char*> *jvalues = new Vector<char*>(nname); + Vector<void*> *name_objs = new Vector<void*>(4); + name_objs->store (0, saligns); + name_objs->store (1, mnemonic); + name_objs->store (2, jlabels); + name_objs->store (3, jvalues); + setSummary (objs, saligns, mnemonic, jlabels, jvalues); + + MetricList *prop_mlist = new MetricList (dbev->get_metric_ref (MET_NORMAL)); + if (prop_mlist && dbev->comparingExperiments ()) + prop_mlist = dbev->get_compare_mlist (prop_mlist, 0); + + int nitems = prop_mlist->get_items ()->size (); + + // Set the metrics area + jlabels = new Vector<char*>(nitems); + Vector<double> *clock_list = new Vector<double>(nitems); + Vector<double> *excl_list = new Vector<double>(nitems); + Vector<double> *ep_list = new Vector<double>(nitems); + Vector<double> *incl_list = new Vector<double>(nitems); + Vector<double> *ip_list = new Vector<double>(nitems); + Vector<int> *vtype = new Vector<int>(nitems); + + // Initialize Java String array + Vector<void*> *metric_objs = new Vector<void*>(8); + metric_objs->store (0, jlabels); + metric_objs->store (1, clock_list); + metric_objs->store (2, excl_list); + metric_objs->store (3, ep_list); + metric_objs->store (4, incl_list); + metric_objs->store (5, ip_list); + metric_objs->store (6, vtype); + + int last_init = -1; + for (int i = 0; i < objs->size (); i++) + { + Histable *obj = objs->fetch (i); + // Get the data to be displayed + Hist_data *data = dbev->get_hist_data (prop_mlist, obj->get_type (), subtype, + Hist_data::SELF, obj, dbev->sel_binctx, objs); + + if (data->get_status () != Hist_data::SUCCESS) + { + if (type != DSP_DLAYOUT) + { // For data_layout, rows with zero metrics are OK + delete data; + continue; + } + } + TValue *values = NULL; + if (data->get_status () == Hist_data::SUCCESS) + { + Hist_data::HistItem *hi = data->fetch (0); + if (hi) + values = hi->value; + } + Hist_data::HistItem *total = data->get_totals (); + int index2 = 0; + char *tstr = GTXT (" Time"); + char *estr = GTXT ("Exclusive "); + size_t len = strlen (estr); + + // get the metric list from the data + MetricList *mlist = data->get_metric_list (); + int index; + Metric *mitem; + double clock; + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (last_init < index2) + { + last_init = index2; + jlabels->store (index2, NULL); + clock_list->store (index2, 0.0); + excl_list->store (index2, 0.0); + ep_list->store (index2, 0.0); + incl_list->store (index2, 0.0); + ip_list->store (index2, 0.0); + vtype->store (index2, 0); + } + double dvalue = (values != NULL) ? values[index].to_double () : 0.0; + double dtotal = total->value[index].to_double (); + if (mitem->is_time_val ()) + clock = 1.e+6 * dbeSession->get_clock (-1); + else + clock = 0.0; + + clock_list->store (index2, clock); + if ((mitem->get_subtype () == Metric::EXCLUSIVE) || + (mitem->get_subtype () == Metric::DATASPACE)) + { + if (i == 0) + { + char *sstr = mitem->get_name (); + if (!strncmp (sstr, estr, len)) + sstr += len; + char *buf, *lstr = strstr (sstr, tstr); + if (lstr) + buf = dbe_strndup (sstr, lstr - sstr); + else + buf = dbe_strdup (sstr); + jlabels->store (index2, buf); + vtype->store (index2, mitem->get_vtype ()); + } + dvalue += excl_list->fetch (index2); + double percent = dtotal == 0.0 ? dtotal : (dvalue / dtotal) * 100; + excl_list->store (index2, dvalue); + ep_list->store (index2, percent); + } + else + { + dvalue += incl_list->fetch (index2); + if (dvalue > dtotal) + dvalue = dtotal; // temporary correction + double percent = dtotal == 0.0 ? dtotal : (dvalue / dtotal) * 100; + incl_list->store (index2, dvalue); + ip_list->store (index2, percent); + index2++; + } + } + delete data; + } + delete prop_mlist; + Vector<void*> *summary = new Vector<void*>(2); + summary->store (0, name_objs); + summary->store (1, metric_objs); + return summary; +} + +// Get Summary List +Vector<void*> * +dbeGetSummary (int dbevindex, Vector<Obj> *sel_objs, int type, int subtype) +{ + bool is_data, is_mem, is_indx, is_iodata, is_heapdata; + Hist_data::HistItem *total; + MetricList *prop_mlist; // as passed to get_hist_data + MetricList *mlist; // as stored in the data + Metric *mitem; + int i, nname, nitems, index, index2; + TValue *values; + double dvalue, clock; + Hist_data *data; + Vector<double> *percent_scale; + + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (sel_objs == NULL || sel_objs->size () == 0) + return NULL; + + is_mem = false; + is_data = false; + is_indx = false; + is_iodata = false; + is_heapdata = false; + nname = SUMMARY_NAME; + Vector<Histable*>*objs = new Vector<Histable*>(sel_objs->size ()); + if (type == DSP_TIMELINE) + objs->append ((Histable *) sel_objs->fetch (0)); + else + { + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_SELF: + data = dbev->fitem_data; + break; + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + case DSP_DLAYOUT: + is_data = true; + nname = LSUMMARY_NAME; + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + is_data = true; + nname = DSUMMARY_NAME; + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + is_data = true; + is_mem = true; + nname = IMSUMMARY_NAME; + data = dbev->get_indxobj_data (subtype); + break; + case DSP_INDXOBJ: + is_indx = true; + nname = IMSUMMARY_NAME; + data = dbev->get_indxobj_data (subtype); + break; + case DSP_IOACTIVITY: + is_iodata = true; + nname = IMSUMMARY_NAME; + data = dbev->iofile_data; + break; + case DSP_IOVFD: + is_iodata = true; + nname = IMSUMMARY_NAME; + data = dbev->iovfd_data; + break; + case DSP_IOCALLSTACK: + is_iodata = true; + nname = IMSUMMARY_NAME; + data = dbev->iocs_data; + break; + case DSP_HEAPCALLSTACK: + is_heapdata = true; + nname = IMSUMMARY_NAME; + data = dbev->heapcs_data; + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + Hist_data::HistItem *current_item; + for (i = 0; i < sel_objs->size (); i++) + { + int sel_index = (int) sel_objs->fetch (i); + if (type != DSP_IOACTIVITY && type != DSP_IOVFD && + type != DSP_IOCALLSTACK && type != DSP_HEAPCALLSTACK) + { + if (sel_index < 0 || sel_index >= data->size ()) + continue; + current_item = data->fetch (sel_index); + if (current_item->obj == NULL) + continue; + } + else + { + if (sel_index < 0) + continue; + bool found = false; + for (int j = 0; j < data->size (); j++) + { + current_item = data->fetch (j); + if ((current_item->obj != NULL) && (current_item->obj->id == sel_index)) + { + found = true; + break; + } + } + if (!found) + continue; + } + char *nm = current_item->obj->get_name (); + if (streq (nm, NTXT ("<Total>"))) + { + // Special case for 'Total'. + // Multi selection which includes 'Total' is only 'Total' + objs->reset (); + objs->append (current_item->obj); + break; + } + objs->append (current_item->obj); + } + } + if (objs->size () == 0) + return NULL; + + // Set name area + Vector<int> *saligns = new Vector<int>(nname); + Vector<char>*mnemonic = new Vector<char>(nname); + Vector<char*> *jlabels = new Vector<char*>(nname); + Vector<char*> *jvalues = new Vector<char*>(nname); + Vector<void*> *name_objs = new Vector<void*>(4); + name_objs->store (0, saligns); + name_objs->store (1, mnemonic); + name_objs->store (2, jlabels); + name_objs->store (3, jvalues); + if (is_mem) + setMemSummary (objs, saligns, mnemonic, jlabels, jvalues); + else if (is_indx) + setIndxSummary (objs, saligns, mnemonic, jlabels, jvalues); + else if (is_data) + setDataSummary (objs, saligns, mnemonic, jlabels, jvalues); + else if (is_iodata) + setIOActivitySummary (objs, saligns, mnemonic, jlabels, jvalues); + else if (is_heapdata) + setHeapActivitySummary (objs, saligns, mnemonic, jlabels, jvalues); + else + setSummary (objs, saligns, mnemonic, jlabels, jvalues); + + // Get the reference metrics + if (is_data) + prop_mlist = new MetricList (dbev->get_metric_ref (MET_DATA)); + else if (is_indx) + prop_mlist = new MetricList (dbev->get_metric_ref (MET_INDX)); + else if (is_iodata) + prop_mlist = new MetricList (dbev->get_metric_ref (MET_IO)); + else if (is_heapdata) + prop_mlist = new MetricList (dbev->get_metric_ref (MET_HEAP)); + else + prop_mlist = new MetricList (dbev->get_metric_ref (MET_NORMAL)); + + // XXXX a workaround to avoid aggregated data for compare mode, only show base experiment data + if (prop_mlist && dbev->comparingExperiments ()) + prop_mlist = dbev->get_compare_mlist (prop_mlist, 0); + nitems = prop_mlist->get_items ()->size (); + + // Set the metrics area + jlabels = new Vector<char*>(nitems); + Vector<double> *clock_list = new Vector<double>(nitems); + Vector<double> *excl_list = new Vector<double>(nitems); + Vector<double> *ep_list = new Vector<double>(nitems); + Vector<double> *incl_list = new Vector<double>(nitems); + Vector<double> *ip_list = new Vector<double>(nitems); + Vector<int> *vtype = new Vector<int>(nitems); + + // Initialize Java String array + Vector<void*> *metric_objs = new Vector<void*>(8); + metric_objs->store (0, jlabels); + metric_objs->store (1, clock_list); + metric_objs->store (2, excl_list); + metric_objs->store (3, ep_list); + metric_objs->store (4, incl_list); + metric_objs->store (5, ip_list); + metric_objs->store (6, vtype); + percent_scale = new Vector<double>(); + int last_init = -1; + for (i = 0; i < objs->size (); i++) + { + Histable *current_obj = objs->fetch (i); + // Get the data to be displayed + data = dbev->get_hist_data (prop_mlist, current_obj->get_type (), subtype, + Hist_data::SELF, current_obj, dbev->sel_binctx, objs); + if (data->get_status () != Hist_data::SUCCESS) + { + if (type != DSP_DLAYOUT) + { // For data_layout, rows with zero metrics are OK + delete data; + continue; + } + } + Hist_data::HistItem *hi = data->fetch (0); + values = hi ? hi->value : NULL; + total = data->get_totals (); + index2 = 0; + + // get the metric list from the data + mlist = data->get_metric_list (); + + // We loop over the metrics in mlist. + // We construct index2, which tells us + // the corresponding entry in the metric_objs lists. + // We need this mapping multiple times. + // So, if you change the looping in any way here, + // do so as well in other similar loops. + // All such loops are marked so: + // See discussion on "mlist-to-index2 mapping". + + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (last_init < index2) + { + last_init = index2; + jlabels->store (index2, NULL); + clock_list->store (index2, 0.0); + excl_list->store (index2, 0.0); + ep_list->store (index2, 0.0); + incl_list->store (index2, 0.0); + ip_list->store (index2, 0.0); + vtype->store (index2, 0); + } + dvalue = (values != NULL) ? values[index].to_double () : 0.0; + double dtotal = total->value[index].to_double (); + percent_scale->store (index, dtotal == 0. ? 0. : 100. / dtotal); + if (mitem->is_time_val ()) + clock = 1.e+6 * dbeSession->get_clock (-1); + else + clock = 0.0; + + clock_list->store (index2, clock); + if (mitem->get_subtype () == Metric::EXCLUSIVE || + mitem->get_subtype () == Metric::DATASPACE) + { + if (i == 0) + { + char *sstr = mitem->get_username (); + char *buf = dbe_strdup (sstr); + jlabels->store (index2, buf); + vtype->store (index2, mitem->get_vtype ()); + } + dvalue += excl_list->fetch (index2); + double percent = dvalue * percent_scale->fetch (index); + excl_list->store (index2, dvalue); + ep_list->store (index2, percent); + if (is_data || is_indx || is_iodata || is_heapdata) + // move to next row, except if there's inclusive data, too + index2++; + } + else + { + dvalue += incl_list->fetch (index2); + if (dvalue > dtotal && mitem->get_type () != BaseMetric::DERIVED) + dvalue = dtotal; // temporary correction + double percent = dvalue * percent_scale->fetch (index); + incl_list->store (index2, dvalue); + ip_list->store (index2, percent); + index2++; + } + } + delete data; + } + + // for multi-selection, we have to recompute the derived metrics + if (objs->size () > 1 && + dbev->get_derived_metrics () != NULL && + dbev->get_derived_metrics ()->get_items () != NULL && + dbev->get_derived_metrics ()->get_items ()->size () > 0) + { + // See discussion on "mlist-to-index2 mapping". + Vector<Metric*> *mvec = new Vector<Metric*>(nitems); + index2 = 0; + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (mitem->get_subtype () == Metric::EXCLUSIVE || + mitem->get_subtype () == Metric::DATASPACE) + { + mvec->store (index2, mitem); + if (is_data || is_indx || is_iodata || is_heapdata) + index2++; + } + else + { + assert (strcmp (mvec->fetch (index2)->get_cmd (), mitem->get_cmd ()) == 0); + index2++; + } + } + int *map = dbev->get_derived_metrics ()->construct_map (mvec, BaseMetric::EXCLUSIVE, mvec->fetch (0)->get_expr_spec ()); + if (map != NULL) + { + int nmetrics = mvec->size (); + double *evalues = (double *) malloc (nmetrics * sizeof (double)); + double *ivalues = (double *) malloc (nmetrics * sizeof (double)); + for (index2 = 0; index2 < nmetrics; index2++) + { + evalues[index2] = excl_list->fetch (index2); + ivalues[index2] = incl_list->fetch (index2); + } + + // evaluate derived metrics + dbev->get_derived_metrics ()->eval (map, evalues); + dbev->get_derived_metrics ()->eval (map, ivalues); + for (index2 = 0; index2 < nmetrics; index2++) + { + excl_list->store (index2, evalues[index2]); + incl_list->store (index2, ivalues[index2]); + } + + // recompute percentages for derived metrics EUGENE maybe all percentage computations should be moved here + // See discussion on "mlist-to-index2 mapping". + index2 = 0; + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (mitem->get_subtype () == Metric::EXCLUSIVE || + mitem->get_subtype () == Metric::DATASPACE) + { + if (mitem->get_type () == BaseMetric::DERIVED) + ep_list->store (index2, excl_list->fetch (index2) * percent_scale->fetch (index)); + if (is_data || is_indx || is_iodata || is_heapdata) + index2++; + } + else + { + if (mitem->get_type () == BaseMetric::DERIVED) + ip_list->store (index2, incl_list->fetch (index2) * percent_scale->fetch (index)); + index2++; + } + } + free (evalues); + free (ivalues); + free (map); + } + delete mvec; + } + delete prop_mlist; + Vector<void*> *summary = new Vector<void*>(2); + summary->store (0, name_objs); + summary->store (1, metric_objs); + delete objs; + delete percent_scale; + return summary; +} + +char * +dbeGetExpName (int /*dbevindex*/, char *dir_name) +{ + char *ret; + char *warn; + if (col_ctr == NULL) + col_ctr = new Coll_Ctrl (1); // Potential race condition? + if (dir_name != NULL) + { + ret = col_ctr->set_directory (dir_name, &warn); + // note that the warning and error msgs are written to stderr, not returned to caller + if (warn != NULL) + fprintf (stderr, NTXT ("%s"), warn); + if (ret != NULL) + fprintf (stderr, NTXT ("%s"), ret); + } + return dbe_strdup (col_ctr->get_expt ()); +} + +// === CollectDialog HWC info === + +Vector<Vector<char*>*> * +dbeGetHwcSets (int /*dbevindex*/, bool forKernel) +{ + Vector<Vector<char*>*> *list = new Vector<Vector<char*>*>(2); + char * defctrs = hwc_get_default_cntrs2 (forKernel, 1); + Vector<char*> *i18n = new Vector<char*>(1); // User name + Vector<char*> *name = new Vector<char*>(1); // Internal name + if (NULL != defctrs) + { + i18n->store (0, strdup (defctrs)); + name->store (0, strdup (NTXT ("default"))); + } + list->store (0, i18n); + list->store (1, name); + return list; +} + +static Vector<void*> * +dbeGetHwcs (Hwcentry **hwcs) +{ + int sz; + for (sz = 0; hwcs && hwcs[sz]; sz++) + ; + Vector<void*> *list = new Vector<void*>(9); + Vector<char*> *i18n = new Vector<char*>(sz); + Vector<char*> *name = new Vector<char*>(sz); + Vector<char*> *int_name = new Vector<char*>(sz); + Vector<char*> *metric = new Vector<char*>(sz); + Vector<long long> *val = new Vector<long long>(sz); + Vector<int> *timecvt = new Vector<int>(sz); + Vector<int> *memop = new Vector<int>(sz); + Vector<char*> *short_desc = new Vector<char*>(sz); + Vector<Vector<int>*> *reglist_v = new Vector<Vector<int>*>(sz); + Vector<bool> *supportsAttrs = new Vector<bool>(sz); + Vector<bool> *supportsMemspace = new Vector<bool>(sz); + + for (int i = 0; i < sz; i++) + { + Hwcentry *ctr = hwcs[i]; + Vector<int> *registers = new Vector<int>(MAX_PICS); + regno_t *reglist = ctr->reg_list; + for (int k = 0; !REG_LIST_EOL (reglist[k]) && k < MAX_PICS; k++) + registers->store (k, reglist[k]); + + i18n->store (i, dbe_strdup (hwc_i18n_metric (ctr))); + name->store (i, dbe_strdup (ctr->name)); + int_name->store (i, dbe_strdup (ctr->int_name)); + metric->store (i, dbe_strdup (ctr->metric)); + val->store (i, ctr->val); // signed promotion from int + timecvt->store (i, ctr->timecvt); + memop->store (i, ctr->memop); + reglist_v->store (i, registers); + short_desc->store (i, dbe_strdup (ctr->short_desc)); + supportsAttrs->store (i, true); + supportsMemspace->store (i, ABST_MEMSPACE_ENABLED (ctr->memop)); + } + list->store (0, i18n); + list->store (1, name); + list->store (2, int_name); + list->store (3, metric); + list->store (4, val); + list->store (5, timecvt); + list->store (6, memop); + list->store (7, short_desc); + list->store (8, reglist_v); + list->store (9, supportsAttrs); + list->store (10, supportsMemspace); + return list; +} + +Vector<void *> * +dbeGetHwcsAll (int /*dbevindex*/, bool forKernel) +{ + Vector<void*> *list = new Vector<void*>(2); + list->store (0, dbeGetHwcs (hwc_get_std_ctrs (forKernel))); + list->store (1, dbeGetHwcs (hwc_get_raw_ctrs (forKernel))); + return list; +} + +Vector<char*> * +dbeGetHwcHelp (int /*dbevindex*/, bool forKernel) +{ + Vector<char*> *strings = new Vector<char*>(32); + FILE *f = tmpfile (); + hwc_usage_f (forKernel, f, "", 0, 0, 1); // writes to f + fflush (f); + fseek (f, 0, SEEK_SET); +#define MAX_LINE_LEN 2048 + char buff[MAX_LINE_LEN]; + int ii = 0; + while (fgets (buff, MAX_LINE_LEN, f)) + strings->store (ii++, dbe_strdup (buff)); + fclose (f); + return strings; +} + +Vector<char*> * +dbeGetHwcAttrList (int /*dbevindex*/, bool forKernel) +{ + char ** attr_list = hwc_get_attrs (forKernel); // Get Attribute list + int size; + for (size = 0; attr_list && attr_list[size]; size++) + ; + + Vector<char*> *name = new Vector<char*>(size); + for (int i = 0; i < size; i++) + name->store (i, dbe_strdup (attr_list[i])); + return name; +} + +//Get maximum number of simultaneous counters +int +dbeGetHwcMaxConcurrent (int /*dbevindex*/, bool forKernel) +{ + return hwc_get_max_concurrent (forKernel); +} + +// === End CollectDialog HWC info === + + +// Instruction-frequency data +Vector<char*> * +dbeGetIfreqData (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (!dbeSession->is_ifreq_available ()) + return NULL; + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(); + for (int i = 0; i < size; i++) + { + Experiment *exp = dbeSession->get_exp (i); + if (exp->broken || !dbev->get_exp_enable (i) || !exp->ifreqavail) + continue; + // write a header for the experiment + list->append (dbe_sprintf (GTXT ("Instruction frequency data from experiment %s\n\n"), + exp->get_expt_name ())); + // add its instruction frequency messages + char *ifreq = pr_mesgs (exp->fetch_ifreq (), NTXT (""), NTXT ("")); + list->append (ifreq); + } + return list; +} + +// LeakList related methods +// +Vector<void*> * +dbeGetLeakListInfo (int dbevindex, bool leakflag) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + MetricList *origmlist = dbev->get_metric_list (MET_NORMAL); + MetricList *nmlist = new MetricList (origmlist); + if (leakflag) + nmlist->set_metrics (NTXT ("e.heapleakbytes:e.heapleakcnt:name"), true, + dbev->get_derived_metrics ()); + else + nmlist->set_metrics (NTXT ("e.heapallocbytes:e.heapalloccnt:name"), true, + dbev->get_derived_metrics ()); + MetricList *mlist = new MetricList (nmlist); + delete nmlist; + + CStack_data *lam = dbev->get_cstack_data (mlist); + if (lam == NULL || lam->size () == 0) + { + delete lam; + delete mlist; + return NULL; + } + Vector<Vector<Obj>*> *evalue = new Vector<Vector<Obj>*>(lam->size ()); + Vector<Vector<Obj>*> *pcstack = new Vector<Vector<Obj>*>(lam->size ()); + Vector<Vector<Obj>*> *offstack = new Vector<Vector<Obj>*>(lam->size ()); + Vector<Vector<Obj>*> *fpcstack = new Vector<Vector<Obj>*>(lam->size ()); + Vector<Vector<Obj>*> *sumval = new Vector<Vector<Obj>*>(lam->size ()); + + int index; + CStack_data::CStack_item *lae; + Vec_loop (CStack_data::CStack_item*, lam->cstack_items, index, lae) + { + Vector<Obj> *jivals = NULL; + if (lae != NULL) + { + jivals = new Vector<Obj>(4); + jivals->store (0, (Obj) (index + 1)); + jivals->store (1, (Obj) lae->value[1].ll); + jivals->store (2, (Obj) lae->value[0].ll); + jivals->store (3, (Obj) (leakflag ? 1 : 2)); + } + evalue->store (index, jivals); + int snum = lae->stack->size (); + Vector<Obj> *jivals1 = new Vector<Obj>(snum); + Vector<Obj> *jivals2 = new Vector<Obj>(snum); + Vector<Obj> *jivals3 = new Vector<Obj>(snum); + if (lae->stack != NULL) + { + for (int i = lae->stack->size () - 1; i >= 0; i--) + { + DbeInstr *instr = lae->stack->fetch (i); + jivals1->store (i, (Obj) instr); + jivals2->store (i, (Obj) instr->func); + jivals3->store (i, (Obj) instr->addr); + } + } + fpcstack->store (index, jivals1); + pcstack->store (index, jivals2); + offstack->store (index, jivals3); + lae++; + } + Vector<Obj> *jivals4 = new Vector<Obj>(3); + jivals4->store (0, (Obj) lam->size ()); + jivals4->store (1, (Obj) lam->total->value[1].ll); + jivals4->store (2, (Obj) lam->total->value[0].ll); + sumval->store (0, jivals4); + delete lam; + delete mlist; + Vector<void*> *earray = new Vector<void*>(5); + earray->store (0, evalue); + earray->store (1, pcstack); + earray->store (2, offstack); + earray->store (3, fpcstack); + earray->store (4, sumval); + return earray; +} + +// Map timeline address to function instr +// +Obj +dbeGetObject (int dbevindex, Obj sel_func, Obj sel_pc) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (sel_pc) + return sel_pc; + return sel_func; +} + +char * +dbeGetName (int /*dbevindex*/, int exp_id) +// This function's name is not descriptive enough - it returns a string +// containing the full experiment name with path, process name, and PID. +// There are various dbe functions that provide experiment name and experiment +// details, and they should probably be consolidated/refactored. (TBR) +// For another example of similar output formatting, see dbeGetExpName(). +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + if (exp == NULL) + return NULL; + char *buf = + dbe_sprintf (NTXT ("%s [%s, PID %d]"), + exp->get_expt_name (), + exp->utargname != NULL ? exp->utargname : GTXT ("(unknown)"), + exp->getPID ()); + return buf; +} + +Vector<char*> * +dbeGetExpVerboseName (Vector<int> *exp_ids) +{ + int len = exp_ids->size (); + Vector<char*> *list = new Vector<char*>(len); + for (int i = 0; i < len; i++) + { + char * verboseName = dbeGetName (0, exp_ids->fetch (i)); // no strdup() + list->store (i, verboseName); + } + return list; +} + +long long +dbeGetStartTime (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + return exp ? exp->getStartTime () : (long long) 0; +} + +long long +dbeGetRelativeStartTime (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + return exp ? exp->getRelativeStartTime () : (long long) 0; +} + +long long +dbeGetEndTime (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + + // Experiment::getEndTime was initially implemented as + // returning exp->last_event. To preserve the semantics + // new Experiment::getLastEvent() is used here. + return exp ? exp->getLastEvent () : (long long) 0; +} + +int +dbeGetClock (int /*dbevindex*/, int exp_id) +{ + return dbeSession->get_clock (exp_id); +} + +long long +dbeGetWallStartSec (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + return exp ? exp->getWallStartSec () : 0ll; +} + +char * +dbeGetHostname (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + return exp ? dbe_strdup (exp->hostname) : NULL; +} + +static DataView * +getTimelinePackets (int dbevindex, int exp_id, int data_id, int entity_prop_id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + const int sortprop_count = 3; + const int sortprops[sortprop_count] = { + PROP_HWCTAG, // aux + entity_prop_id, + PROP_TSTAMP + }; + DataView *packets = dbev->get_filtered_events (exp_id, data_id, + sortprops, sortprop_count); + return packets; +} + +static long +getIdxByVals (DataView * packets, int aux, int entity_prop_val, + uint64_t time, DataView::Relation rel) +{ + const int sortprop_count = 3; + Datum tval[sortprop_count]; + tval[0].setUINT32 (aux); + tval[1].setUINT32 (entity_prop_val); //CPUID, LWPID, THRID are downsized to 32 + tval[2].setUINT64 (time); + long idx = packets->getIdxByVals (tval, rel); + return idx; +} + +static bool +isValidIdx (DataView * packets, int entity_prop_id, + int aux, int entity_prop_val, long idx) +{ + if (idx < 0 || idx >= packets->getSize ()) + return false; + int pkt_aux = packets->getIntValue (PROP_HWCTAG, idx); + if (pkt_aux != aux) + return false; + if (entity_prop_id == PROP_EXPID) + return true; // not a packet property; we know the packet is in this experiment + if (entity_prop_id == PROP_NONE) + return true; // not a packet property; we know the packet is in this experiment + int pkt_ent = packets->getIntValue (entity_prop_id, idx); + if (pkt_ent != entity_prop_val) + return false; + return true; +} + +static bool +hasInvisbleTLEvents (Experiment *exp, VMode view_mode) +{ + if (exp->has_java && view_mode == VMODE_USER) + return true; + return false; +} + +static bool +isVisibleTLEvent (Experiment *exp, VMode view_mode, DataView* packets, long idx) +{ + if (hasInvisbleTLEvents (exp, view_mode)) + { + JThread *jthread = (JThread*) packets->getObjValue (PROP_JTHREAD, idx); + if (jthread == JTHREAD_NONE || (jthread != NULL && jthread->is_system ())) + return false; + } + return true; +} + +static long +getTLVisibleIdxByStepping (Experiment *exp, VMode view_mode, int entity_prop_id, + DataView * packets, int aux, int entity_prop_val, + long idx, long move_count, int direction) +{ + assert (move_count >= 0); + assert (direction == 1 || direction == -1 || direction == 0); + if (direction == 0 /* precise hit required */) + move_count = 0; + do + { + if (!isValidIdx (packets, entity_prop_id, aux, entity_prop_val, idx)) + return -1; + if (isVisibleTLEvent (exp, view_mode, packets, idx)) + { + if (move_count <= 0) + break; + move_count--; + } + if (direction == 0) + return -1; + idx += direction; + } + while (1); + return idx; +} + +static long +getTLVisibleIdxByVals (Experiment *exp, VMode view_mode, int entity_prop_id, + DataView * packets, + int aux, int entity_prop_val, uint64_t time, DataView::Relation rel) +{ + long idx = getIdxByVals (packets, aux, entity_prop_val, time, rel); + if (!hasInvisbleTLEvents (exp, view_mode)) + return idx; + if (idx < 0) + return idx; + if (rel == DataView::REL_EQ) + return -1; // would require bi-directional search... not supported for now + int direction = (rel == DataView::REL_LT || rel == DataView::REL_LTEQ) ? -1 : 1; + idx = getTLVisibleIdxByStepping (exp, view_mode, entity_prop_id, packets, + aux, entity_prop_val, + idx, 0 /* first match */, direction); + return idx; +} + +// In thread mode, the entity name for non Java thread should be the 1st func +// from the current thread's stack. See #4961315 +static char* +getThreadRootFuncName (int, int, int, int, VMode) +{ + return NULL; // until we figure out what we want to show... YXXX +} + +Vector<void*> * +dbeGetEntityProps (int dbevindex) //YXXX TBD, should this be exp-specific? +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<int> *prop_id = new Vector<int>(); + Vector<char*> *prop_name = new Vector<char*>(); + Vector<char*> *prop_uname = new Vector<char*>(); + Vector<char*> *prop_cname = new Vector<char*>(); //must match TLModeCmd vals! + + prop_id->append (PROP_NONE); + prop_name->append (dbe_strdup (GTXT ("NONE"))); + prop_uname->append (dbe_strdup (GTXT ("Unknown"))); + prop_cname->append (dbe_strdup (NTXT ("unknown"))); + + prop_id->append (PROP_LWPID); + prop_name->append (dbe_strdup (GTXT ("LWPID"))); + prop_uname->append (dbe_strdup (GTXT ("LWP"))); + prop_cname->append (dbe_strdup (NTXT ("lwp"))); + + prop_id->append (PROP_THRID); + prop_name->append (dbe_strdup (GTXT ("THRID"))); + prop_uname->append (dbe_strdup (GTXT ("Thread"))); + prop_cname->append (dbe_strdup (NTXT ("thread"))); + + prop_id->append (PROP_CPUID); + prop_name->append (dbe_strdup (GTXT ("CPUID"))); + prop_uname->append (dbe_strdup (GTXT ("CPU"))); + prop_cname->append (dbe_strdup (NTXT ("cpu"))); + + prop_id->append (PROP_EXPID); + prop_name->append (dbe_strdup (GTXT ("EXPID"))); + prop_uname->append (dbe_strdup (GTXT ("Process"))); // placeholder... + // ...until we finalize how to expose user-level Experiments, descendents + prop_cname->append (dbe_strdup (NTXT ("experiment"))); + Vector<void*> *darray = new Vector<void*>(); + darray->store (0, prop_id); + darray->store (1, prop_name); + darray->store (2, prop_uname); + darray->store (3, prop_cname); + return darray; +} + +Vector<void*> * +dbeGetEntities (int dbevindex, int exp_id, int entity_prop_id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Experiment *exp = dbeSession->get_exp (exp_id); + if (exp == NULL) + return NULL; + + // Recognize and skip faketime experiments + if (exp->timelineavail == false) + return NULL; + Vector<Histable*> *tagObjs = exp->getTagObjs ((Prop_type) entity_prop_id); + int total_nelem; + if (tagObjs) + total_nelem = (int) tagObjs->size (); + else + total_nelem = 0; + const VMode view_mode = dbev->get_view_mode (); + bool show_java_threadnames = (entity_prop_id == PROP_THRID && + view_mode != VMODE_MACHINE); + // allocate the structures for the return + Vector<int> *entity_prop_vals = new Vector<int>(); + Vector<char*> *jthr_names = new Vector<char*>(); + Vector<char*> *jthr_g_names = new Vector<char*>(); + Vector<char*> *jthr_p_names = new Vector<char*>(); + + // now walk the tagObjs from the experiment, and check for filtering + for (int tagObjsIdx = 0; tagObjsIdx < total_nelem; tagObjsIdx++) + { + int entity_prop_val = (int) ((Other *) tagObjs->fetch (tagObjsIdx))->tag; + entity_prop_vals->append (entity_prop_val); + char *jname, *jgname, *jpname; + JThread *jthread = NULL; + bool has_java_threadnames = false; + if (show_java_threadnames) + { + jthread = exp->get_jthread (entity_prop_val); + has_java_threadnames = (jthread != JTHREAD_DEFAULT + && jthread != JTHREAD_NONE); + } + if (!has_java_threadnames) + { + jname = jgname = jpname = NULL; + if (entity_prop_id == PROP_THRID || entity_prop_id == PROP_LWPID) + // if non Java thread, set thread name to the 1st func + // from the current thread's stack. see #4961315 + jname = getThreadRootFuncName (dbevindex, exp_id, entity_prop_id, + entity_prop_val, view_mode); + } + else + { + jname = dbe_strdup (jthread->name); + jgname = dbe_strdup (jthread->group_name); + jpname = dbe_strdup (jthread->parent_name); + } + jthr_names->append (jname); + jthr_g_names->append (jgname); + jthr_p_names->append (jpname); + } + Vector<char*> *entity_prop_name_v = new Vector<char*>(); + char* entity_prop_name = dbeSession->getPropName (entity_prop_id); + entity_prop_name_v->append (entity_prop_name); + Vector<void*> *darray = new Vector<void*>(5); + darray->store (0, entity_prop_vals); + darray->store (1, jthr_names); + darray->store (2, jthr_g_names); + darray->store (3, jthr_p_names); + darray->store (4, entity_prop_name_v); // vector only has 1 element + return darray; +} + +// TBR: dbeGetEntities() can be set to private now that we have dbeGetEntitiesV2() +Vector<void*> * +dbeGetEntitiesV2 (int dbevindex, Vector<int> *exp_ids, int entity_prop_id) +{ + int sz = exp_ids->size (); + Vector<void*> *res = new Vector<void*>(sz); + for (int ii = 0; ii < sz; ii++) + { + int expIdx = exp_ids->fetch (ii); + Vector<void*>* ents = dbeGetEntities (dbevindex, expIdx, entity_prop_id); + res->store (ii, ents); + } + return res; +} + +//YXXX old-tl packets still used for details +static Vector<void*> * +getTLDetailValues (int dbevindex, Experiment * exp, int data_id, + VMode view_mode, DataView *packets, long idx) +{ + Vector<long long> *value = new Vector<long long>(15); + long i = idx; + if (data_id == DATA_SAMPLE || data_id == DATA_GCEVENT) + { + //YXXX DATA_SAMPLE not handled but could be. + } + Obj stack = (unsigned long) getStack (view_mode, packets, i); + Vector<Obj> *funcs = stack ? dbeGetStackFunctions (dbevindex, stack) : NULL; + Function *func = (Function*) + getStackPC (0, view_mode, packets, i)->convertto (Histable::FUNCTION); + // Fill common data + value->store (0, packets->getIntValue (PROP_LWPID, i)); + value->store (1, packets->getIntValue (PROP_THRID, i)); + value->store (2, packets->getIntValue (PROP_CPUID, i)); + value->store (3, packets->getLongValue (PROP_TSTAMP, i)); + value->store (4, (unsigned long) stack); + value->store (5, (unsigned long) func); + + // Fill specific data + switch (data_id) + { + case DATA_CLOCK: + value->store (6, packets->getIntValue (PROP_MSTATE, i)); + { + hrtime_t interval = exp->get_params ()->ptimer_usec * 1000LL // nanoseconds + * packets->getLongValue (PROP_NTICK, i); + value->store (7, interval); + } + value->store (8, packets->getIntValue (PROP_OMPSTATE, i)); + value->store (9, packets->getLongValue (PROP_EVT_TIME, i)); // visual duration + break; + case DATA_SYNCH: + value->store (6, packets->getLongValue (PROP_EVT_TIME, i)); + value->store (7, packets->getLongValue (PROP_SOBJ, i)); + break; + case DATA_HWC: + value->store (6, packets->getLongValue (PROP_HWCINT, i)); + value->store (7, packets->getLongValue (PROP_VADDR, i)); // data vaddr + value->store (8, packets->getLongValue (PROP_PADDR, i)); // data paddr + value->store (9, packets->getLongValue (PROP_VIRTPC, i)); // pc paddr + value->store (10, packets->getLongValue (PROP_PHYSPC, i)); // pc vaddr + break; + case DATA_RACE: + value->store (6, packets->getIntValue (PROP_RTYPE, i)); + value->store (7, packets->getIntValue (PROP_RID, i)); + value->store (8, packets->getLongValue (PROP_RVADDR, i)); + break; + case DATA_DLCK: + value->store (6, packets->getIntValue (PROP_DTYPE, i)); + value->store (7, packets->getIntValue (PROP_DLTYPE, i)); + value->store (8, packets->getIntValue (PROP_DID, i)); + value->store (9, packets->getLongValue (PROP_DVADDR, i)); + break; + case DATA_HEAP: + case DATA_HEAPSZ: + value->store (6, packets->getIntValue (PROP_HTYPE, i)); + value->store (7, packets->getLongValue (PROP_HSIZE, i)); + value->store (8, packets->getLongValue (PROP_HVADDR, i)); + value->store (9, packets->getLongValue (PROP_HOVADDR, i)); + value->store (10, packets->getLongValue (PROP_HLEAKED, i)); + value->store (11, packets->getLongValue (PROP_HFREED, i)); + value->store (12, packets->getLongValue (PROP_HCUR_ALLOCS, i)); // signed int64_t + value->store (13, packets->getLongValue (PROP_HCUR_LEAKS, i)); + break; + case DATA_IOTRACE: + value->store (6, packets->getIntValue (PROP_IOTYPE, i)); + value->store (7, packets->getIntValue (PROP_IOFD, i)); + value->store (8, packets->getLongValue (PROP_IONBYTE, i)); + value->store (9, packets->getLongValue (PROP_EVT_TIME, i)); + value->store (10, packets->getIntValue (PROP_IOVFD, i)); + break; + } + Vector<void*> *result = new Vector<void*>(5); + result->store (0, value); + result->store (1, funcs); // Histable::Function* + result->store (2, funcs ? dbeGetFuncNames (dbevindex, funcs) : 0); // formatted func names + result->store (3, stack ? dbeGetStackPCs (dbevindex, stack) : 0); // Histable::DbeInstr* + result->store (4, stack ? dbeGetStackNames (dbevindex, stack) : 0); // formatted pc names + return result; +} + +Vector<void*> * +dbeGetTLDetails (int dbevindex, int exp_id, int data_id, + int entity_prop_id, Obj event_id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Experiment *exp = dbeSession->get_exp (exp_id < 0 ? 0 : exp_id); + if (exp == NULL) + return NULL; + DataView *packets = + getTimelinePackets (dbevindex, exp_id, data_id, entity_prop_id); + if (!packets) + return NULL; + + VMode view_mode = dbev->get_view_mode (); + long idx = (long) event_id; + Vector<void*> *values = getTLDetailValues (dbevindex, exp, data_id, view_mode, packets, idx); + return values; +} + +Vector<Obj> * +dbeGetStackFunctions (int dbevindex, Obj stack) +{ + Vector<Obj> *instrs = dbeGetStackPCs (dbevindex, stack); + if (instrs == NULL) + return NULL; + int stsize = instrs->size (); + Vector<Obj> *jivals = new Vector<Obj>(stsize); + for (int i = 0; i < stsize; i++) + { + Histable *obj = (Histable*) instrs->fetch (i); + // if ( obj->get_type() != Histable::LINE ) {//YXXX what is this? + // Remove the above check: why not do this conversion for lines - + // otherwise filtering in timeline by function stack in omp user mode is broken + obj = obj->convertto (Histable::FUNCTION); + jivals->store (i, (Obj) obj); + } + delete instrs; + return jivals; +} + +Vector<void*> * +dbeGetStacksFunctions (int dbevindex, Vector<Obj> *stacks) +{ + long sz = stacks->size (); + Vector<void*> *res = new Vector<void*>(sz); + for (int ii = 0; ii < sz; ii++) + { + Obj stack = stacks->fetch (ii); + Vector<Obj> *jivals = dbeGetStackFunctions (dbevindex, stack); + res->store (ii, jivals); + } + return res; +} + +Vector<Obj> * +dbeGetStackPCs (int dbevindex, Obj stack) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (stack == 0) + return NULL; + + bool show_all = dbev->isShowAll (); + Vector<Histable*> *instrs = CallStack::getStackPCs ((void *) stack, !show_all); + int stsize = instrs->size (); + int istart = 0; + bool showAll = dbev->isShowAll (); + for (int i = 0; i < stsize - 1; i++) + { + Function *func = (Function*) instrs->fetch (i)->convertto (Histable::FUNCTION); + int ix = func->module->loadobject->seg_idx; + if (showAll && dbev->get_lo_expand (ix) == LIBEX_API) + // truncate stack here: LIBRARY_VISIBILITY if we are using API only but no hide + istart = i; + } + stsize = stsize - istart; + Vector<Obj> *jlvals = new Vector<Obj>(stsize); + for (int i = 0; i < stsize; i++) + { + Histable *instr = instrs->fetch (i + istart); + jlvals->store (i, (Obj) instr); + } + delete instrs; + return jlvals; +} + +Vector<char*> * +dbeGetStackNames (int dbevindex, Obj stack) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Vector<Obj> *instrs = dbeGetStackPCs (dbevindex, stack); + if (instrs == NULL) + return NULL; + int stsize = instrs->size (); + Vector<char*> *list = new Vector<char*>(stsize); + bool showAll = dbev->isShowAll (); + for (int i = 0; i < stsize; i++) + { + Histable* instr = (Histable*) instrs->fetch (i); + if (!showAll) + { + // LIBRARY_VISIBILITY + Function *func = (Function*) instr->convertto (Histable::FUNCTION); + LoadObject *lo = ((Function*) func)->module->loadobject; + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + { + list->store (i, dbe_strdup (lo->get_name ())); + continue; + } + } + list->store (i, dbe_strdup (instr->get_name (dbev->get_name_format ()))); + } + delete instrs; + return list; +} + +Vector<void*> * +dbeGetSamples (int dbevindex, int exp_id, int64_t lo_idx, int64_t hi_idx) +{ + DataView * packets = + getTimelinePackets (dbevindex, exp_id, DATA_SAMPLE, PROP_EXPID); + if (packets == NULL || packets->getSize () == 0) + return NULL; + long lo; + if (lo_idx < 0) + lo = 0; + else + lo = (long) lo_idx; + + long long max = packets->getSize () - 1; + long hi; + if (hi_idx < 0 || hi_idx > max) + hi = (long) max; + else + hi = (long) hi_idx; + + Vector<Vector<long long>*> *sarray = new Vector<Vector<long long>*>; + Vector<long long>* starts = new Vector<long long>; + Vector<long long>* ends = new Vector<long long>; + Vector<long long>* rtimes = new Vector<long long>; + Vector<char*> *startNames = new Vector<char*>; + Vector<char*> *endNames = new Vector<char*>; + Vector<int> *sampId = new Vector<int>; + + for (long index = lo; index <= hi; index++) + { + Sample *sample = (Sample*) packets->getObjValue (PROP_SMPLOBJ, index); + PrUsage *prusage = sample->get_usage (); + if (prusage == NULL) + prusage = new PrUsage; + Vector<long long> *states = prusage->getMstateValues (); + sarray->append (states); + starts->append (sample->get_start_time ()); + ends->append (sample->get_end_time ()); + rtimes->append (prusage->pr_rtime); + startNames->append (dbe_strdup (sample->get_start_label ())); + endNames->append (dbe_strdup (sample->get_end_label ())); + sampId->append (sample->get_number ()); + } + Vector<void *> *res = new Vector<void*>(6); + res->store (0, sarray); + res->store (1, starts); + res->store (2, ends); + res->store (3, rtimes); + res->store (4, startNames); + res->store (5, endNames); + res->store (6, sampId); + return res; +} + +Vector<void*> * +dbeGetGCEvents (int dbevindex, int exp_id, int64_t lo_idx, int64_t hi_idx) +{ + DataView *packets = + getTimelinePackets (dbevindex, exp_id, DATA_GCEVENT, PROP_EXPID); + if (packets == NULL || packets->getSize () == 0) + return NULL; + + long lo; + if (lo_idx < 0) + lo = 0; + else + lo = (long) lo_idx; + long long max = packets->getSize () - 1; + long hi; + if (hi_idx < 0 || hi_idx > max) + hi = (long) max; + else + hi = (long) hi_idx; + + Vector<long long>* starts = new Vector<long long>; + Vector<long long>* ends = new Vector<long long>; + Vector<int> *eventId = new Vector<int>; + for (long index = lo; index <= hi; index++) + { + GCEvent *gcevent = (GCEvent*) packets->getObjValue (PROP_GCEVENTOBJ, index); + if (gcevent) + { + starts->append (gcevent->start); + ends->append (gcevent->end); + eventId->append (gcevent->id); + } + } + Vector<void *> *res = new Vector<void*>(3); + res->store (0, starts); + res->store (1, ends); + res->store (2, eventId); + return res; +} + +Vector<Vector<char*>*>* +dbeGetIOStatistics (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Hist_data *hist_data; + Hist_data::HistItem *hi; + FileData *fDataTotal; + + hist_data = dbev->iofile_data; + if (hist_data == NULL) + return NULL; + hi = hist_data->fetch (0); + fDataTotal = (FileData*) hi->obj; + + Vector<char*> *writeStat = new Vector<char*>; + Vector<char*> *readStat = new Vector<char*>; + Vector<char*> *otherStat = new Vector<char*>; + Vector<char*> *errorStat = new Vector<char*>; + + writeStat->append (dbe_strdup (GTXT ("Write Statistics"))); + readStat->append (dbe_strdup (GTXT ("Read Statistics"))); + otherStat->append (dbe_strdup (GTXT ("Other I/O Statistics"))); + errorStat->append (dbe_strdup (GTXT ("I/O Error Statistics"))); + + StringBuilder sb; + if (fDataTotal->getWriteCnt () > 0) + { + if (fDataTotal->getW0KB1KBCnt () > 0) + { + sb.sprintf (GTXT ("0KB - 1KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW0KB1KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW1KB8KBCnt () > 0) + { + sb.sprintf (GTXT ("1KB - 8KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW1KB8KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW8KB32KBCnt () > 0) + { + sb.sprintf (GTXT ("8KB - 32KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW8KB32KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW32KB128KBCnt () > 0) + { + sb.sprintf (GTXT ("32KB - 128KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW32KB128KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW128KB256KBCnt () > 0) + { + sb.sprintf (GTXT ("128KB - 256KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW128KB256KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW256KB512KBCnt () > 0) + { + sb.sprintf (GTXT ("256KB - 512KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW256KB512KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW512KB1000KBCnt () > 0) + { + sb.sprintf (GTXT ("512KB - 1000KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW512KB1000KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW1000KB10MBCnt () > 0) + { + sb.sprintf (GTXT ("1000KB - 10MB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW1000KB10MBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW10MB100MBCnt () > 0) + { + sb.sprintf (GTXT ("10MB - 100MB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW10MB100MBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW100MB1GBCnt () > 0) + { + sb.sprintf (GTXT ("100MB - 1GB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW100MB1GBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW1GB10GBCnt () > 0) + { + sb.sprintf (GTXT ("1GB - 10GB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW1GB10GBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW10GB100GBCnt () > 0) + { + sb.sprintf (GTXT ("10GB - 100GB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW10GB100GBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW100GB1TBCnt () > 0) + { + sb.sprintf (GTXT ("100GB - 1TB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW100GB1TBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW1TB10TBCnt () > 0) + { + sb.sprintf (GTXT ("1TB - 10TB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW1TB10TBCnt ()); + writeStat->append (sb.toString ()); + } + + sb.sprintf (GTXT ("Longest write")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getWSlowestBytes () / (double) NANOSEC)); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Smallest write bytes")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getWSmallestBytes ())); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Largest write bytes")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getWLargestBytes ())); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total time")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getWriteTime () / (double) NANOSEC)); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total calls")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getWriteCnt ())); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total bytes")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (fDataTotal->getWriteBytes ())); + writeStat->append (sb.toString ()); + } + + if (fDataTotal->getReadCnt () > 0) + { + if (fDataTotal->getR0KB1KBCnt () > 0) + { + sb.sprintf (GTXT ("0KB - 1KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR0KB1KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR1KB8KBCnt () > 0) + { + sb.sprintf (GTXT ("1KB - 8KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR1KB8KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR8KB32KBCnt () > 0) + { + sb.sprintf (GTXT ("8KB - 32KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR8KB32KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR32KB128KBCnt () > 0) + { + sb.sprintf (GTXT ("32KB - 128KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR32KB128KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR128KB256KBCnt () > 0) + { + sb.sprintf (GTXT ("128KB - 256KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR128KB256KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR256KB512KBCnt () > 0) + { + sb.sprintf (GTXT ("256KB - 512KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR256KB512KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR512KB1000KBCnt () > 0) + { + sb.sprintf (GTXT ("512KB - 1000KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR512KB1000KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR1000KB10MBCnt () > 0) + { + sb.sprintf (GTXT ("1000KB - 10MB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR1000KB10MBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR10MB100MBCnt () > 0) + { + sb.sprintf (GTXT ("10MB - 100MB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR10MB100MBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR100MB1GBCnt () > 0) + { + sb.sprintf (GTXT ("100MB - 1GB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR100MB1GBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR1GB10GBCnt () > 0) + { + sb.sprintf (GTXT ("1GB - 10GB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR1GB10GBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR10GB100GBCnt () > 0) + { + sb.sprintf (GTXT ("10GB - 100GB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR10GB100GBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR100GB1TBCnt () > 0) + { + sb.sprintf (GTXT ("100GB - 1TB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR100GB1TBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR1TB10TBCnt () > 0) + { + sb.sprintf (GTXT ("1TB - 10TB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR1TB10TBCnt ()); + readStat->append (sb.toString ()); + } + + sb.sprintf (GTXT ("Longest read")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getRSlowestBytes () / (double) NANOSEC)); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Smallest read bytes")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getRSmallestBytes ())); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Largest read bytes")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getRLargestBytes ())); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total time")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getReadTime () / (double) NANOSEC)); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total calls")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getReadCnt ())); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total bytes")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (fDataTotal->getReadBytes ())); + readStat->append (sb.toString ()); + } + + if (fDataTotal->getOtherCnt () > 0) + { + sb.sprintf (GTXT ("Total time")); + otherStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getOtherTime () / (double) NANOSEC)); + otherStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total calls")); + otherStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getOtherCnt ())); + otherStat->append (sb.toString ()); + } + + if (fDataTotal->getErrorCnt () > 0) + { + sb.sprintf (GTXT ("Total time")); + errorStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getErrorTime () / (double) NANOSEC)); + errorStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total calls")); + errorStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getErrorCnt ())); + errorStat->append (sb.toString ()); + } + Vector<Vector<char*>*>* statisticsData = new Vector<Vector<char*>*>(4); + statisticsData->store (0, writeStat); + statisticsData->store (1, readStat); + statisticsData->store (2, otherStat); + statisticsData->store (3, errorStat); + return statisticsData; +} + +Vector<Vector<char*>*>* +dbeGetHeapStatistics (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Hist_data *hist_data; + Hist_data::HistItem *hi; + HeapData *hDataTotal; + hist_data = dbev->heapcs_data; + if (hist_data == NULL) + return NULL; + + hi = hist_data->fetch (0); + hDataTotal = (HeapData*) hi->obj; + Vector<char*> *memoryUsage = new Vector<char*>; + Vector<char*> *allocStat = new Vector<char*>; + Vector<char*> *leakStat = new Vector<char*>; + + memoryUsage->append (dbe_strdup (GTXT ("Process With Highest Peak Memory Usage"))); + allocStat->append (dbe_strdup (GTXT ("Memory Allocations Statistics"))); + leakStat->append (dbe_strdup (GTXT ("Memory Leaks Statistics"))); + StringBuilder sb; + if (hDataTotal->getPeakMemUsage () > 0) + { + sb.sprintf (GTXT ("Heap size bytes")); + memoryUsage->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (hDataTotal->getPeakMemUsage ())); + memoryUsage->append (sb.toString ()); + + sb.sprintf (GTXT ("Experiment Id")); + memoryUsage->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getUserExpId ())); + memoryUsage->append (sb.toString ()); + + sb.sprintf (GTXT ("Process Id")); + memoryUsage->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getPid ())); + memoryUsage->append (sb.toString ()); + + Vector<hrtime_t> *pTimestamps; + pTimestamps = hDataTotal->getPeakTimestamps (); + if (pTimestamps != NULL) + { + for (int i = 0; i < pTimestamps->size (); i++) + { + sb.sprintf (GTXT ("Time of peak")); + memoryUsage->append (sb.toString ()); + sb.sprintf (NTXT ("%.3f (secs.)"), (double) (pTimestamps->fetch (i) / (double) NANOSEC)); + memoryUsage->append (sb.toString ()); + } + } + } + + if (hDataTotal->getAllocCnt () > 0) + { + if (hDataTotal->getA0KB1KBCnt () > 0) + { + sb.sprintf (GTXT ("0KB - 1KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA0KB1KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA1KB8KBCnt () > 0) + { + sb.sprintf (GTXT ("1KB - 8KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA1KB8KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA8KB32KBCnt () > 0) + { + sb.sprintf (GTXT ("8KB - 32KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA8KB32KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA32KB128KBCnt () > 0) + { + sb.sprintf (GTXT ("32KB - 128KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA32KB128KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA128KB256KBCnt () > 0) + { + sb.sprintf (GTXT ("128KB - 256KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA128KB256KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA256KB512KBCnt () > 0) + { + sb.sprintf (GTXT ("256KB - 512KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA256KB512KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA512KB1000KBCnt () > 0) + { + sb.sprintf (GTXT ("512KB - 1000KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA512KB1000KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA1000KB10MBCnt () > 0) + { + sb.sprintf (GTXT ("1000KB - 10MB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA1000KB10MBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA10MB100MBCnt () > 0) + { + sb.sprintf (GTXT ("10MB - 100MB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA10MB100MBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA100MB1GBCnt () > 0) + { + sb.sprintf (GTXT ("100MB - 1GB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA100MB1GBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA1GB10GBCnt () > 0) + { + sb.sprintf (GTXT ("1GB - 10GB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA1GB10GBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA10GB100GBCnt () > 0) + { + sb.sprintf (GTXT ("10GB - 100GB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA10GB100GBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA100GB1TBCnt () > 0) + { + sb.sprintf (GTXT ("100GB - 1TB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA100GB1TBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA1TB10TBCnt () > 0) + { + sb.sprintf (GTXT ("1TB - 10TB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA1TB10TBCnt ()); + allocStat->append (sb.toString ()); + } + + sb.sprintf (GTXT ("Smallest allocation bytes")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getASmallestBytes ())); + allocStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Largest allocation bytes")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getALargestBytes ())); + allocStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total allocations")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getAllocCnt ())); + allocStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total bytes")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (hDataTotal->getAllocBytes ())); + allocStat->append (sb.toString ()); + } + + if (hDataTotal->getLeakCnt () > 0) + { + if (hDataTotal->getL0KB1KBCnt () > 0) + { + sb.sprintf (GTXT ("0KB - 1KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL0KB1KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL1KB8KBCnt () > 0) + { + sb.sprintf (GTXT ("1KB - 8KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL1KB8KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL8KB32KBCnt () > 0) + { + sb.sprintf (GTXT ("8KB - 32KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL8KB32KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL32KB128KBCnt () > 0) + { + sb.sprintf (GTXT ("32KB - 128KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL32KB128KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL128KB256KBCnt () > 0) + { + sb.sprintf (GTXT ("128KB - 256KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL128KB256KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL256KB512KBCnt () > 0) + { + sb.sprintf (GTXT ("256KB - 512KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL256KB512KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL512KB1000KBCnt () > 0) + { + sb.sprintf (GTXT ("512KB - 1000KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL512KB1000KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL1000KB10MBCnt () > 0) + { + sb.sprintf (GTXT ("1000KB - 10MB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL1000KB10MBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL10MB100MBCnt () > 0) + { + sb.sprintf (GTXT ("10MB - 100MB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL10MB100MBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL100MB1GBCnt () > 0) + { + sb.sprintf (GTXT ("100MB - 1GB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL100MB1GBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL1GB10GBCnt () > 0) + { + sb.sprintf (GTXT ("1GB - 10GB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL1GB10GBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL10GB100GBCnt () > 0) + { + sb.sprintf (GTXT ("10GB - 100GB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL10GB100GBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL100GB1TBCnt () > 0) + { + sb.sprintf (GTXT ("100GB - 1TB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL100GB1TBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL1TB10TBCnt () > 0) + { + sb.sprintf (GTXT ("1TB - 10TB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL1TB10TBCnt ()); + leakStat->append (sb.toString ()); + } + + sb.sprintf (GTXT ("Smallest leaked bytes")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getLSmallestBytes ())); + leakStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Largest leaked bytes")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getLLargestBytes ())); + leakStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total leaked")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getLeakCnt ())); + leakStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total bytes")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (hDataTotal->getLeakBytes ())); + leakStat->append (sb.toString ()); + } + Vector<Vector<char*>*>* statisticsData = new Vector<Vector<char*>*>(3); + statisticsData->store (0, memoryUsage); + statisticsData->store (1, allocStat); + statisticsData->store (2, leakStat); + return statisticsData; +} + +Vector<char*> * +dbeGetFuncNames (int dbevindex, Vector<Obj> *funcs) +{ + int len = funcs->size (); + Vector<char*> *list = new Vector<char*>(len); + for (int i = 0; i < len; i++) + list->store (i, dbeGetFuncName (dbevindex, funcs->fetch (i))); // no strdup() + return list; +} + +Vector<char*> * +dbeGetObjNamesV2 (int dbevindex, Vector<uint64_t> *ids) +{ + int len = ids->size (); + Vector<char*> *list = new Vector<char*>(len); + for (int i = 0; i < len; i++) + list->store (i, dbeGetObjNameV2 (dbevindex, ids->fetch (i))); // no strdup() + return list; +} + +char * +dbeGetFuncName (int dbevindex, Obj func) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (func == 0) + return NULL; + char *fname; + fname = ((Histable *) func)->get_name (dbev->get_name_format ()); + return fname ? dbe_strdup (fname) : NULL; +} + +Vector<uint64_t> * +dbeGetFuncIds (int dbevindex, Vector<Obj> *funcs) +{ + int len = funcs->size (); + Vector<uint64_t> *list = new Vector<uint64_t>(len); + for (int i = 0; i < len; i++) + list->store (i, dbeGetFuncId (dbevindex, funcs->fetch (i))); + return list; +} + +uint64_t +dbeGetFuncId (int dbevindex, Obj func) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (func == 0) + return 0; + uint64_t id = ((Histable *) func)->id; + return id; +} + +char * +dbeGetObjNameV2 (int dbevindex, uint64_t id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = dbeSession->findObjectById (id); + if (obj == NULL) + return NULL; + char *fname = obj->get_name (dbev->get_name_format ()); + return fname ? dbe_strdup (fname) : NULL; +} + +char * +dbeGetDataspaceTypeDesc (int /*dbevindex*/, Obj stack) +{ + if (stack == 0) + return NULL; + Histable *hist = CallStack::getStackPC ((void *) stack, 0); + DbeInstr *instr; + Histable::Type type = hist->get_type (); + if (type != Histable::INSTR) + return NULL; + else + instr = (DbeInstr *) hist; + char *descriptor = instr->get_descriptor (); + return descriptor ? dbe_strdup (descriptor) : NULL; +} + +Vector<void*> * +dbeGetDataDescriptorsV2 (int exp_id) +{ + Experiment *exp = dbeSession->get_exp (exp_id); + if (exp == NULL) + return NULL; + Vector<int> *dataId = new Vector<int>; + Vector<char*> *dataName = new Vector<char*>; + Vector<char*> *dataUName = new Vector<char*>; + Vector<int> *auxProp = new Vector<int>; + Vector<DataDescriptor*> *ddscr = exp->getDataDescriptors (); + for (int i = 0; i < ddscr->size (); i++) + { + DataDescriptor *dataDscr = ddscr->fetch (i); + if (dataDscr->getFlags () & DDFLAG_NOSHOW) + continue; + int data_id = dataDscr->getId (); + int aux_prop_id = (data_id == DATA_HWC) ? PROP_HWCTAG : PROP_NONE; + dataId->append (data_id); + dataName->append (strdup (dataDscr->getName ())); + dataUName->append (strdup (dataDscr->getUName ())); + auxProp->append (aux_prop_id); + } + delete ddscr; + Vector<void*> *res = new Vector<void*>(3); + res->store (0, dataId); + res->store (1, dataName); + res->store (2, dataUName); + res->store (3, auxProp); + return res; +} + +Vector<void*> * +dbeGetDataPropertiesV2 (int exp_id, int data_id) +{ + Experiment *exp = dbeSession->get_exp (exp_id); + if (exp == NULL) + return NULL; + DataDescriptor *dataDscr = exp->get_raw_events (data_id); + if (dataDscr == NULL) + return NULL; + Vector<PropDescr*> *props = dataDscr->getProps (); + Vector<int> *propId = new Vector<int>(props->size ()); + Vector<char*> *propUName = new Vector<char*>(props->size ()); + Vector<int> *propTypeId = new Vector<int>(props->size ()); + Vector<char*> *propTypeName = new Vector<char*>(props->size ()); + Vector<int> *propFlags = new Vector<int>(props->size ()); + Vector<char*> *propName = new Vector<char*>(props->size ()); + Vector<void*> *propStateNames = new Vector<void*>(props->size ()); + Vector<void*> *propStateUNames = new Vector<void*>(props->size ()); + + for (int i = 0; i < props->size (); i++) + { + PropDescr *prop = props->fetch (i); + char *pname = prop->name; + if (pname == NULL) + pname = NTXT (""); + char *uname = prop->uname; + if (uname == NULL) + uname = pname; + int vtypeNum = prop->vtype; + if (vtypeNum < 0 || vtypeNum >= TYPE_LAST) + vtypeNum = TYPE_NONE; + const char * vtypeNames[] = VTYPE_TYPE_NAMES; + const char *vtype = vtypeNames[prop->vtype]; + Vector<char*> *stateNames = NULL; + Vector<char*> *stateUNames = NULL; + int nStates = prop->getMaxState (); + if (nStates > 0) + { + stateNames = new Vector<char*>(nStates); + stateUNames = new Vector<char*>(nStates); + for (int kk = 0; kk < nStates; kk++) + { + const char * stateName = prop->getStateName (kk); + stateNames->store (kk, dbe_strdup (stateName)); + const char * Uname = prop->getStateUName (kk); + stateUNames->store (kk, dbe_strdup (Uname)); + } + } + propId->store (i, prop->propID); + propUName->store (i, dbe_strdup (uname)); + propTypeId->store (i, prop->vtype); + propTypeName->store (i, dbe_strdup (vtype)); + propFlags->store (i, prop->flags); + propName->store (i, dbe_strdup (pname)); + propStateNames->store (i, stateNames); + propStateUNames->store (i, stateUNames); + } + Vector<void*> *res = new Vector<void*>(7); + res->store (0, propId); + res->store (1, propUName); + res->store (2, propTypeId); + res->store (3, propTypeName); + res->store (4, propFlags); + res->store (5, propName); + res->store (6, propStateNames); + res->store (7, propStateUNames); + return res; +} + +Vector<void *> * +dbeGetExperimentTimeInfo (Vector<int> *exp_ids) +{ + int sz = exp_ids->size (); + Vector<long long> *offset_time = new Vector<long long> (sz); + Vector<long long> *start_time = new Vector<long long> (sz); + Vector<long long> *end_time = new Vector<long long> (sz); + Vector<long long> *start_wall_sec = new Vector<long long> (sz); + Vector<char* > *hostname = new Vector<char*> (sz); + Vector<int> *cpu_freq = new Vector<int> (sz); + for (int ii = 0; ii < sz; ii++) + { + int expIdx = exp_ids->fetch (ii); + { // update end_time by forcing fetch of experiment data + // workaround until dbeGetEndTime() is more robust + int id = (expIdx < 0) ? 0 : expIdx; + Experiment *exp = dbeSession->get_exp (id); + if (exp) + { + Vector<DataDescriptor*> *ddscr = exp->getDataDescriptors (); + delete ddscr; + } + } + offset_time->store (ii, dbeGetRelativeStartTime (0, expIdx)); + start_time->store (ii, dbeGetStartTime (0, expIdx)); + end_time->store (ii, dbeGetEndTime (0, expIdx)); + start_wall_sec->store (ii, dbeGetWallStartSec (0, expIdx)); + hostname->store (ii, dbeGetHostname (0, expIdx)); + cpu_freq->store (ii, dbeGetClock (0, expIdx)); + } + Vector<void*> *res = new Vector<void*>(4); + res->store (0, offset_time); + res->store (1, start_time); + res->store (2, end_time); + res->store (3, start_wall_sec); + res->store (4, hostname); + res->store (5, cpu_freq); + return res; +} + +Vector<void *> * +dbeGetExperimentDataDescriptors (Vector<int> *exp_ids) +{ + int sz = exp_ids->size (); + Vector<void*> *exp_dscr_info = new Vector<void*> (sz); + Vector<void*> *exp_dscr_props = new Vector<void*> (sz); + + for (int ii = 0; ii < sz; ii++) + { + int expIdx = exp_ids->fetch (ii); + Vector<void*> *ddscrInfo = dbeGetDataDescriptorsV2 (expIdx); + Vector<void*> *ddscrProps = new Vector<void*> (); // one entry per ddscrInfo + if (ddscrInfo) + { + Vector<int> *dataId = (Vector<int>*)ddscrInfo->fetch (0); + if (dataId) + { + // loop thru data descriptors + int ndata = dataId->size (); + for (int j = 0; j < ndata; ++j) + { + Vector<void*> *props = dbeGetDataPropertiesV2 (expIdx, dataId->fetch (j)); + ddscrProps->store (j, props); + } + } + } + exp_dscr_info->store (ii, ddscrInfo); + exp_dscr_props->store (ii, ddscrProps); + } + Vector<void*> *res = new Vector<void*>(2); + res->store (0, exp_dscr_info); + res->store (1, exp_dscr_props); + return res; +} + +static Vector<void *> * +dbeGetTLDataRepVals (VMode view_mode, hrtime_t start_ts, hrtime_t delta, + int numDeltas, DataView*packets, + Vector<long> *representativeEvents, bool showDuration); + +static bool +dbeHasTLData (int dbevindex, int exp_id, int data_id, int entity_prop_id, + int entity_prop_value, int aux) +{ + DataView *packets = + getTimelinePackets (dbevindex, exp_id, data_id, entity_prop_id); + if (!packets || packets->getSize () == 0) + return false; + long start_ind = getIdxByVals (packets, aux, entity_prop_value, + 0, DataView::REL_GTEQ); // time >= 0 + if (start_ind < 0) + return false; + + DbeView *dbev = dbeSession->getView (dbevindex); + VMode view_mode = dbev->get_view_mode (); + Experiment *exp = dbeSession->get_exp (exp_id); + if (!hasInvisbleTLEvents (exp, view_mode)) + return true; // all events are visible, no further checking required + long end_ind = getIdxByVals (packets, aux, entity_prop_value, + MAX_TIME, DataView::REL_LTEQ); + for (long ii = start_ind; ii <= end_ind; ii++) + { + if (!isVisibleTLEvent (exp, view_mode, packets, ii)) + continue; + return true; // first visible packet => has data + } + return false; +} + +Vector<bool> * +dbeHasTLData (int dbev_index, Vector<int> *exp_ids, Vector<int> *data_ids, + Vector<int> *entity_prop_ids, // LWP,CPU,THR, etc + Vector<int> *entity_prop_values, Vector<int> *auxs) +{ + DbeView *dbev = dbeSession->getView (dbev_index); + if (!dbev->isShowAll () && (dbev->isShowHideChanged () + || dbev->isNewViewMode ())) + { + // LIBRARY_VISIBILITY + dbev->resetAndConstructShowHideStacks (); + if (dbev->isNewViewMode ()) + dbev->resetNewViewMode (); + if (dbev->isShowHideChanged ()) + dbev->resetShowHideChanged (); + } + + int sz = exp_ids->size (); + Vector<bool> *hasVec = new Vector<bool>(sz); + for (int ii = 0; ii < sz; ii++) + { + bool hasData = dbeHasTLData (dbev_index, exp_ids->fetch (ii), + data_ids->fetch (ii), + entity_prop_ids->fetch (ii), + entity_prop_values->fetch (ii), + auxs->fetch (ii)); + hasVec->store (ii, hasData); + } + return hasVec; +} + +/* + * dbeGetTLData implements: + * FROM data_id + * DURATION >= delta AND ( start_ts <= TSTAMP < start_ts+num*delta OR + * start_ts <= TSTAMP-DURATION < start_ts+num*delta ) + * OR + * FAIR( DURATION < delta AND ( start_ts <= TSTAMP < start_ts+num*delta ) ) + * WHERE lfilter + */ + +Vector<void *> * +dbeGetTLData ( + int dbevindex, + int exp_id, + int data_id, // DATA_* + int entity_prop_id, // Show PROP_LWPID, PROP_CPUID, PROP_THRID, PROP_EXPID, or N/A + int entity_prop_value, // which LWPID, CPUID, THRID, EXPID for this request + int aux, + hrtime_t param_start_ts, + hrtime_t param_delta, + int param_numDeltas, + bool getRepresentatives, // fetch TL representatives + Vector<char *> *chartProps) // calculate sums for these property vals +{ + const hrtime_t start_ts = param_start_ts; + const hrtime_t delta = param_delta; + const int numDeltas = param_numDeltas; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Experiment *exp = dbeSession->get_exp (exp_id); + if (exp == NULL) + return NULL; + if (getRepresentatives == false && chartProps == NULL) + return NULL; + if (delta <= 0) + return NULL; + + hrtime_t tmp_ts = start_ts + delta * numDeltas; + if (tmp_ts < start_ts) + tmp_ts = MAX_TIME; + const hrtime_t end_ts = tmp_ts; + if (exp->get_status () == Experiment::INCOMPLETE && + exp->getLastEvent () < end_ts) + exp->update (); + DataView *packets = + getTimelinePackets (dbevindex, exp_id, data_id, entity_prop_id); + if (packets == NULL) + return NULL; // strange, no data view? + + VMode view_mode = dbev->get_view_mode (); // user, expert, machine //YXXX yuck + + // storage for calculating timeline representative events + Vector<long> *representativeEvents = NULL; + // list of representative events to be displayed on TL + Vector<int> *binRepIdx = NULL; + // for each bin, index of current "best" representativeEvent + Vector<void*> *representativeVals = NULL; + // TL representative packets' values + + // storage for calculating charts + Vector<int> *propIds = NULL; // [propIdx], which prop to measure + Vector<void*> *propVals = NULL; // [propIdx][bin], prop vals + Vector<int> *propNumStates = NULL; // [propIdx], how many states for prop? + Vector<bool> *propCumulativeChart = NULL; // [propIdx], data represents cumulative totals + Vector<long long> *propCumulativeRecentBinLastVal = NULL; // [propIdx], most recent value + Vector<long long> *propCumulativeRecentBinHighVal = NULL; // [propIdx], highest value for propCumulativeRecentBin + Vector<int> *propCumulativeRecentBin = NULL; // [propIdx], most recent bin + + // determine when to show duration of events + bool tmp_repsShowDuration = false; + bool tmp_statesUseDuration = false; + bool tmp_extendMicrostates = false; + const hrtime_t ptimerTickDuration = exp->get_params ()->ptimer_usec * 1000LL; // nanoseconds per tick + const bool hasDuration = packets->getProp (PROP_EVT_TIME) ? true : false; + if (hasDuration) + { + switch (entity_prop_id) + { + case PROP_CPUID: + tmp_repsShowDuration = false; + tmp_statesUseDuration = false; + break; + case PROP_THRID: + case PROP_LWPID: + tmp_repsShowDuration = true; + tmp_statesUseDuration = true; + tmp_extendMicrostates = (DATA_CLOCK == data_id) && (ptimerTickDuration < param_delta); + break; + case PROP_EXPID: + case PROP_NONE: // experiment summary row uses this + default: + if (DATA_SAMPLE == data_id) + { + tmp_repsShowDuration = true; + tmp_statesUseDuration = true; + } + else if (DATA_GCEVENT == data_id) + { + tmp_repsShowDuration = true; + tmp_statesUseDuration = true; + } + else if (DATA_CLOCK == data_id) + { + tmp_repsShowDuration = false; + tmp_statesUseDuration = true; + tmp_extendMicrostates = true; + } + else + { + tmp_repsShowDuration = false; + tmp_statesUseDuration = true; + } + break; + } + } + const bool repsShowDuration = tmp_repsShowDuration; // show stretched callstacks + const bool statesUseDuration = tmp_statesUseDuration; // use duration to calculate state charts + const bool extendMicrostates = tmp_extendMicrostates; // we show discrete profiling microstates with + // width=(tick-1), but for computing + // zoomed-out graphs we need to extend to + // account for all ticks, width=(ntick) + const bool reverseScan = repsShowDuration || extendMicrostates; // scan packets in reverse + + // determine range of packet indices (lo_pkt_idx, hi_pkt_idx) + long lo_pkt_idx, hi_pkt_idx; + if (extendMicrostates && !(entity_prop_id == PROP_THRID || entity_prop_id == PROP_LWPID)) + { + // merging data from multiple threads, need to scan all packets with timestamp [start_ts, exp end] + hrtime_t exp_end_time = exp->getLastEvent () + 1; + hi_pkt_idx = getIdxByVals (packets, aux, entity_prop_value, + exp_end_time, DataView::REL_LT); // last item + } + else + hi_pkt_idx = getIdxByVals (packets, aux, entity_prop_value, + end_ts, DataView::REL_LT); + if (repsShowDuration) + { + // There are two issues to deal with + // 1. events that end "off screen" to the right + // 2. overlapping events + + // 1. events that end "off screen" to the right + // For now, we only consistently handle the case where events don't overlap. + // Note that packet timestamps mark end of duration, not start. + // This means that the rightmost event won't be within hi_pkt_idx. + // Solution: Check if end+1 packet _started_ in-range + // Caveat: because we only look ahead by one packet, if there are + // overlapping duration events (e.g. EXPID aggregation)), zoom level + // and panning combo may cause events with TSTAMP>end_ts + // to appear/disappear. A complete solution would involve + // a solution to 2. + + // 2. overlapping events + // For now, we have a simplistic solution that makes "wide" events win. However, + // a future solution for deterministically dealing with overlap might look like this: + // - find all packets that touch the visible time range + // - possibly use two DataViews: one with TSTAMP_HI sort and one with TSTAMP_LO + // sort to allow efficient determination of packets with HI and LO endpoints in-range + // - create buckets to capture "winning" event for each bin (each pixel, that is) + // - sort the new list of packets by TSTAMP_HI (for example) + // - looping thru the packets that are in-range, update every bin it touches with it's id + // - if there is overlap, earlier packets will be kicked out of bins + // - On the GUI side, paint one event at a time, as normal. + // - However, for selections, recognize that duration of event may span many bins + // + long idx; + if (hi_pkt_idx >= 0) + // a packet was found to the left of the end time + idx = hi_pkt_idx + 1; // attempt to go one packet right + else + idx = getIdxByVals (packets, aux, entity_prop_value, + end_ts, DataView::REL_GTEQ); + if (isValidIdx (packets, entity_prop_id, aux, entity_prop_value, idx)) + { + int64_t pkt_ts = packets->getLongValue (PROP_TSTAMP, idx); + int64_t duration = packets->getLongValue (PROP_EVT_TIME, idx); + pkt_ts -= duration; + if (pkt_ts < end_ts) + hi_pkt_idx = idx; + } + } + lo_pkt_idx = getIdxByVals (packets, aux, entity_prop_value, + start_ts, DataView::REL_GTEQ); + + // allocate structs that return chart data + bool hasCumulativeCharts = false; + if (chartProps && chartProps->size () > 0) + { + int nprops = chartProps->size (); + // pre-allocate storage + propIds = new Vector<int> (nprops); + propVals = new Vector<void*>(nprops); + propNumStates = new Vector<int> (nprops); + propCumulativeChart = new Vector<bool>(nprops); + propCumulativeRecentBinLastVal = new Vector<long long>(nprops); + propCumulativeRecentBinHighVal = new Vector<long long>(nprops); + propCumulativeRecentBin = new Vector<int>(nprops); + for (int propNum = 0; propNum < nprops; propNum++) + { + const char* propStr = chartProps->fetch (propNum); + int items_per_prop = 0; + int prop_id = PROP_NONE; + if (!strcmp (propStr, "EVT_COUNT")) + items_per_prop = 1; // use PROP_NONE for counting packets + else + { + int lookup_prop_id = dbeSession->getPropIdByName (propStr); + PropDescr *propDscr = packets->getProp (lookup_prop_id); + if (propDscr != NULL) + { + switch (propDscr->vtype) + { + case TYPE_INT32: + case TYPE_UINT32: + case TYPE_INT64: + case TYPE_UINT64: + items_per_prop = propDscr->getMaxState () + 1; + // add extra slot to store values with out-of-range idx + prop_id = lookup_prop_id; + break; + case TYPE_DOUBLE: + break; // not implemented yet + case TYPE_STRING: + case TYPE_OBJ: + case TYPE_DATE: + default: + break; + } + } + } + void *vals; + if (!items_per_prop) + vals = NULL; + else if (items_per_prop == 1) + { + Vector<long long> *longVals = new Vector<long long> (); + longVals->store (numDeltas - 1, 0); // initialize all elements + vals = longVals; + } + else + { + Vector<Vector<long long>*> *stateVals = + new Vector<Vector<long long>*> (); + vals = stateVals; + // initialize only on-demand, some may not be needed + } + + bool isCumulativeChart; +#define YXXX_HEAP_VS_TIME 1 // YXXX add data meaning to properties? +#if YXXX_HEAP_VS_TIME + isCumulativeChart = (prop_id == PROP_HCUR_LEAKS || prop_id == PROP_HCUR_ALLOCS); +#endif + if (isCumulativeChart) + hasCumulativeCharts = true; + propIds->store (propNum, prop_id); + propVals->store (propNum, vals); + propNumStates->store (propNum, items_per_prop); + propCumulativeRecentBinLastVal->store (propNum, 0); + propCumulativeRecentBinHighVal->store (propNum, 0); + propCumulativeRecentBin->store (propNum, 0); + propCumulativeChart->store (propNum, isCumulativeChart); + } + } + + // Adjust idx range for calculating 'cumulative charts' e.g. heap size + if (hasCumulativeCharts) + { + // set initial values if earlier packet exists + long lo_idx; + if (lo_pkt_idx >= 0) + // packet was found to the right of start + lo_idx = lo_pkt_idx - 1; // attempt to go left by one event + else + // no packet was to the right of start, look left of start + lo_idx = getIdxByVals (packets, aux, entity_prop_value, + start_ts, DataView::REL_LT); + if (isValidIdx (packets, entity_prop_id, aux, entity_prop_value, lo_idx)) + { + // preceding packet found + // update initial values + int nprops = propCumulativeChart->size (); + for (int propNum = 0; propNum < nprops; propNum++) + { + if (!propCumulativeChart->fetch (propNum)) + continue; + int propId = propIds->fetch (propNum); + long long value = packets->getLongValue (propId, lo_idx); + propCumulativeRecentBinLastVal->store (propNum, value); + propCumulativeRecentBinHighVal->store (propNum, value); + } + // update indices used for iterating + lo_pkt_idx = lo_idx; + if (hi_pkt_idx < lo_pkt_idx) + hi_pkt_idx = lo_pkt_idx; + } + } + if (lo_pkt_idx < 0 || hi_pkt_idx < 0) + goto dbeGetTLData_done; // no data; return empty vectors, not null + + // representative events (subset of callstacks to represent on TL) + if (getRepresentatives) + { + representativeEvents = new Vector<long>(numDeltas); + // per-bin, longest event's index + binRepIdx = new Vector<int>(numDeltas); + for (int ii = 0; ii < numDeltas; ++ii) + binRepIdx->append (-1); + } + // While packets are sorted by _end_ timestamp (TSTAMP), + // after calculating start times for non-zero durations, + // start times are not guaranteed be monotonically increasing. + // For packets with duration, we'll scan them in reverse order to + // take advantage of the monotonically decreasing _end_ timestamps. + long start_idx, idx_inc; + if (!reverseScan) + { + start_idx = lo_pkt_idx; + idx_inc = 1; + } + else + { + start_idx = hi_pkt_idx; + idx_inc = -1; + } + for (long ii = start_idx; ii >= lo_pkt_idx && ii <= hi_pkt_idx; ii += idx_inc) + { + if (!isVisibleTLEvent (exp, view_mode, packets, ii) && !hasCumulativeCharts) + continue; + + // determine packet time duration and start bin + int tmp_start_bin; // packet start bin + int tmp_end_bin; // packet end bin (inclusive) + const hrtime_t pkt_end_ts = packets->getLongValue (PROP_TSTAMP, ii); + const hrtime_t pkt_dur = packets->getLongValue (PROP_EVT_TIME, ii); + const hrtime_t pkt_start_ts = pkt_end_ts - pkt_dur; + if (pkt_end_ts < start_ts && !hasCumulativeCharts) + continue; // weird, should not happen + if (pkt_start_ts >= end_ts) + continue; // could happen + hrtime_t bin_end_ts = pkt_end_ts; + if (bin_end_ts >= end_ts) + bin_end_ts = end_ts - 1; + tmp_end_bin = (int) ((bin_end_ts - start_ts) / delta); + hrtime_t bin_start_ts = pkt_start_ts; + if (bin_start_ts < start_ts) + bin_start_ts = start_ts; // event truncated to left. + tmp_start_bin = (int) ((bin_start_ts - start_ts) / delta); + // By definition + // (end_ts - start_ts) == delta * numDeltas + // and we know + // pkt_start < end_ts + // therefore + // (pkt_start - start_ts) < delta * numDeltas + // (pkt_start - start_ts) / delta < numDeltas + // bin < numDeltas + assert (tmp_end_bin < numDeltas); + assert (tmp_start_bin < numDeltas); + const bool is_offscreen = tmp_end_bin < 0 ? true : false; + if (tmp_end_bin < 0) + tmp_end_bin = 0; + const int pkt_end_bin = tmp_end_bin; // packet end bin (inclusive) + const int pkt_start_bin = tmp_start_bin; + if (getRepresentatives && !is_offscreen) + { // find best representative + // Note: for events with duration, we're scanning packets in order + // of decreasing end-timestamp. This means that the first packet + // that hits a particular _start_ bin will have the longest duration + // of any later packet that might hit that start bin. The + // the first packet will be the best (longest) packet. + const int bin = reverseScan ? pkt_start_bin : pkt_end_bin; + int eventIdx = binRepIdx->fetch (bin); + if (eventIdx == -1) + { + eventIdx = representativeEvents->size (); // append to end + representativeEvents->append (ii); + binRepIdx->store (bin, eventIdx); + } + } + if (propIds) + { // per-bin chart: sum across filtered packets + for (int propNum = 0; propNum < propIds->size (); propNum++) + { + void *thisProp = propVals->fetch (propNum); + if (thisProp == NULL) + continue; // no valid data + if (is_offscreen && !propCumulativeChart->fetch (propNum)) + continue; // offscreen events are only processed for cumulative charts + int propId = propIds->fetch (propNum); + long long val; + if (propId == PROP_NONE) + val = 1; // count + else + val = packets->getLongValue (propId, ii); + long nitems = propNumStates->fetch (propNum); + if (nitems < 1) + continue; + else if (nitems == 1) + { + // chart is not based on not multiple states + Vector<long long>* thisPropVals = + (Vector<long long>*)thisProp; + if (thisPropVals->size () == 0) + thisPropVals->store (numDeltas - 1, 0); + const int bin = statesUseDuration ? pkt_start_bin : pkt_end_bin; + if (!propCumulativeChart->fetch (propNum)) + { + val += thisPropVals->fetch (bin); + thisPropVals->store (bin, val); + } + else + { + // propCumulativeChart + long long high_value = propCumulativeRecentBinHighVal->fetch (propNum); + int last_bin = propCumulativeRecentBin->fetch (propNum); + if (last_bin < bin) + { + // backfill from previous event + // last_bin: store largest value (in case of multiple events) + thisPropVals->store (last_bin, high_value); + // propagate forward the bin's last value + long long last_value = propCumulativeRecentBinLastVal->fetch (propNum); + for (int kk = last_bin + 1; kk < bin; kk++) + thisPropVals->store (kk, last_value); + // prepare new bin for current event + high_value = 0; // high value of next bin is 0. + propCumulativeRecentBinHighVal->store (propNum, high_value); + propCumulativeRecentBin->store (propNum, bin); + } + long long this_value = packets->getLongValue (propId, ii); + propCumulativeRecentBinLastVal->store (propNum, this_value); + if (high_value < this_value) + { + // record the max + high_value = this_value; + propCumulativeRecentBinHighVal->store (propNum, high_value); + } + if (ii == hi_pkt_idx) + { + // bin: show largest value (in case of multiple events + thisPropVals->store (bin, high_value); + //forward fill remaining bins + for (int kk = bin + 1; kk < numDeltas; kk++) + thisPropVals->store (kk, this_value); + } + } + } + else + { + // means val is actually a state # + Vector<Vector<long long>*>* thisPropStateVals = + (Vector<Vector<long long>*>*)thisProp; + if (thisPropStateVals->size () == 0) + thisPropStateVals->store (numDeltas - 1, 0); + long stateNum; + if (val >= 0 && val < nitems) + stateNum = (long) val; + else + stateNum = nitems - 1; // out of range, use last slot + hrtime_t graph_pkt_dur = pkt_dur; + hrtime_t graph_pkt_start_ts = pkt_start_ts; + int tmp2_start_bin = pkt_start_bin; + if (propId == PROP_MSTATE) + { + if (statesUseDuration && extendMicrostates) + { + // microstate stacks are shown and filtered with width=NTICK-1 + // but for microstate graph calcs use width=NTICK. + graph_pkt_dur += ptimerTickDuration; + graph_pkt_start_ts -= ptimerTickDuration; + hrtime_t bin_start_ts = graph_pkt_start_ts; + if (bin_start_ts < start_ts) + bin_start_ts = start_ts; // event truncated to left. + tmp2_start_bin = (int) ((bin_start_ts - start_ts) / delta); + } + } + const int graph_pkt_start_bin = statesUseDuration ? tmp2_start_bin : pkt_end_bin; + + // We will distribute the state's presence evenly over duration of the event. + // When only a 'partial bin' is touched by an event, adjust accordingly. + long long value_per_bin; // weight to be applied to each bin + { + long long weight; + if (propId == PROP_MSTATE) // ticks to nanoseconds + weight = packets->getLongValue (PROP_NTICK, ii) * ptimerTickDuration; + else if (graph_pkt_dur) + weight = graph_pkt_dur; // nanoseconds + else + weight = 1; // no duration; indicate presence + if (graph_pkt_start_bin != pkt_end_bin) + { + // spans multiple bins + double nbins = (double) graph_pkt_dur / delta; + value_per_bin = weight / nbins; + } + else + value_per_bin = weight; + } + for (int evtbin = graph_pkt_start_bin; evtbin <= pkt_end_bin; evtbin++) + { + Vector<long long>* stateValues = + (Vector<long long>*) thisPropStateVals->fetch (evtbin); + if (stateValues == NULL) + { + // on-demand storage + stateValues = new Vector<long long>(nitems); + stateValues->store (nitems - 1, 0); // force memset of full vector + thisPropStateVals->store (evtbin, stateValues); + } + long long new_val = stateValues->fetch (stateNum); + if (graph_pkt_start_bin == pkt_end_bin || + (evtbin > graph_pkt_start_bin && evtbin < pkt_end_bin)) + { + new_val += value_per_bin; + } + else + { + // partial bin + const hrtime_t bin_start = start_ts + evtbin * delta; + const hrtime_t bin_end = start_ts + (evtbin + 1) * delta - 1; + if (evtbin == graph_pkt_start_bin) + { + // leftmost bin + if (graph_pkt_start_ts < bin_start) + new_val += value_per_bin; + else + { + double percent = (double) (bin_end - graph_pkt_start_ts) / delta; + new_val += value_per_bin*percent; + } + } + else + { + // rightmost bin + if (pkt_end_ts > bin_end) + new_val += value_per_bin; + else + { + double percent = (double) (pkt_end_ts - bin_start) / delta; + new_val += value_per_bin*percent; + } + } + } + stateValues->store (stateNum, new_val); + } + } + } + } + } + delete binRepIdx; + delete propIds; + delete propCumulativeChart; + delete propCumulativeRecentBinLastVal; + delete propCumulativeRecentBinHighVal; + delete propCumulativeRecentBin; + if (representativeEvents != NULL && reverseScan) + { + if (repsShowDuration) + { + //YXXX for now prune here, but in the future, let gui decide what to show + // Prune events that are completely obscured long duration events. + // Note: representativeEvents is sorted by decreasing _end_ timestamps. + Vector<long> *prunedEvents = new Vector<long>(numDeltas); + hrtime_t prev_start_ts = MAX_TIME; + long repCnt = representativeEvents->size (); + for (long kk = 0; kk < repCnt; kk++) + { + long ii = representativeEvents->fetch (kk); + hrtime_t tmp_end_ts = packets->getLongValue (PROP_TSTAMP, ii); + hrtime_t tmp_dur = packets->getLongValue (PROP_EVT_TIME, ii); + hrtime_t tmp_start_ts = tmp_end_ts - tmp_dur; + if (tmp_start_ts >= prev_start_ts) + // this event would be completely hidden + // (because of sorting, we know tmp_end_ts <= prev_end_ts) + continue; + prev_start_ts = tmp_start_ts; + prunedEvents->append (ii); + } + // invert order to to get increasing _end_ timestamps + representativeEvents->reset (); + for (long kk = prunedEvents->size () - 1; kk >= 0; kk--) + { + long packet_idx = prunedEvents->fetch (kk); + representativeEvents->append (packet_idx); + } + delete prunedEvents; + } + else + { // !repsShowDuration + // Note: representativeEvents is sorted by decreasing _end_ timestamps. + // Reverse the order: + long hi_idx = representativeEvents->size () - 1; + long lo_idx = 0; + while (hi_idx > lo_idx) + { + // swap + long lo = representativeEvents->fetch (lo_idx); + long hi = representativeEvents->fetch (hi_idx); + representativeEvents->store (lo_idx, hi); + representativeEvents->store (hi_idx, lo); + hi_idx--; + lo_idx++; + } + } + } + +dbeGetTLData_done: + if (getRepresentatives) + { + representativeVals = dbeGetTLDataRepVals (view_mode, start_ts, delta, + numDeltas, packets, representativeEvents, repsShowDuration); + delete representativeEvents; + } + Vector<void*> *results = new Vector<void*> (2); + results->store (0, representativeVals); + results->store (1, propVals); + return results; +} + +// add representative events to return buffer + +static Vector<void *> * +dbeGetTLDataRepVals (VMode view_mode, hrtime_t start_ts, hrtime_t delta, + int numDeltas, DataView*packets, + Vector<long> *representativeEvents, bool showDuration) +{ + int numrecs = representativeEvents ? representativeEvents->size () : 0; + // allocate storage for results + Vector<int> *startBins = new Vector<int>(numrecs); + Vector<int> *numBins = new Vector<int>(numrecs); + Vector<Obj> *eventIdxs = new Vector<Obj>(numrecs); + Vector<Obj> *stackIds = NULL; + if (packets->getProp (PROP_FRINFO)) + stackIds = new Vector<Obj>(numrecs); + Vector<int> *mstates = NULL; + if (packets->getProp (PROP_MSTATE)) + mstates = new Vector<int>(numrecs); + Vector<Vector<long long>*> *sampleVals = NULL; + if (packets->getProp (PROP_SMPLOBJ)) + sampleVals = new Vector<Vector<long long>*>(numrecs); + Vector<long long> *timeStart = new Vector<long long>(numrecs); + Vector<long long> *timeEnd = new Vector<long long>(numrecs); + int prevEndBin = -1; // make sure we don't overlap bins + for (int eventIdx = 0; eventIdx < numrecs; eventIdx++) + { + long packetIdx = representativeEvents->fetch (eventIdx); + // long eventId = packets->getIdByIdx( packetIdx ); + const hrtime_t pkt_tstamp = packets->getLongValue (PROP_TSTAMP, packetIdx); + const hrtime_t pkt_dur = showDuration ? packets->getLongValue (PROP_EVT_TIME, packetIdx) : 0; + timeStart->store (eventIdx, pkt_tstamp - pkt_dur); + timeEnd->store (eventIdx, pkt_tstamp); + + // calc startBin + int startBin = (int) ((pkt_tstamp - pkt_dur - start_ts) / delta); + if (startBin <= prevEndBin) + startBin = prevEndBin + 1; + // calc binCnt + int endBin = (int) ((pkt_tstamp - start_ts) / delta); + if (endBin >= numDeltas) + endBin = numDeltas - 1; + int binCnt = endBin - startBin + 1; + prevEndBin = endBin; + startBins->store (eventIdx, startBin); + numBins->store (eventIdx, binCnt); + eventIdxs->store (eventIdx, packetIdx); // store packet's idx + if (stackIds != NULL) + { + void* stackId = getStack (view_mode, packets, packetIdx); + stackIds->store (eventIdx, (Obj) (unsigned long) stackId); + } + if (mstates != NULL) + { + int mstate = packets->getIntValue (PROP_MSTATE, packetIdx); + mstates->store (eventIdx, mstate); + } + if (sampleVals != NULL) + { + Sample* sample = (Sample*) packets->getObjValue (PROP_SMPLOBJ, packetIdx); + if (!sample || !sample->get_usage ()) + sample = sample; + else + { + PrUsage* prusage = sample->get_usage (); + Vector<long long> *mstateVals = prusage->getMstateValues (); + sampleVals->store (eventIdx, mstateVals); + } + } + } + // caller responsible for: delete representativeEvents; + Vector<void*> *results = new Vector<void*> (8); + results->store (0, startBins); + results->store (1, numBins); + results->store (2, eventIdxs); + results->store (3, stackIds); + results->store (4, mstates); + results->store (5, sampleVals); + results->store (6, timeStart); + results->store (7, timeEnd); + return results; +} + +// starting from <event_id> packet idx, step <move_count> visible events +// return the resulting idx and that packet's center time, or null if no event. +Vector<long long> * +dbeGetTLEventCenterTime (int dbevindex, int exp_id, int data_id, + int entity_prop_id, int entity_prop_val, int aux, + long long event_id, long long move_count) +{ + DataView *packets = getTimelinePackets (dbevindex, exp_id, data_id, + entity_prop_id); + if (packets == NULL) + return NULL; + long idx = (long) event_id; + + DbeView *dbev = dbeSession->getView (dbevindex); + VMode view_mode = dbev->get_view_mode (); + Experiment *exp = dbeSession->get_exp (exp_id); + int direction; + if (move_count == 0) + direction = 0; + else if (move_count < 0) + { + move_count = -move_count; + direction = -1; + } + else + direction = 1; + idx = getTLVisibleIdxByStepping (exp, view_mode, entity_prop_id, packets, aux, + entity_prop_val, idx, move_count, direction); + if (idx >= 0) + { + long long ts = packets->getLongValue (PROP_TSTAMP, idx); + long long dur = packets->getLongValue (PROP_EVT_TIME, idx); + long long center = ts - dur / 2; + Vector<long long> *results = new Vector<long long> (2); + results->store (0, idx); // result idx + results->store (1, center); // result timestamp + return results; + } + return NULL; +} + +long long +dbeGetTLEventIdxNearTime (int dbevindex, int exp_id, int data_id, + int entity_prop_id, int entity_prop_val, int aux, + int searchDirection, long long tstamp) +{ + DataView *packets = getTimelinePackets (dbevindex, exp_id, data_id, + entity_prop_id); + if (packets == NULL) + return -1; + DbeView *dbev = dbeSession->getView (dbevindex); + VMode view_mode = dbev->get_view_mode (); + Experiment *exp = dbeSession->get_exp (exp_id); + if (searchDirection < 0) + { + int idx = getTLVisibleIdxByVals (exp, view_mode, entity_prop_id, + packets, aux, entity_prop_val, tstamp, + DataView::REL_LTEQ); + if (idx != -1) + return idx; + searchDirection = 1; // couldn't find to left, try to right + } + if (searchDirection > 0) + { + int idx = getTLVisibleIdxByVals (exp, view_mode, entity_prop_id, + packets, aux, entity_prop_val, tstamp, + DataView::REL_GTEQ); + if (idx != -1) + return idx; + // couldn't find to right, fall through to generic + } + // search left and right of timestamp + long idx1, idx2; + idx1 = getTLVisibleIdxByVals (exp, view_mode, entity_prop_id, + packets, aux, entity_prop_val, tstamp, + DataView::REL_LT); + idx2 = getTLVisibleIdxByVals (exp, view_mode, entity_prop_id, + packets, aux, entity_prop_val, tstamp, + DataView::REL_GTEQ); + if (idx1 == -1) + return idx2; + else if (idx2 == -1) + return idx1; + + // both valid, so need to compare to see which is closer + long long t1 = packets->getLongValue (PROP_TSTAMP, idx1); + long long t2 = packets->getLongValue (PROP_TSTAMP, idx2); + long long t2dur = packets->getLongValue (PROP_EVT_TIME, idx2); + long long delta1 = tstamp - t1; // should always be positive + long long delta2 = (t2 - t2dur) - tstamp; // if negative, overlaps idx1 + if (delta1 > delta2) + return idx2; + else + return idx1; +} + +enum Aggr_type +{ + AGGR_NONE, + AGGR_FAIR, + AGGR_MAX, + AGGR_MIN, + AGGR_CNT, + AGGR_SUM, + AGGR_AVG +}; + +static Aggr_type +getAggrFunc (char *aname) +{ + Aggr_type agrfn = AGGR_NONE; + if (aname == NULL) + return agrfn; + if (strcmp (aname, NTXT ("FAIR")) == 0) + agrfn = AGGR_FAIR; + else if (strcmp (aname, NTXT ("MAX")) == 0) + agrfn = AGGR_MAX; + else if (strcmp (aname, NTXT ("MIN")) == 0) + agrfn = AGGR_MIN; + else if (strcmp (aname, NTXT ("CNT")) == 0) + agrfn = AGGR_CNT; + else if (strcmp (aname, NTXT ("SUM")) == 0) + agrfn = AGGR_SUM; + else if (strcmp (aname, NTXT ("AVG")) == 0) + agrfn = AGGR_AVG; + return agrfn; +} + +static long long +computeAggrVal (DefaultMap<long long, long long> *fval_map, Aggr_type agrfn) +{ + long long aval = 0; + long cnt = 0; + Vector<long long> *fvals = fval_map->values (); + long nvals = fvals->size (); + for (int i = 0; i < nvals; ++i) + { + long long val = fvals->fetch (i); + switch (agrfn) + { + case AGGR_FAIR: + aval = val; + break; + case AGGR_MAX: + if (aval < val || cnt == 0) + aval = val; + break; + case AGGR_MIN: + if (aval > val || cnt == 0) + aval = val; + break; + case AGGR_CNT: + aval = cnt + 1; + break; + case AGGR_SUM: + case AGGR_AVG: + aval += val; + break; + case AGGR_NONE: + break; + } + if (agrfn == AGGR_FAIR) + break; + cnt += 1; + } + + // Finalize aggregation + if (agrfn == AGGR_AVG) + if (cnt > 0) + aval = (aval + cnt / 2) / cnt; + delete fvals; + return aval; +} + +Vector<long long> * +dbeGetAggregatedValue (int data_id, // data table id + char *lfilter, // local filter + char *fexpr, // function expression + char *pname_ts, // property name for timestamp + hrtime_t start_ts, // start of the first time interval + hrtime_t delta, // time interval length + int num, // number of time intervals + char *pname_key, // property name for aggregation key + char *aggr_func) // aggregation function +{ + Vector<long long> *res = new Vector<long long>; + Experiment *exp = dbeSession->get_exp (0); + if (exp == NULL) + return res; + hrtime_t end_ts = start_ts + delta * num; + if (end_ts < start_ts) // check overflow + end_ts = MAX_TIME; + + if (exp->get_status () == Experiment::INCOMPLETE + && exp->getLastEvent () < end_ts) + exp->update (); + + DataDescriptor *dataDscr = exp->get_raw_events (data_id); + if (dataDscr == NULL) + return res; + + // Process timestamp argument + int prop_ts = dbeSession->getPropIdByName (pname_ts); + if (prop_ts == PROP_NONE) + return res; + assert (prop_ts == -1); + + // Parse all expressions + Expression *flt_expr = NULL; + if (lfilter != NULL) + flt_expr = dbeSession->ql_parse (lfilter); + Expression *func_expr = NULL; + if (fexpr != NULL) + func_expr = dbeSession->ql_parse (fexpr); + if (func_expr == NULL) // Not specified or malformed + return res; + + // Process aggregation key argument + int prop_key = PROP_NONE; + Data *data_key = NULL; + if (pname_key != NULL) + { + prop_key = dbeSession->getPropIdByName (pname_key); + data_key = dataDscr->getData (prop_key); + if (data_key == NULL) // Specified but not found + return res; + } + + // Process aggregation function argument + Aggr_type agrfn = AGGR_FAIR; + if (aggr_func != NULL) + { + agrfn = getAggrFunc (aggr_func); + if (agrfn == AGGR_NONE) // Specified but not recognized + return res; + } + DefaultMap<long long, long long> * + fval_map = new DefaultMap<long long, long long>; // key_val -> func_val + Vector<long long> *key_set = NULL; + assert (key_set != NULL); + if (key_set == NULL) + { + key_set = new Vector<long long>; + key_set->append (0L); + } + DefaultMap<long long, int> *key_seen = new DefaultMap<long long, int>; + long idx_prev = -1; + for (int tidx = 0; tidx < num; ++tidx) + { + long idx_cur = -1; + assert (idx_cur != -1); + int left = key_set->size (); + key_seen->clear (); + for (long idx = idx_cur; idx > idx_prev; --idx) + { + long id = 0; + assert (id != 0); + + // Pre-create expression context + Expression::Context ctx (dbeSession->getView (0), exp, NULL, id); + // First use the filter + if (flt_expr != NULL) + if (flt_expr->eval (&ctx) == 0) + continue; + + // Calculate the key + // keys are limited to integral values + long long key = 0; + if (data_key != NULL) + key = data_key->fetchLong (id); + + // Check if already seen + if (key_seen->get (key) == 1) + continue; + key_seen->put (key, 1); + left -= 1; + + // Calculate function value + // function values are limited to integral values + long long fval = func_expr->eval (&ctx); + fval_map->put (key, fval); + if (left == 0) + break; + } + idx_prev = idx_cur; + long long aval = computeAggrVal (fval_map, agrfn); + res->store (tidx, aval); + } + delete key_seen; + delete fval_map; + delete flt_expr; + delete func_expr; + return res; +} + +Vector<char*> * +dbeGetLineInfo (Obj pc) +{ + DbeInstr *instr = (DbeInstr*) pc; + if (instr == NULL || instr->get_type () != Histable::INSTR) + return NULL; + DbeLine *dbeline = (DbeLine*) instr->convertto (Histable::LINE); + const char *fname = dbeline ? dbeline->sourceFile->get_name () : NTXT (""); + char lineno[16]; + *lineno = '\0'; + if (dbeline != NULL) + snprintf (lineno, sizeof (lineno), NTXT ("%d"), dbeline->lineno); + Vector<char*> *res = new Vector<char*>(2); + res->store (0, strdup (fname)); + res->store (1, strdup (lineno)); + return res; +} + +int +dbeSetAlias (char *name, char *uname, char *expr) +{ + char *res = dbeSession->indxobj_define (name, uname, expr, NULL, NULL); + return res == NULL ? 0 : 1; +} + +Vector<char*> * +dbeGetAlias (char *name) +{ + Vector<char*> *res = new Vector<char*>; + int idx = dbeSession->findIndexSpaceByName (name); + if (idx >= 0) + { + char *str = dbeSession->getIndexSpaceDescr (idx); + res->append (dbe_strdup (str)); + str = dbeSession->getIndexSpaceExprStr (idx); + res->append (dbe_strdup (str)); + } + return res; +} + +static int +key_cmp (const void *p1, const void *p2) +{ + long long ll1 = *(long long*) p1; + long long ll2 = *(long long*) p2; + return ll1 < ll2 ? -1 : ll1 > ll2 ? 1 : 0; +} + +Vector<Vector<long long>*> * +dbeGetXYPlotData ( + int data_id, // data table id + char *lfilter, // local filter expression + char *arg, // name for the argument + char *func1, // expression for the first axis (x) + char *aggr1, // aggregation function for func1: "SUM","CNT",... + char *func2, // expression for the second axis (y) + char *aggr2, // aggregation function for func2 + char *func3, // expression for the third axis (color) + char *aggr3) // aggregation function for func3 +{ + Vector<Vector<long long>*> *res = new Vector<Vector<long long>*>; + Experiment *exp = dbeSession->get_exp (0); + if (exp == NULL) + return res; + if (exp->get_status () == Experiment::INCOMPLETE) + exp->update (); + + DataDescriptor *dataDscr = exp->get_raw_events (data_id); + if (dataDscr == NULL) + return res; + + // Parse all expressions + Vector<Expression*> *funcs = new Vector<Expression*>; + Vector<Aggr_type> *aggrs = new Vector<Aggr_type>; + Vector<DefaultMap<long long, long long>*> *fval_maps = + new Vector<DefaultMap<long long, long long>*>; + Vector<DefaultMap<long long, long>*> *cnt_maps = + new Vector<DefaultMap<long long, long>*>; + if (func1 != NULL) + { + Expression *expr = dbeSession->ql_parse (func1); + funcs->append (expr); + aggrs->append (getAggrFunc (aggr1)); + fval_maps->append (new DefaultMap<long long, long long>); + cnt_maps->append (new DefaultMap<long long, long>); + res->append (new Vector<long long>); + if (func2 != NULL) + { + expr = dbeSession->ql_parse (func2); + funcs->append (expr); + aggrs->append (getAggrFunc (aggr2)); + fval_maps->append (new DefaultMap<long long, long long>); + cnt_maps->append (new DefaultMap<long long, long>); + res->append (new Vector<long long>); + if (func3 != NULL) + { + expr = dbeSession->ql_parse (func3); + funcs->append (expr); + aggrs->append (getAggrFunc (aggr3)); + fval_maps->append (new DefaultMap<long long, long long>); + cnt_maps->append (new DefaultMap<long long, long>); + res->append (new Vector<long long>); + } + } + } + if (funcs->size () == 0) + { + funcs->destroy (); + delete funcs; + fval_maps->destroy (); + delete fval_maps; + cnt_maps->destroy (); + delete cnt_maps; + delete aggrs; + return res; + } + Expression *arg_expr = NULL; + if (arg != NULL) + arg_expr = dbeSession->ql_parse (arg); + if (arg_expr == NULL) + { + funcs->destroy (); + delete funcs; + fval_maps->destroy (); + delete fval_maps; + cnt_maps->destroy (); + delete cnt_maps; + delete aggrs; + return res; + } + Expression *flt_expr = NULL; + if (lfilter != NULL) + flt_expr = dbeSession->ql_parse (lfilter); + Vector<long long> *kidx_map = new Vector<long long>(); // key_idx -> key_val + for (long i = 0; i < dataDscr->getSize (); i++) + { + Expression::Context ctx (dbeSession->getView (0), exp, NULL, i); + // First use the filter + if (flt_expr != NULL) + if (flt_expr->eval (&ctx) == 0) + continue; + + // Compute the argument + long long key = arg_expr->eval (&ctx); + if (kidx_map->find (key) == -1) + kidx_map->append (key); + for (long j = 0; j < funcs->size (); ++j) + { + Expression *func = funcs->fetch (j); + Aggr_type aggr = aggrs->fetch (j); + DefaultMap<long long, long long> *fval_map = fval_maps->fetch (j); + DefaultMap<long long, long> *cnt_map = cnt_maps->fetch (j); + long long fval = func->eval (&ctx); + long long aval = fval_map->get (key); + long cnt = cnt_map->get (key); + switch (aggr) + { + case AGGR_NONE: + case AGGR_FAIR: + if (cnt == 0) + aval = fval; + break; + case AGGR_MAX: + if (aval < fval || cnt == 0) + aval = fval; + break; + case AGGR_MIN: + if (aval > fval || cnt == 0) + aval = fval; + break; + case AGGR_CNT: + aval = cnt + 1; + break; + case AGGR_SUM: + case AGGR_AVG: + aval += fval; + break; + } + cnt_map->put (key, cnt + 1); + fval_map->put (key, aval); + } + } + kidx_map->sort (key_cmp); + + // Finalize aggregation, prepare result + for (long j = 0; j < funcs->size (); ++j) + { + Aggr_type aggr = aggrs->fetch (j); + Vector<long long> *resj = res->fetch (j); + DefaultMap<long long, long long> * + fval_map = fval_maps->fetch (j); + DefaultMap<long long, long> * + cnt_map = cnt_maps->fetch (j); + for (int kidx = 0; kidx < kidx_map->size (); ++kidx) + { + long long key = kidx_map->fetch (kidx); + long long aval = fval_map->get (key); + if (aggr == AGGR_AVG) + { + long cnt = cnt_map->get (key); + if (cnt > 0) + aval = (aval + cnt / 2) / cnt; + } + resj->append (aval); + } + } + delete flt_expr; + funcs->destroy (); + delete funcs; + delete aggrs; + delete arg_expr; + delete kidx_map; + fval_maps->destroy (); + delete fval_maps; + cnt_maps->destroy (); + delete cnt_maps; + return res; +} + +/* ********************************************************************* */ +/* Routines for use by Collector GUI */ +/** + * Returns signal value for provided name. Example of name: "SIGUSR1" + * @param signal + * @return value + */ +int +dbeGetSignalValue (char *signal) +{ + int ret = -1; + if (signal == NULL) + return ret; + if (strcmp (signal, "SIGUSR1") == 0) + return (SIGUSR1); + if (strcmp (signal, "SIGUSR2") == 0) + return (SIGUSR2); + if (strcmp (signal, "SIGPROF") == 0) + return (SIGPROF); + return ret; +} + +char * +dbeSendSignal (pid_t p, int signum) +{ + int ret = kill (p, signum); + if (p == 0 || p == -1) + return (dbe_sprintf (GTXT ("kill of process %d not supported\n"), p)); + if (ret == 0) + return NULL; + char *msg = dbe_sprintf (GTXT ("kill(%d, %d) failed: %s\n"), p, signum, + strerror (errno)); + return msg; +} + +char * +dbeGetCollectorControlValue (char *control) +{ + if (control == NULL) + return NULL; + if (col_ctr == NULL) + col_ctr = new Coll_Ctrl (1); + char *msg = col_ctr->get (control); + return msg; +} + +char * +dbeSetCollectorControlValue (char *control, char * value) +{ + if (control == NULL) + return NULL; + if (col_ctr == NULL) + col_ctr = new Coll_Ctrl (1); + char *msg = col_ctr->set (control, value); + return msg; +} + +char * +dbeUnsetCollectorControlValue (char *control) +{ + if (control == NULL) + return NULL; + if (col_ctr == NULL) + col_ctr = new Coll_Ctrl (1); + char *msg = col_ctr->unset (control); + return msg; +} + +void +dbeSetLocation (const char *fname, const char *location) +{ + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i = 0, sz = sources ? sources->size () : 0; i < sz; i++) + { + SourceFile *src = sources->get (i); + DbeFile *df = src->dbeFile; + if (df && (strcmp (fname, df->get_name ()) == 0)) + { + df->find_file ((char *) location); + break; + } + } +} + +void +dbeSetLocations (Vector<const char *> *fnames, Vector<const char *> *locations) +{ + if (fnames == NULL || locations == NULL + || fnames->size () != locations->size ()) + return; + for (long i = 0, sz = fnames->size (); i < sz; i++) + dbeSetLocation (fnames->get (i), locations->get (i)); +} + +Vector<void*> * +dbeResolvedWith_setpath (const char *path) +{ + Vector<char*> *names = new Vector<char*>(); + Vector<char*> *pathes = new Vector<char*>(); + Vector<long long> *ids = new Vector<long long>(); + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i = 0, sz = sources ? sources->size () : 0; i < sz; i++) + { + SourceFile *src = sources->get (i); + DbeFile *df = src->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_FICTION) != 0) + continue; + char *fnm = df->get_name (); + if ((df->filetype & (DbeFile::F_JAVACLASS | DbeFile::F_JAVA_SOURCE)) != 0) + { + char *jnm = dbe_sprintf (NTXT ("%s/%s"), path, fnm); + if (df->check_access (jnm) == DbeFile::F_FILE) + { + names->append (dbe_strdup (fnm)); + pathes->append (jnm); + ids->append (src->id); + continue; + } + free (jnm); + } + char *nm = dbe_sprintf (NTXT ("%s/%s"), path, get_basename (fnm)); + if (df->check_access (nm) == DbeFile::F_FILE) + { + names->append (dbe_strdup (fnm)); + pathes->append (nm); + ids->append (src->id); + continue; + } + free (nm); + } + if (names->size () != 0) + { + Vector<void*> *data = new Vector<void*>(3); + data->append (names); + data->append (pathes); + data->append (ids); + return data; + } + return NULL; +} + +Vector<void*> * +dbeResolvedWith_pathmap (const char *old_prefix, const char *new_prefix) +{ + size_t len = strlen (old_prefix); + Vector<char*> *names = new Vector<char*>(); + Vector<char*> *pathes = new Vector<char*>(); + Vector<long long> *ids = new Vector<long long>(); + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i = 0, sz = sources ? sources->size () : 0; i < sz; i++) + { + SourceFile *src = sources->get (i); + DbeFile *df = src->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_FICTION) != 0) + continue; + char *fnm = df->get_name (); + if (strncmp (old_prefix, fnm, len) == 0 + && (fnm[len] == '/' || fnm[len] == '\0')) + { + char *nm = dbe_sprintf (NTXT ("%s/%s"), new_prefix, fnm + len); + if (df->check_access (nm) == DbeFile::F_FILE) + { + names->append (dbe_strdup (fnm)); + pathes->append (nm); + ids->append (src->id); + continue; + } + if ((df->filetype & DbeFile::F_JAVA_SOURCE) != 0) + { + free (nm); + nm = dbe_sprintf (NTXT ("%s/%s"), new_prefix, fnm); + if (df->check_access (nm) == DbeFile::F_FILE) + { + names->append (dbe_strdup (fnm)); + pathes->append (nm); + ids->append (src->id); + continue; + } + } + free (nm); + } + } + if (names->size () != 0) + { + Vector<void*> *data = new Vector<void*>(3); + data->append (names); + data->append (pathes); + data->append (ids); + return data; + } + return NULL; +} + +void +dbe_archive (Vector<long long> *ids, Vector<const char *> *locations) +{ + if (ids == NULL || locations == NULL || ids->size () != locations->size ()) + return; + Experiment *exp = dbeSession->get_exp (0); + if (exp == NULL) + return; + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i1 = 0, sz1 = ids->size (); i1 < sz1; i1++) + { + long long id = ids->get (i1); + for (long i = 0, sz = sources ? sources->size () : 0; i < sz; i++) + { + SourceFile *src = sources->get (i); + if (src->id == id) + { + DbeFile *df = src->dbeFile; + if (df) + { + char *fnm = df->find_file ((char *) locations->get (i1)); + if (fnm) + { + char *nm = df->get_name (); + char *anm = exp->getNameInArchive (nm, false); + exp->copy_file (fnm, anm, true); + free (anm); + } + } + } + } + } +} + +/* ************************************************************************ */ + +/* Routines to check connection between Remote Analyzer Client and er_print */ +char * +dbeCheckConnection (char *str) +{ + return dbe_strdup (str); +} diff --git a/gprofng/src/Dbe.h b/gprofng/src/Dbe.h new file mode 100644 index 0000000..f811096 --- /dev/null +++ b/gprofng/src/Dbe.h @@ -0,0 +1,294 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_H_ +#define _DBE_H_ + +#include <stdio.h> +#include "enums.h" + +class MetricList; +template <class ITEM> class Vector; +typedef long long Obj; + +Vector<char*> *dbeGetInitMessages (void); +Vector<char*> *dbeGetExpPreview (int dbevindex, char *exp_name); +char *dbeGetExpParams (int dbevindex, char *exp_name); +char *dbeCreateDirectories (const char *dirname); +char *dbeDeleteFile (const char *pathname); +Vector<char*> *dbeReadFile (const char *pathname); +int dbeWriteFile (const char *pathname, const char *contents); +char *dbeGetFileAttributes (const char *filename, const char *format); +char *dbeGetFiles (const char *dirname, const char *format); +char *dbeGetRunningProcesses (const char *format); +char *dbeOpenExperimentList (int dbevindex, Vector<Vector<char*>*> *groups, + bool sessionRestart); +char *dbeReadRCFile (int dbevindex, char* path); +char *dbeSetExperimentsGroups (Vector<Vector<char*>*> *groups); +Vector<Vector<char*>*> *dbeGetExperimensGroups (); +char *dbeDropExperiment (int dbevindex, Vector<int> *drop_index); +Vector<char*> *dbeGetExpsProperty (const char *prop_name); +Vector<char*> *dbeGetExpName (int dbevindex); +Vector<int> *dbeGetExpState (int dbevindex); +Vector<bool> *dbeGetExpEnable (int dbevindex); +bool dbeSetExpEnable (int dbevindex, Vector<bool> *enable); +Vector<char*> *dbeGetExpInfo (int dbevindex); +bool dbeGetViewModeEnable (); +bool dbeGetJavaEnable (); +int dbeUpdateNotes (int dbevindex, int exp_id, int type, char* text, + bool handle_file); +Vector<void*> *dbeGetTabListInfo (int dbevindex); +Vector<bool> *dbeGetTabSelectionState (int dbevindex); +void dbeSetTabSelectionState (int dbevindex, Vector<bool> *selected); +Vector<bool> *dbeGetMemTabSelectionState (int dbevindex); +void dbeSetMemTabSelectionState (int dbevindex, Vector<bool> *selected); +Vector<bool> *dbeGetIndxTabSelectionState (int dbevindex); +void dbeSetIndxTabSelectionState (int dbevindex, Vector<bool> *selected); +Vector<char*> *dbeGetLoadObjectName (int dbevindex); +Vector<void *> *dbeGetLoadObjectList (int dbevindex); +Vector<char*> *dbeGetSearchPath (int dbevindex); +void dbeSetSearchPath (int dbevindex, Vector<char*> *path); +Vector<void*> *dbeGetPathmaps (int dbevindex); +char *dbeSetPathmaps (Vector<char*> *from, Vector<char*> *to); +char *dbeAddPathmap (int dbevindex, char *from, char *to); +char *dbeGetMsg (int dbevindex, int type); +int dbeInitView (int index, int cloneindex); +void dbeDeleteView (int dbevindex); + +// methods concerning metrics +MetricList *dbeGetMetricListV2 (int dbevindex, MetricType mtype, + Vector<int> *type, Vector<int> *subtype, + Vector<bool> *sort, Vector<int> *vis, + Vector<char*> *aux, Vector<char*> *expr_spec, + Vector<char*> *legends); +Vector<void*> *dbeGetRefMetricsV2 (); +Vector<void*> *dbeGetCurMetricsV2 (int dbevindex, MetricType mtype); +void dbeSetSort (int dbevindex, int sort_index, MetricType mtype, bool reverse); + +// methods concerning metrics for Overview Tab +Vector<void*> *dbeGetRefMetricTree (int dbevindex, bool include_unregistered); +Vector<void*> *dbeGetRefMetricTreeValues (int dbevindex, Vector<char *> *met_cmds, + Vector<char *> *non_met_cmds); +Vector<char*> *dbeGetOverviewText (int dbevindex); +Vector<int> *dbeGetAnoValue (int dbevindex); +void dbeSetAnoValue (int dbevindex, Vector<int> *set); +int dbeGetNameFormat (int dbevindex); +bool dbeGetSoName (int dbevindex); +void dbeSetNameFormat (int dbevindex, int fnames, bool soname); +int dbeGetViewMode (int dbevindex); +void dbeSetViewMode (int dbevindex, int nmode); +Vector<void*> *dbeGetTLValue (int dbevindex); +void dbeSetTLValue (int dbevindex, const char *tldata_cmd, + int entitiy_prop_id, int stackalign, int stackdepth); +Vector<void*> *dbeGetExpFounderDescendants (); +Vector<void*> *dbeGetExpSelection (int dbevindex); +Vector<void*> *dbeGetSampleStatus (int dbevindex, int nselected, + Vector<bool> *selected); +Vector<unsigned> *dbeGetSampleSize (int dbevindex, Vector<bool> *selected); +char *dbeCheckPattern (int dbevindex, Vector<bool> *selected, char *pattern, + int type); +char *dbeSetFilterStr (int dbevindex, char *filter_str); +char *dbeGetFilterStr (int dbevindex); +int dbeValidateFilterExpression (char *str_expr); +Vector<void*> *dbeGetFilterKeywords (int dbevindex); +Vector<void*> *dbeGetFilters (int dbevindex, int nexp); +bool dbeUpdateFilters (int dbevindex, Vector<bool> *selected, + Vector<char*> *pattern_str); +char *dbeComposeFilterClause (int dbevindex, int type, int subtype, + Vector<int>*selections); +Vector<int> *dbeGetLoadObjectState (int dbevindex); +void dbeSetLoadObjectState (int dbevindex, Vector<int> *selected); +void dbeSetLoadObjectDefaults (int dbevindex); +Vector<void*> *dbeGetMemObjects (int dbevindex); +char *dbeDefineMemObj (char *name, char *index_expr, char *_machmodel, + char *sdesc, char *ldesc); +char *dbeDeleteMemObj (char *name); +Vector<char*> *dbeGetCPUVerMachineModel (int dbevindex); +char *dbeLoadMachineModel (char *name); +char *dbeGetMachineModel (); +Vector<char*> *dbeListMachineModels (); +void dbeDetectLoadMachineModel (int dbevindex); +Vector<void*> *dbeGetIndxObjDescriptions (int dbevindex); +Vector<void*> *dbeGetCustomIndxObjects (int dbevindex); +char *dbeDefineIndxObj (char *name, char *index_expr, char *sdesc, char *ldesc); +void dbeSetSelObj (int dbevindex, Obj sel_obj, int type, int subtype); +void dbeSetSelObjV2 (int dbevindex, uint64_t id); +Obj dbeGetSelObj (int dbevindex, int type, int subtype); +uint64_t dbeGetSelObjV2 (int dbevindex, char *typeStr); +int dbeGetSelIndex (int dbevindex, Obj sel_obj, int type, int subtype); +Vector<uint64_t> *dbeGetSelObjsIO (int dbevindex, Vector<uint64_t> *ids, int type); +Vector<uint64_t> *dbeGetSelObjIO (int dbevindex, uint64_t id, int type); +uint64_t dbeGetSelObjHeapTimestamp (int dbevindex, uint64_t id); +int dbeGetSelObjHeapUserExpId (int dbevindex, uint64_t id); +char *dbeSetPrintLimit (int dbevindex, int limit); +int dbeGetPrintLimit (int dbevindex); +char *dbeSetPrintMode (int dbevindex, char *printmode); +int dbeGetPrintMode (int dbevindex); +char *dbeGetPrintModeString (int dbevindex); +char dbeGetPrintDelim (int dbevindex); +Vector<void*> *dbeGetTotals (int dbevindex, int dsptype, int subtype); +Vector<void*> *dbeGetHotMarks (int dbevindex, int type); +Vector<void*> *dbeGetHotMarksInc (int dbevindex, int type); +Vector<void*> *dbeGetSummaryHotMarks (int dbevindex, Vector<Obj> *sel_objs, int type); +Vector<uint64_t> *dbeGetFuncId (int dbevindex, int type, int begin, int length); +Vector<void*> *dbeGetFuncCalleeInfo (int dbevindex, int type, Vector<int>* idxs, int groupId); +Vector<void*> *dbeGetFuncCallerInfo (int dbevindex, int type, Vector<int>* idxs, int groupId); +Vector<void*> *dbeGetFuncCalleeInfoById (int dbevindex, int type, int idx); +Vector<void*> *dbeGetFuncCallerInfoById (int dbevindex, int type, int idx); +char *dbePrintData (int dbevindex, int type, int subtype, char *printer, + char *fname, FILE *outfile); +int dbeSetFuncData (int dbevindex, Obj sel_obj, int type, int subtype); +Vector<void*> *dbeGetFuncList (int dbevindex, int type, int subtype); +Vector<void*> *dbeGetFuncListV2 (int dbevindex, int mtype, Obj sel_obj, int type, int subtype); +Vector<void*> *dbeGetFuncListMini (int dbevindex, int type, int subtype); +Vector<Obj> *dbeGetComparableObjsV2 (int dbevindex, Obj sel_obj, int type); +Obj dbeConvertSelObj (Obj obj, int type); +Vector<int> *dbeGetGroupIds (int dbevindex); +Vector<void*> *dbeGetTableDataV2 (int dbevindex, char *mlistStr, char *modeStr, + char *typeStr, char *subtypeStr, Vector<uint64_t> *ids); + +int dbeGetCallTreeNumLevels (int dbevindex); +Vector<void*> *dbeGetCallTreeLevel (int dbevindex, char *mcmd, int level); +Vector<void*> *dbeGetCallTreeLevels (int dbevindex, char *mcmd); +Vector<void*> *dbeGetCallTreeChildren (int dbevindex, char *mcmd, Vector<int /*NodeIdx*/>*nodes); +Vector<void*> *dbeGetCallTreeLevelFuncs (int dbevindex, int level_start, int level_end); +Vector<void*> *dbeGetCallTreeFuncs (int dbevindex); +Vector<char*> *dbeGetNames (int dbevindex, int type, Obj sel_obj); +Vector<void*> *dbeGetTotalMax (int dbevindex, int type, int subtype); +Vector<void*> *dbeGetStatisOverviewList (int dbevindex); +Vector<void*> *dbeGetStatisList (int dbevindex); +Vector<void*> *dbeGetSummary (int dbevindex, Vector<Obj> *objs, int type, int subtype); +Vector<void*> *dbeGetSummaryV2 (int dbevindex, Vector<Obj> *objs, int type, int subtype); +Vector<int> *dbeGetFounderExpId (Vector<int> *expIds); +Vector<int> *dbeGetUserExpId (Vector<int> *expIds); // filter "user visible" experiment id +Vector<int> *dbeGetExpGroupId (Vector<int> *expIds); +char *dbeGetExpName (int dbevindex, char *dir_name); +Vector<char*> *dbeGetHwcHelp (int dbevindex, bool forKernel); +Vector<Vector<char*>*> *dbeGetHwcSets (int dbevindex, bool forKernel); +Vector<void*> *dbeGetHwcsAll (int dbevindex, bool forKernel); +Vector<char*> *dbeGetHwcAttrList (int dbevindex, bool forKernel); +int dbeGetHwcMaxConcurrent (int dbevindex, bool forKernel); +int dbeGetHwcMaxReg (int dbevindex); // TBR? + +Vector<char*> *dbeGetIfreqData (int dbevindex); +Vector<void*> *dbeGetLeakListInfo (int dbevindex, bool leakflag); +Vector<void*> *dbeMpviewGetTlFuncReps (int dbevindex, int exp_id, + long long binSizeTime, long long startTime, long long endTime, + long long binSizeRank, long long startRank, long long endRank); +Vector<void*> *dbeMpviewGetTlMsgReps (int dbevindex, int exp_id, int throttle, + long long binSizeTime, long long startTime, long long endTime, + long long binSizeRank, long long startRank, long long endRank); +Vector<long long> *dbeMpviewGetAxisRange (int dbevindex, int exp_id, + int chart_type, int axis_type); +Vector<char*> *dbeMpviewGetAxisDiscreteLabels (int dbevindex, int exp_id, + int chart_type, int axis_type); +Vector<void*> *dbeMpviewGetFuncDetails (int dbevindex, int exp_id, Obj funcId); +Vector<void*> *dbeMpviewGetMesgDetails (int dbevindex, int exp_id, Obj mesgId); +Vector<long long> *dbeMpviewGetChartData (int dbevindex, int exp_id, int ctype, + int attr1, long long start1, + long long end1, int nbins1, + int attr2, long long start2, + long long end2, int nbins2, + int metric, int reduction); +void dbeMpviewFilterSet (int dbevindex, int exp_id, Vector<int> *ctid, + Vector<int > *axid, Vector<long long> *startVal, + Vector<long long> *endVal); +void dbeMpviewLoadStacks (int dbevindex, int exp_id); + + +Obj dbeGetObject (int dbevindex, Obj sel_func, Obj sel_pc); +char *dbeGetName (int dbevindex, int exp_id); +Vector<char*> *dbeGetExpVerboseName (Vector<int> *exp_ids); +long long dbeGetStartTime (int dbevindex, int exp_id); +long long dbeGetRelativeStartTime (int dbevindex, int exp_id); +long long dbeGetEndTime (int dbevindex, int exp_id); +int dbeGetClock (int dbevindex, int exp_id); +long long dbeGetWallStartSec (int dbevindex, int exp_id); +char *dbeGetHostname (int dbevindex, int exp_id); +Vector<void*> *dbeGetEntityProps (int dbevindex); +Vector<void*> *dbeGetEntities (int dbevindex, int exp_id, int ekind); +Vector<void*> *dbeGetEntitiesV2 (int dbevindex, Vector<int> *exp_ids, int ekind); +Vector<void*> *dbeGetTLDetails (int dbevindex, int exp_id, int data_id, + int entity_prop_id, Obj event_id); +Vector<Obj> *dbeGetStackFunctions (int dbevindex, Obj stack); +Vector<void*> *dbeGetStacksFunctions (int dbevindex, Vector<Obj> *stacks); +Vector<Obj> *dbeGetStackPCs (int dbevindex, Obj stack); +Vector<char*> *dbeGetStackNames (int dbevindex, Obj stack); +Vector<void*> *dbeGetSamples (int dbevindex, int exp_id, int64_t lo, int64_t hi); +Vector<void*> *dbeGetGCEvents (int dbevindex, int exp_id, int64_t lo, int64_t hi); +Vector<Vector<char*>*>* dbeGetIOStatistics (int dbevindex); +Vector<Vector<char*>*>* dbeGetHeapStatistics (int dbevindex); +Vector<char*> *dbeGetFuncNames (int dbevindex, Vector<Obj> *funcs); +Vector<char*> *dbeGetObjNamesV2 (int dbevindex, Vector<uint64_t> *ids); +char *dbeGetFuncName (int dbevindex, Obj func); +char *dbeGetObjNameV2 (int dbevindex, uint64_t id); +Vector<uint64_t> *dbeGetFuncIds (int dbevindex, Vector<Obj> *funcs); +uint64_t dbeGetFuncId (int dbevindex, Obj func); +char *dbeGetDataspaceTypeDesc (int dbevindex, Obj stack); +Vector<void*> *dbeGetDataDescriptorsV2 (int exp_id); +Vector<void*> *dbeGetDataPropertiesV2 (int exp_id, int data_id); +Vector<void*> *dbeGetExperimentTimeInfo (Vector<int> *exp_ids); +Vector<void*> *dbeGetExperimentDataDescriptors (Vector<int> *exp_ids); + +/* New Timeline Interface */ +Vector<long long> *dbeGetAggregatedValue (int data_id, char *lfilter, char *fexpr, + char *pname_ts, hrtime_t start_ts, + hrtime_t delta, int num, + char *pname_key, char *aggr_func); +Vector<char*> *dbeGetLineInfo (Obj pc); +int dbeSetAlias (char *name, char *uname, char *expr); +Vector<char*> *dbeGetAlias (char *name); +Vector<Vector<long long>*> *dbeGetXYPlotData (int data_id, char *lfilter, + char *arg, char *func1, char *aggr1, + char *func2, char *aggr2, + char *func3, char *aggr3); +Vector<bool> *dbeHasTLData (int dbev_index, Vector<int> *exp_ids, + Vector<int> *data_ids, // DATA_* + Vector<int> *entity_prop_ids, // LWP,CPU,THR, etc + Vector<int> *entity_prop_values, + Vector<int> *auxs); +Vector<void*> *dbeGetTLData (int dbevindex, int exp_id, int data_id, + int entity_prop_id, int entity_prop_val, int aux, + hrtime_t start_ts, hrtime_t delta, int num, + bool getRepresentatives, Vector<char*> *chartProperties); +Vector<long long> *dbeGetTLEventCenterTime (int dbevindex, int exp_id, + int data_id, int entity_prop_id, + int entity_prop_val, int aux, + long long event_idx, long long move_count); +long long dbeGetTLEventIdxNearTime (int dbevindex, int exp_id, + int data_id, + int entity_prop_id, int entity_prop_val, int aux, + int searchDirection, + long long timestamp); + +/* Interface for use by Collector GUI */ +int dbeGetSignalValue (char *); +char *dbeSendSignal (pid_t, int); +char *dbeGetCollectorControlValue (char *); +char *dbeSetCollectorControlValue (char *, char *); +char *dbeUnsetCollectorControlValue (char *); +char *dbeCheckConnection (char *); +void dbe_archive (Vector<long long> *ids, Vector<const char *> *locations); +void dbeSetLocation (const char *fname, const char *location); +void dbeSetLocations (Vector<const char *> *fnames, Vector<const char *> *locations); +Vector<void*> *dbeResolvedWith_setpath (const char *path); +Vector<void*> *dbeResolvedWith_pathmap (const char *old_prefix, const char *new_prefix); + +#endif /* _DBE_H_ */ diff --git a/gprofng/src/DbeApplication.cc b/gprofng/src/DbeApplication.cc new file mode 100644 index 0000000..382bd45 --- /dev/null +++ b/gprofng/src/DbeApplication.cc @@ -0,0 +1,113 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include "DbeSession.h" +#include "DbeApplication.h" +#include "LoadObject.h" +#include "Experiment.h" +#include "PreviewExp.h" +#include "Function.h" +#include "Hist_data.h" +#include "Module.h" +#include "DataObject.h" +#include "Sample.h" +#include "CallStack.h" +#include "Print.h" +#include "util.h" +#include "libgen.h" +#include "i18n.h" + +DbeApplication *theDbeApplication; + +DbeApplication::DbeApplication (int argc, char *argv[], char* _run_dir) +: Application (argc, argv, _run_dir) +{ + theDbeApplication = this; + ipcMode = false; + rdtMode = false; + if (argc > 1) + if (strcmp (argv[1], NTXT ("-IPC")) == 0 + || strcmp (argv[1], NTXT ("-DIPC")) == 0) + ipcMode = true; + lic_found = 0; + lic_err = NULL; + + // Instantiate a session + (void) new DbeSession (settings, ipcMode, rdtMode); +} + +DbeApplication::~DbeApplication () +{ + delete dbeSession; + theDbeApplication = NULL; +} + +Vector<char*> * +DbeApplication::initApplication (char *fdhome, char *licpath, ProgressFunc func) +{ + // set the home directory + if (fdhome != NULL) + set_run_dir (fdhome); + + // Set progress function + set_progress_func (func); + + // Get license + char *license_err = NULL; + char *sts; + if (licpath != NULL) + { + lic_found = 0; + if (lic_found == 0) + { + lic_err = dbe_strdup (DbeApplication::get_version ()); + sts = dbe_strdup (GTXT ("OK")); + } + else if (lic_found == 2) + { + lic_err = dbe_strdup (license_err); + sts = dbe_strdup (GTXT ("WARN")); + } + else if (lic_found == 3) + { + lic_err = dbe_strdup (license_err); + sts = dbe_strdup (GTXT ("FATAL")); + } + else + { + lic_err = dbe_strdup (license_err); + sts = dbe_strdup (GTXT ("ERROR")); + } + } + else + { + lic_err = dbe_strdup (DbeApplication::get_version ()); + sts = dbe_strdup (GTXT ("OK")); + } + Vector<char*> *data = new Vector<char*>(2); + data->store (0, sts); + data->store (1, lic_err); + return data; +} diff --git a/gprofng/src/DbeApplication.h b/gprofng/src/DbeApplication.h new file mode 100644 index 0000000..b6bdaaf --- /dev/null +++ b/gprofng/src/DbeApplication.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * The DbeApplication class is the base class for all C++ executables + * that read Experiments + * + * It is derived from the Application class, and differs from it + * only in that it instantiates a DbeSession (q.v.) to manage + * the reading and processing of Experiments + */ + +#ifndef _DBEAPPLICATION_H +#define _DBEAPPLICATION_H + +#include "Application.h" + +template <class ITEM> class Vector; + +class DbeApplication : public Application +{ +public: + DbeApplication (int argc, char *argv[], char *_run_dir = NULL); + ~DbeApplication (); + Vector<char*> *initApplication (char *fdhome, char *licpath, ProgressFunc func); + + bool rdtMode; + bool ipcMode; +}; + +extern DbeApplication *theDbeApplication; + +#endif /* _DBEAPPLICATION_H */ diff --git a/gprofng/src/DbeArray.h b/gprofng/src/DbeArray.h new file mode 100644 index 0000000..af7c36e --- /dev/null +++ b/gprofng/src/DbeArray.h @@ -0,0 +1,99 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DbeArray_H +#define _DbeArray_H + +template <typename ITEM> class DbeArray +{ +public: + + DbeArray (long sz) + { + count = 0; + limit = sz; + data = new ITEM[limit]; + }; + + virtual + ~DbeArray () + { + delete[] data; + } + + int + append (const ITEM &item) + { + int n = allocate (1); + ITEM *p = get (n); + *p = item; + return n; + }; + + ITEM * + get (long index) + { + return (index < count && index >= 0) ? data + index : (ITEM *) NULL; + }; + + int + allocate (int cnt) + { + count += cnt; + resize (count); + return count - cnt; + }; + + int + size () + { + return (int) count; + }; + + void + reset () + { + count = 0; + }; + +private: + + void + resize (long cnt) + { + if (limit <= cnt) + { + limit *= 2; + if (limit < cnt) + limit = cnt + 1; + ITEM *d = new ITEM[limit]; + if (count > 0) + memcpy (d, data, sizeof (ITEM) * count); + delete[] data; + data = d; + } + }; + + ITEM *data; // Pointer to data vector + long count; // Number of items + long limit; // Array length +}; + +#endif /* _DbeArray_H */ diff --git a/gprofng/src/DbeCacheMap.h b/gprofng/src/DbeCacheMap.h new file mode 100644 index 0000000..4c51a34 --- /dev/null +++ b/gprofng/src/DbeCacheMap.h @@ -0,0 +1,109 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Dbe Cache Map implementation. + * + * Simple Cache Map makes the following assumptions: + * - Cache Map is used for very fast but not guaranteed mapping; + * - No Relations can be used; + * - all objects used as keys or values has to be managed + * outside CacheMap (f.e. deletion); + */ + +#ifndef _DbeCacheMap_h +#define _DbeCacheMap_h + +#include <Map.h> + +template <typename Key_t, class ITEM> +class DbeCacheMap : public Map<Key_t, ITEM *> +{ +public: + + DbeCacheMap (int _size = DefaultSize) + { // _size should be 2 ** N + size = _size; + table = new DbeCache_T[size]; + memset (table, 0, size * sizeof (DbeCache_T)); + }; + + ~DbeCacheMap () + { + delete[] table; + }; + + void + put (Key_t key, ITEM *val) + { + int ind = get_hash (key); + table[ind].key = key; + table[ind].value = val; + }; + + ITEM * + get (Key_t key) + { + int ind = get_hash (key); + if (table[ind].key == key) + return table[ind].value; + return (ITEM *) NULL; + }; + + ITEM * + remove (Key_t key) + { + int ind = get_hash (key); + ITEM *v = table[ind].value; + table[ind].value = (ITEM *) NULL; + return v; + }; + + ITEM * + get (Key_t /* key */, typename Map<Key_t, ITEM *>::Relation /* rel */) + { + return (ITEM *) NULL; + }; + +private: + + enum + { + DefaultSize = (1 << 13) + }; + + typedef struct DbeCache_S + { + Key_t key; + ITEM *value; + } DbeCache_T; + DbeCache_T *table; + int size; + + int + get_hash (Key_t key) + { + unsigned long long h = (unsigned long long) key; + h ^= (h >> 20) ^ (h >> 12); + return (h ^ (h >> 7) ^ (h >> 4)) & (size - 1); + } +}; + +#endif diff --git a/gprofng/src/DbeFile.cc b/gprofng/src/DbeFile.cc new file mode 100644 index 0000000..9e1fe1c --- /dev/null +++ b/gprofng/src/DbeFile.cc @@ -0,0 +1,541 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "util.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "DbeFile.h" +#include "ExpGroup.h" +#include "DbeJarFile.h" + +DbeFile::DbeFile (const char *filename) +{ + filetype = 0; + name = dbe_strdup (filename); + name = canonical_path (name); + orig_location = NULL; + location = NULL; + location_info = NULL; + jarFile = NULL; + container = NULL; + need_refind = true; + inArchive = false; + sbuf.st_atim.tv_sec = 0; + experiment = NULL; +} + +DbeFile::~DbeFile () +{ + free (name); + free (location); + free (orig_location); + free (location_info); +} + +void +DbeFile::set_need_refind (bool val) +{ + if (val != need_refind) + { + free (location_info); + location_info = NULL; + need_refind = val; + } +} + +void +DbeFile::set_location (const char *filename) +{ + free (location); + location = NULL; + if (filename) + { + if (strncmp (filename, NTXT ("./"), 2) == 0) + filename += 2; + location = canonical_path (dbe_strdup (filename)); + } + free (location_info); + location_info = NULL; + set_need_refind (false); +} + +char * +DbeFile::get_location_info () +{ + if (location_info == NULL) + { + char *fnm = get_name (); + char *loc = get_location (); + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location_info: %s %s\n"), + STR (fnm), STR (loc)); + if (loc == NULL) + { + if (filetype & F_FICTION) + location_info = dbe_strdup (fnm); + else + location_info = dbe_sprintf (GTXT ("%s (not found)"), + get_relative_path (fnm)); + } + else + { + char *r_fnm = get_relative_path (fnm); + char *r_loc = get_relative_path (loc); + if (strcmp (r_fnm, r_loc) == 0) + location_info = dbe_strdup (r_fnm); + else + { + char *bname = get_basename (r_fnm); + if (strcmp (bname, r_loc) == 0) // found in current directory + location_info = dbe_strdup (bname); + else + location_info = dbe_sprintf (GTXT ("%s (found as %s)"), bname, r_loc); + } + } + } + return location_info; +} + +char * +DbeFile::getResolvedPath () +{ + if (get_location ()) + return location; + return name; +} + +DbeFile * +DbeFile::getJarDbeFile (char *fnm, int sym) +{ + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::getJarDbeFile: %s fnm='%s' sym=%d\n"), + STR (name), STR (fnm), sym); + DbeFile *df = NULL; + if (sym) + { + char *s = strchr (fnm, sym); + if (s) + { + s = dbe_strndup (fnm, s - fnm); + df = dbeSession->getDbeFile (s, F_JAR_FILE | F_FILE); + free (s); + } + } + if (df == NULL) + df = dbeSession->getDbeFile (fnm, F_JAR_FILE | F_FILE); + if (df && (df->experiment == NULL)) + df->experiment = experiment; + return df; +} + +char * +DbeFile::get_location (bool find_needed) +{ + Dprintf (DEBUG_DBE_FILE, NTXT ("get_location 0x%x %s\n"), filetype, STR (name)); + if (!find_needed) + return need_refind ? NULL : location; + if (location || !need_refind) + return location; + set_need_refind (false); + if ((filetype & F_FICTION) != 0) + return NULL; + if (filetype == F_DIR_OR_JAR) + { + find_in_archives (name); + if (location) + { + filetype |= F_JAR_FILE | F_FILE; + return location; + } + find_in_pathmap (name); + if (location) + return location; + if (check_access (name) == F_DIRECTORY) + { + filetype |= F_DIRECTORY; + set_location (name); + return location; + } + } + + if ((filetype & F_FILE) != 0) + { + if (experiment) + { + char *fnm = experiment->checkFileInArchive (name, false); + if (fnm) + { + set_location (fnm); + inArchive = true; + sbuf.st_mtime = 0; // Don't check timestamps + free (fnm); + return location; + } + if ((filetype & F_JAVACLASS) != 0) + { + if (orig_location) + { + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d name='%s' orig_location='%s'\n"), + (int) __LINE__, name, orig_location); + // Parse a fileName attribute. There are 4 possibilities: + // file:<Class_Name> + // file:<name_of_jar_or_zip_file> + // jar:file:<name_of_jar_or_zip_file>!<Class_Name> + // zip:<name_of_jar_or_zip_file>!<Class_Name> + DbeFile *jar_df = NULL; + if (strncmp (orig_location, NTXT ("zip:"), 4) == 0) + jar_df = getJarDbeFile (orig_location + 4, '!'); + else if (strncmp (orig_location, NTXT ("jar:file:"), 9) == 0) + jar_df = getJarDbeFile (orig_location + 9, '!'); + else if (strncmp (orig_location, NTXT ("file:"), 5) == 0 + && isJarOrZip (orig_location + 5)) + jar_df = getJarDbeFile (orig_location + 5, 0); + if (jar_df) + { + if (find_in_jar_file (name, jar_df->get_jar_file ())) + { + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d FOUND name='%s' location='%s' jar='%s'\n"), + (int) __LINE__, name, STR (location), STR (jar_df->get_location ())); + inArchive = jar_df->inArchive; + container = jar_df; + return location; + } + } + if (strncmp (orig_location, NTXT ("file:"), 5) == 0 + && !isJarOrZip (orig_location + 5)) + { + DbeFile *df = new DbeFile (orig_location + 5); + df->filetype = DbeFile::F_FILE; + df->experiment = experiment; + fnm = df->get_location (); + if (fnm) + { + set_location (fnm); + inArchive = df->inArchive; + sbuf.st_mtime = df->sbuf.st_mtime; + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d FOUND name='%s' orig_location='%s' location='%s'\n"), + (int) __LINE__, name, orig_location, fnm); + delete df; + return location; + } + delete df; + } + } + fnm = dbe_sprintf (NTXT ("%s/%s/%s"), experiment->get_expt_name (), SP_DYNAMIC_CLASSES, name); + if (find_file (fnm)) + { + inArchive = true; + sbuf.st_mtime = 0; // Don't check timestamps + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d FOUND name='%s' location='%s'\n"), + (int) __LINE__, name, fnm); + free (fnm); + return location; + } + free (fnm); + } + } + } + + if (dbeSession->archive_mode) + { + find_file (name); + if (location) + return location; + } + + bool inPathMap = find_in_pathmap (name); + if (location) + return location; + find_in_setpath (name, dbeSession->get_search_path ()); + if (location) + return location; + if ((filetype & (F_JAVACLASS | F_JAVA_SOURCE)) != 0) + { + find_in_classpath (name, dbeSession->get_classpath ()); + if (location) + return location; + } + if (!inPathMap) + find_file (name); + Dprintf (DEBUG_DBE_FILE && (location == NULL), + "DbeFile::get_location:%d NOT FOUND name='%s'\n", __LINE__, name); + return location; +} + +int +DbeFile::check_access (const char *filename) +{ + if (filename == NULL) + return F_NOT_FOUND; + int st = dbe_stat (filename, &sbuf); + Dprintf (DEBUG_DBE_FILE, NTXT ("check_access: %d 0x%x %s\n"), st, filetype, filename); + if (st == 0) + { + if (S_ISDIR (sbuf.st_mode)) + return F_DIRECTORY; + else if (S_ISREG (sbuf.st_mode)) + return F_FILE; + return F_UNKNOWN; // Symbolic link or unknown type of file + } + sbuf.st_atim.tv_sec = 0; + sbuf.st_mtime = 0; // Don't check timestamps + return F_NOT_FOUND; // File not found +} + +bool +DbeFile::isJarOrZip (const char *fnm) +{ + size_t len = strlen (fnm) - 4; + return len > 0 && (strcmp (fnm + len, NTXT (".jar")) == 0 + || strcmp (fnm + len, NTXT (".zip")) == 0); +} + +char * +DbeFile::find_file (const char *filename) +{ + switch (check_access (filename)) + { + case F_DIRECTORY: + if (filetype == F_DIR_OR_JAR) + filetype |= F_DIRECTORY; + if ((filetype & F_DIRECTORY) != 0) + set_location (filename); + break; + case F_FILE: + if (filetype == F_DIR_OR_JAR) + { + filetype |= F_FILE; + if (isJarOrZip (filename)) + filetype |= F_JAR_FILE; + } + if ((filetype & F_DIRECTORY) == 0) + set_location (filename); + break; + } + return location; +} + +DbeJarFile * +DbeFile::get_jar_file () +{ + if (jarFile == NULL) + { + char *fnm = get_location (); + if (fnm) + jarFile = dbeSession->get_JarFile (fnm); + } + return jarFile; +} + +char * +DbeFile::find_package_name (const char *filename, const char *dirname) +{ + char *nm = dbe_sprintf (NTXT ("%s/%s"), dirname, filename); + if (!find_in_pathmap (nm)) + find_file (nm); + free (nm); + return location; +} + +char * +DbeFile::find_in_directory (const char *filename, const char *dirname) +{ + if (filename && dirname) + { + char *nm = dbe_sprintf (NTXT ("%s/%s"), dirname, filename); + find_file (nm); + free (nm); + } + return location; +} + +char * +DbeFile::find_in_jar_file (const char *filename, DbeJarFile *jfile) +{ + // Read .jar or .zip + if (jfile == NULL) + return NULL; + int entry = jfile->get_entry (filename); + if (entry >= 0) + { + char *fnm = dbeSession->get_tmp_file_name (filename, true); + long long fsize = jfile->copy (fnm, entry); + if (fsize >= 0) + { + dbeSession->tmp_files->append (fnm); + set_location (fnm); + sbuf.st_size = fsize; + sbuf.st_mtime = 0; // Don't check timestamps + fnm = NULL; + } + free (fnm); + } + return location; +} + +bool +DbeFile::find_in_pathmap (char *filename) +{ + Vector<pathmap_t*> *pathmaps = dbeSession->get_pathmaps (); + bool inPathMap = false; + if (strncmp (filename, NTXT ("./"), 2) == 0) + filename += 2; + for (int i = 0, sz = pathmaps ? pathmaps->size () : 0; i < sz; i++) + { + pathmap_t *pmp = pathmaps->fetch (i); + size_t len = strlen (pmp->old_prefix); + if (strncmp (pmp->old_prefix, filename, len) == 0 + && (filename[len] == '/' || filename[len] == '\0')) + { + inPathMap = true; + if (find_in_directory (filename + len, pmp->new_prefix)) + { + return inPathMap; + } + } + } + return inPathMap; +} + +void +DbeFile::find_in_archives (char *filename) +{ + for (int i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (i1); + if (gr->founder) + { + char *nm = gr->founder->checkFileInArchive (filename, false); + if (nm) + { + find_file (nm); + if (location) + { + sbuf.st_mtime = 0; // Don't check timestamps + return; + } + } + } + } +} + +void +DbeFile::find_in_setpath (char *filename, Vector<char*> *searchPath) +{ + char *base = get_basename (filename); + for (int i = 0, sz = searchPath ? searchPath->size () : 0; i < sz; i++) + { + char *spath = searchPath->fetch (i); + // Check file in each experiment directory + if (streq (spath, "$") || streq (spath, NTXT ("$expts"))) + { + // find only in founders and only LoadObj. + for (int i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (i1); + char *exp_name = gr->founder->get_expt_name (); + if (gr->founder) + { + if ((filetype & (F_JAVACLASS | F_JAVA_SOURCE)) != 0) + { + // Find with the package name + if (find_in_directory (filename, exp_name)) + return; + } + if (find_in_directory (base, exp_name)) + return; + } + } + continue; + } + DbeFile *df = dbeSession->getDbeFile (spath, DbeFile::F_DIR_OR_JAR); + if (df->get_location () == NULL) + continue; + if ((filetype & (F_JAVACLASS | F_JAVA_SOURCE)) != 0) + { + if ((df->filetype & F_JAR_FILE) != 0) + { + if (find_in_jar_file (filename, df->get_jar_file ())) + { + container = df; + return; + } + continue; + } + else if ((df->filetype & F_DIRECTORY) != 0) + // Find with the package name + if (find_package_name (filename, spath)) + return; + } + if ((df->filetype & F_DIRECTORY) != 0) + if (find_in_directory (base, df->get_location ())) + return; + } +} + +void +DbeFile::find_in_classpath (char *filename, Vector<DbeFile*> *classPath) +{ + for (int i = 0, sz = classPath ? classPath->size () : 0; i < sz; i++) + { + DbeFile *df = classPath->fetch (i); + if (df->get_location () == NULL) + continue; + if ((df->filetype & F_JAR_FILE) != 0) + { + if (find_in_jar_file (filename, df->get_jar_file ())) + { + container = df; + return; + } + } + else if ((df->filetype & F_DIRECTORY) != 0) + // Find with the package name + if (find_package_name (filename, df->get_name ())) + return; + } +} + +struct stat64 * +DbeFile::get_stat () +{ + if (sbuf.st_atim.tv_sec == 0) + { + int st = check_access (get_location (false)); + if (st == F_NOT_FOUND) + return NULL; + } + return &sbuf; +} + +bool +DbeFile::compare (DbeFile *df) +{ + if (df == NULL) + return false; + struct stat64 *st1 = get_stat (); + struct stat64 *st2 = df->get_stat (); + if (st1 == NULL || st2 == NULL) + return false; + if (st1->st_size != st2->st_size) + return false; + if (st1->st_mtim.tv_sec != st2->st_mtim.tv_sec) + return false; + return true; +} diff --git a/gprofng/src/DbeFile.h b/gprofng/src/DbeFile.h new file mode 100644 index 0000000..abd7180 --- /dev/null +++ b/gprofng/src/DbeFile.h @@ -0,0 +1,103 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_FILE_H +#define _DBE_FILE_H + +#include <fcntl.h> + +class DbeJarFile; +class Experiment; +template <class ITEM> class Vector; + +class DbeFile +{ +public: + + enum + { + F_NOT_FOUND = 0, + F_FICTION = 1, + F_LOADOBJ = 2, + F_SOURCE = 4, + F_JAVACLASS = 8, + F_JAVA_SOURCE = 16, + F_DOT_O = 32, + F_DEBUG_FILE = 64, + F_DOT_A_LIB = 128, + F_DIR_OR_JAR = 256, + F_DIRECTORY = 512, + F_FILE = 1024, + F_JAR_FILE = 2048, + F_UNKNOWN = 65536 + }; + + DbeFile (const char *filename); + ~DbeFile (); + + char * + get_name () + { + return name; + }; + + bool + get_need_refind () + { + return need_refind; + }; + + char *get_location (bool find_needed = true); + char *getResolvedPath (); + char *get_location_info (); + struct stat64 *get_stat (); + bool compare (DbeFile *df); + void set_need_refind (bool val); + void set_location (const char *filename); + int check_access (const char *filename); + char *find_file (const char *filename); + DbeFile *getJarDbeFile (char *fnm, int sym); + char *find_in_jar_file (const char *filename, DbeJarFile *jfile); + DbeJarFile *get_jar_file (); + + bool inArchive; + int filetype; + struct stat64 sbuf; + DbeFile *container; + char *orig_location; + Experiment *experiment; + +protected: + static bool isJarOrZip (const char *fnm); + char *find_package_name (const char *filename, const char *dirname); + char *find_in_directory (const char *filename, const char *dirname); + bool find_in_pathmap (char *filename); + void find_in_archives (char *filename); + void find_in_setpath (char *filename, Vector<char*> *searchPath); + void find_in_classpath (char *filename, Vector<DbeFile*> *classPath); + + char *name; + char *location; + char *location_info; + bool need_refind; + DbeJarFile *jarFile; +}; + +#endif /* _DBE_FILE_H */ diff --git a/gprofng/src/DbeJarFile.cc b/gprofng/src/DbeJarFile.cc new file mode 100644 index 0000000..9029512 --- /dev/null +++ b/gprofng/src/DbeJarFile.cc @@ -0,0 +1,505 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include "zlib.h" +#include "util.h" +#include "DbeJarFile.h" +#include "Data_window.h" +#include "vec.h" + +static uint32_t +get_u1 (unsigned char *b) +{ + return (uint32_t) ((b)[0]); +} + +static uint32_t +get_u2 (unsigned char *b) +{ + return (get_u1 (b + 1) << 8) | get_u1 (b); +} + +static uint32_t +get_u4 (unsigned char *b) +{ + return (get_u2 (b + 2) << 16) | get_u2 (b); +} + +static uint64_t +get_u8 (unsigned char *b) +{ + return (((uint64_t) get_u4 (b + 4)) << 32) | get_u4 (b); +} + +enum +{ + END_CENT_DIR_SIZE = 22, + LOC_FILE_HEADER_SIZE = 30, + CENT_FILE_HEADER_SIZE = 46, + ZIP64_LOCATOR_SIZE = 20, + ZIP64_CENT_DIR_SIZE = 56, + ZIP_BUF_SIZE = 65536 +}; + +struct EndCentDir +{ + uint64_t count; + uint64_t size; + uint64_t offset; +}; + +class ZipEntry +{ +public: + + ZipEntry () + { + name = NULL; + data_offset = 0; + } + + ~ZipEntry () + { + free (name); + } + + int + compare (ZipEntry *ze) + { + return dbe_strcmp (name, ze->name); + } + + char *name; // entry name + int time; // modification time + int64_t size; // size of uncompressed data + int64_t csize; // size of compressed data (zero if uncompressed) + uint32_t compressionMethod; + int64_t offset; // offset of LOC header + int64_t data_offset; +}; + +static int +cmp_names (const void *a, const void *b) +{ + ZipEntry *e1 = *((ZipEntry **) a); + ZipEntry *e2 = *((ZipEntry **) b); + return e1->compare (e2); +} + +template<> void Vector<ZipEntry *>::dump (const char *msg) +{ + Dprintf (1, NTXT ("Vector<ZipEntry *> %s [%lld]\n"), msg ? msg : NTXT (""), (long long) size ()); + for (long i = 0, sz = size (); i < sz; i++) + { + ZipEntry *ze = get (i); + Dprintf (1, NTXT (" %lld offset:%lld (0x%llx) size: %lld --> %lld %s\n"), + (long long) i, (long long) ze->offset, (long long) ze->offset, + (long long) ze->csize, (long long) ze->size, STR (ze->name)); + } +} + +DbeJarFile::DbeJarFile (const char *jarName) +{ + name = strdup (jarName); + fnames = NULL; + dwin = new Data_window (name); + get_entries (); +} + +DbeJarFile::~DbeJarFile () +{ + free (name); + delete fnames; +} + +void +DbeJarFile::get_entries () +{ + Dprintf (DUMP_JAR_FILE, NTXT ("\nArchive: %s\n"), STR (name)); + if (dwin->not_opened ()) + { + append_msg (CMSG_ERROR, GTXT ("Cannot open file `%s'"), name); + return; + } + struct EndCentDir endCentDir; + if (get_EndCentDir (&endCentDir) == 0) + return; + + if (endCentDir.count == 0) + { + append_msg (CMSG_WARN, GTXT ("No files in %s"), name); + return; + } + unsigned char *b = (unsigned char *) dwin->bind (endCentDir.offset, endCentDir.size); + if (b == NULL) + { + append_msg (CMSG_ERROR, GTXT ("%s: cannot read the central directory record"), name); + return; + } + + fnames = new Vector<ZipEntry*>(endCentDir.count); + for (uint64_t i = 0, offset = endCentDir.offset, last = endCentDir.offset + endCentDir.size; i < endCentDir.count; i++) + { + if ((last - offset) < CENT_FILE_HEADER_SIZE) + { + append_msg (CMSG_ERROR, GTXT ("%s: cannot read the central file header (%lld (from %lld), offset=0x%016llx last=0x%016llx"), + name, (long long) i, (long long) endCentDir.count, (long long) offset, (long long) last); + break; + } + b = (unsigned char *) dwin->bind (offset, CENT_FILE_HEADER_SIZE); + // Central file header + // Offset Bytes Description + // 0 4 central file header signature = 0x02014b50 + // 4 2 version made by + // 6 2 version needed to extract + // 8 2 general purpose bit flag + // 10 2 compression method + // 12 2 last mod file time + // 14 2 last mod file date + // 16 4 crc-32 + // 20 4 compressed size + // 24 4 uncompressed size + // 28 2 file name length + // 30 2 extra field length + // 32 2 file comment length + // 34 2 disk number start + // 36 2 internal file attributes + // 38 4 external file attributes + // 42 4 relative offset of local header + // 46 file name (variable size) + // extra field (variable size) + // file comment (variable size) + uint32_t signature = get_u4 (b); + if (signature != 0x02014b50) + { + append_msg (CMSG_ERROR, GTXT ("%s: wrong header signature (%lld (total %lld), offset=0x%016llx last=0x%016llx"), + name, (long long) i, (long long) endCentDir.count, (long long) offset, (long long) last); + break; + } + ZipEntry *ze = new ZipEntry (); + fnames->append (ze); + uint32_t name_len = get_u2 (b + 28); + uint32_t extra_len = get_u2 (b + 30); + uint32_t comment_len = get_u2 (b + 32); + ze->compressionMethod = get_u2 (b + 10); + ze->csize = get_u4 (b + 20); + ze->size = get_u4 (b + 24); + ze->offset = get_u4 (b + 42); + char *nm = (char *) dwin->bind (offset + 46, name_len); + if (nm) + { + ze->name = (char *) malloc (name_len + 1); + strncpy (ze->name, nm, name_len); + ze->name[name_len] = 0; + } + offset += CENT_FILE_HEADER_SIZE + name_len + extra_len + comment_len; + } + fnames->sort (cmp_names); + if (DUMP_JAR_FILE) + fnames->dump (get_basename (name)); +} + +int +DbeJarFile::get_entry (const char *fname) +{ + if (fnames == NULL) + return -1; + ZipEntry zipEntry, *ze = &zipEntry; + ze->name = (char *) fname; + int ind = fnames->bisearch (0, -1, &ze, cmp_names); + ze->name = NULL; + return ind; +} + +long long +DbeJarFile::copy (char *toFileNname, int fromEntryNum) +{ + if (fromEntryNum < 0 || fromEntryNum >= VecSize (fnames)) + return -1; + ZipEntry *ze = fnames->get (fromEntryNum); + if (ze->data_offset == 0) + { + // Local file header + // Offset Bytes Description + // 0 4 local file header signature = 0x04034b50 + // 4 2 version needed to extract + // 6 2 general purpose bit flag + // 8 2 compression method + // 10 2 last mod file time + // 12 2 last mod file date + // 14 4 crc-32 + // 18 4 compressed size + // 22 4 uncompressed size + // 26 2 file name length + // 28 2 extra field length + // 30 2 file name (variable size) + // extra field (variable size) + unsigned char *b = (unsigned char *) dwin->bind (ze->offset, LOC_FILE_HEADER_SIZE); + if (b == NULL) + { + append_msg (CMSG_ERROR, + GTXT ("%s: Cannot read a local file header (%s offset=0x%lld"), + name, STR (ze->name), (long long) ze->offset); + return -1; + } + uint32_t signature = get_u4 (b); + if (signature != 0x04034b50) + { + append_msg (CMSG_ERROR, + GTXT ("%s: wrong local header signature ('%s' offset=%lld (0x%llx)"), + name, STR (ze->name), (long long) ze->offset, + (long long) ze->offset); + return -1; + } + ze->data_offset = ze->offset + LOC_FILE_HEADER_SIZE + get_u2 (b + 26) + get_u2 (b + 28); + } + + if (ze->compressionMethod == 0) + { + int fd = open (toFileNname, O_CREAT | O_WRONLY | O_LARGEFILE, 0644); + if (fd == -1) + { + append_msg (CMSG_ERROR, GTXT ("Cannot create file %s (%s)"), toFileNname, STR (strerror (errno))); + return -1; + } + long long len = dwin->copy_to_file (fd, ze->data_offset, ze->size); + close (fd); + if (len != ze->size) + { + append_msg (CMSG_ERROR, GTXT ("%s: Cannot write %lld bytes (only %lld)"), + toFileNname, (long long) ze->size, (long long) len); + unlink (toFileNname); + return -1; + } + return len; + } + + unsigned char *b = (unsigned char *) dwin->bind (ze->data_offset, ze->csize); + if (b == NULL) + { + append_msg (CMSG_ERROR, + GTXT ("%s: Cannot extract file %s (offset=0x%lld csize=%lld)"), + name, STR (ze->name), (long long) ze->offset, + (long long) ze->csize); + return -1; + } + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_in = Z_NULL; + strm.avail_in = 0; + if (inflateInit2 (&strm, -MAX_WBITS) != Z_OK) + { + append_msg (CMSG_ERROR, GTXT ("%s: inflateInit2 failed (%s)"), STR (ze->name), STR (strm.msg)); + return -1; + } + strm.avail_in = ze->csize; + strm.next_in = b; + int retval = ze->size; + unsigned char *buf = (unsigned char *) malloc (ze->size); + for (;;) + { + strm.next_out = buf; + strm.avail_out = ze->size; + int ret = inflate (&strm, Z_SYNC_FLUSH); + if ((ret == Z_NEED_DICT) || (ret == Z_DATA_ERROR) || (ret == Z_MEM_ERROR) || (ret == Z_STREAM_ERROR)) + { + append_msg (CMSG_ERROR, GTXT ("%s: inflate('%s') error %d (%s)"), name, STR (ze->name), ret, STR (strm.msg)); + retval = -1; + break; + } + if (strm.avail_out != 0) + break; + } + inflateEnd (&strm); + if (retval != -1) + { + int fd = open (toFileNname, O_CREAT | O_WRONLY | O_LARGEFILE, 0644); + if (fd == -1) + { + append_msg (CMSG_ERROR, GTXT ("Cannot create file %s (%s)"), toFileNname, STR (strerror (errno))); + retval = -1; + } + else + { + long long len = write (fd, buf, ze->size); + if (len != ze->size) + { + append_msg (CMSG_ERROR, GTXT ("%s: Cannot write %lld bytes (only %lld)"), + toFileNname, (long long) strm.avail_out, (long long) len); + retval = -1; + } + close (fd); + } + } + free (buf); + return retval; +} + +int +DbeJarFile::get_EndCentDir (struct EndCentDir *endCentDir) +{ + int64_t fsize = dwin->get_fsize (); + int64_t sz = (fsize < ZIP_BUF_SIZE) ? fsize : ZIP_BUF_SIZE; + + // Find the end of central directory record: + unsigned char *b = (unsigned char *) dwin->bind (fsize - sz, sz); + if (b == NULL) + { + append_msg (CMSG_ERROR, GTXT ("%s: cannot find the central directory record (fsize=%lld)"), + name, (long long) fsize); + return 0; + } + + // End of central directory record: + // Offset Bytes Description + // 0 4 end of central directory signature = 0x06054b50 + // 4 2 number of this disk + // 6 2 disk where central directory starts + // 8 2 number of central directory records on this disk + // 10 2 total number of central directory records + // 12 4 size of central directory(bytes) + // 16 4 offset of start of central directory, relative to start of archive + // 20 2 comment length(n) + // 22 n comment + + endCentDir->count = 0; + endCentDir->size = 0; + endCentDir->offset = 0; + int64_t ecdrOffset = fsize; + for (int64_t i = END_CENT_DIR_SIZE; i < sz; i++) + { + b = (unsigned char *) dwin->bind (fsize - i, END_CENT_DIR_SIZE); + if (b == NULL) + { + append_msg (CMSG_ERROR, GTXT ("%s: read failed (offset:0x%llx bytes:%lld"), + name, (long long) (fsize - i), (long long) END_CENT_DIR_SIZE); + break; + } + uint32_t signature = get_u4 (b); + if (signature == 0x06054b50) + { + int64_t len_comment = get_u2 (b + 20); + if (i != (len_comment + END_CENT_DIR_SIZE)) + continue; + ecdrOffset = fsize - i; + endCentDir->count = get_u2 (b + 10); + endCentDir->size = get_u4 (b + 12); + endCentDir->offset = get_u4 (b + 16); + Dprintf (DUMP_JAR_FILE, + " Zip archive file size: %10lld (0x%016llx)\n" + " end-cent-dir record offset: %10lld (0x%016llx)\n" + " cent-dir offset: %10lld (0x%016llx)\n" + " cent-dir size: %10lld (0x%016llx)\n" + " cent-dir entries: %10lld\n", + (long long) fsize, (long long) fsize, + (long long) ecdrOffset, (long long) ecdrOffset, + (long long) endCentDir->offset, (long long) endCentDir->offset, + (long long) endCentDir->size, (long long) endCentDir->size, + (long long) endCentDir->count); + break; + } + } + if (ecdrOffset == fsize) + { + append_msg (CMSG_ERROR, + GTXT ("%s: cannot find the central directory record"), name); + return 0; + } + if (endCentDir->count == 0xffff || endCentDir->offset == 0xffffffff + || endCentDir->size == 0xffffffff) + { + // Zip64 format: + // Zip64 end of central directory record + // Zip64 end of central directory locator ( Can be absent ) + // End of central directory record + b = (unsigned char *) dwin->bind (ecdrOffset - ZIP64_LOCATOR_SIZE, + ZIP64_LOCATOR_SIZE); + if (b == NULL) + { + append_msg (CMSG_ERROR, + GTXT ("%s: cannot find the Zip64 central directory record"), name); + return 0; + } + uint32_t signature = get_u4 (b); + if (signature == 0x07064b50) + { // Get an offset from the Zip64 cent-dir locator + // Zip64 end of central directory locator + // Offset Bytes Description + // 0 4 Zip64 end of central dir locator signature = 0x07064b50 + // 4 4 number of the disk with the start of the zip64 end of central directory + // 8 8 relative offset of the Zip64 end of central directory record + // 12 4 total number of disks + Dprintf (DUMP_JAR_FILE, " cent-dir locator offset %10lld (0x%016llx)\n", + (long long) (ecdrOffset - ZIP64_LOCATOR_SIZE), (long long) (ecdrOffset - ZIP64_LOCATOR_SIZE)); + ecdrOffset = get_u8 (b + 8); + } + else // the Zip64 end of central directory locator is absent + ecdrOffset -= ZIP64_CENT_DIR_SIZE; + Dprintf (DUMP_JAR_FILE, NTXT (" Zip64 end-cent-dir record offset: %10lld (0x%016llx)\n"), + (long long) ecdrOffset, (long long) ecdrOffset); + + b = (unsigned char *) dwin->bind (ecdrOffset, ZIP64_CENT_DIR_SIZE); + if (b == NULL) + { + append_msg (CMSG_ERROR, + GTXT ("%s: cannot find the Zip64 central directory record"), name); + return 0; + } + // Zip64 end of central directory record + // Offset Bytes Description + // 0 4 Zip64 end of central dir signature = 0x06064b50 + // 4 8 size of zip64 end of central directory record + // 12 2 version made by + // 14 2 version needed to extract + // 16 4 number of this disk + // 20 4 number of the disk with the start of the central directory + // 24 8 total number of entries in the central directory on this disk + // 32 8 total number of entries in the central directory + // 40 8 size of the central directory + // 48 8 offset of start of centraldirectory with respect to the starting disk number + // 56 Zip64 extensible data sector (variable size) + signature = get_u4 (b); + if (signature != 0x06064b50) + { + append_msg (CMSG_ERROR, GTXT ("%s: cannot find the Zip64 central directory record"), name); + return 0; + } + endCentDir->count = get_u8 (b + 32); + endCentDir->size = get_u8 (b + 40); + endCentDir->offset = get_u8 (b + 48); + Dprintf (DUMP_JAR_FILE, + NTXT (" cent-dir offset: %10lld (0x%016llx)\n" + " cent-dir size: %10lld (0x%016llx)\n" + " cent-dir entries: %10lld\n"), + (long long) endCentDir->offset, (long long) endCentDir->offset, + (long long) endCentDir->size, (long long) endCentDir->size, + (long long) endCentDir->count); + } + return 1; +} + diff --git a/gprofng/src/DbeJarFile.h b/gprofng/src/DbeJarFile.h new file mode 100644 index 0000000..f0b4f7b --- /dev/null +++ b/gprofng/src/DbeJarFile.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_JAR_FILE_H +#define _DBE_JAR_FILE_H + +#include "Emsg.h" + +class Data_window; +class ZipEntry; +template <class ITEM> class Vector; + +class DbeJarFile : public DbeMessages +{ +public: + DbeJarFile (const char *jarName); + ~DbeJarFile (); + + int get_entry (const char *fname); + long long copy (char *toFileNname, int fromEntryNum); + +private: + void get_entries (); + int get_EndCentDir (struct EndCentDir *endCentDir); + char *name; + Vector<ZipEntry *> *fnames; + Data_window *dwin; +}; +#endif /* _DBE_JAR_FILE_H */ diff --git a/gprofng/src/DbeLinkList.h b/gprofng/src/DbeLinkList.h new file mode 100644 index 0000000..760cc67 --- /dev/null +++ b/gprofng/src/DbeLinkList.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DbeLinkList_h +#define _DbeLinkList_h + +template <typename ITEM> class DbeLinkList +{ +public: + + DbeLinkList (ITEM _item) + { + item = _item; + next = NULL; + }; + + ~DbeLinkList () { }; + + ITEM + get_item () + { + return item; + } + + DbeLinkList<ITEM> * + get_next () + { + return next; + } + + void + set_next (DbeLinkList<ITEM> *p) + { + next = p; + } + + void + destroy (bool deleteItem = false) + { + for (DbeLinkList<ITEM> *p = next; p;) + { + DbeLinkList<ITEM> *p1 = p->get_next (); + if (deleteItem) + delete p->get_item (); + delete p; + p = p1; + } + next = NULL; + } + +private: + ITEM item; + DbeLinkList<ITEM> *next; +}; + +#endif /* _DbeLinkList_h */ diff --git a/gprofng/src/DbeLock.cc b/gprofng/src/DbeLock.cc new file mode 100644 index 0000000..14a1eb3 --- /dev/null +++ b/gprofng/src/DbeLock.cc @@ -0,0 +1,41 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "DbeLock.h" + +DbeLock::DbeLock () +{ + pthread_mutex_init (&lock, NULL); +} + +DbeLock::~DbeLock () { } + +void +DbeLock::aquireLock () +{ + pthread_mutex_lock (&lock); +} + +void +DbeLock::releaseLock () +{ + pthread_mutex_unlock (&lock); +} diff --git a/gprofng/src/DbeLock.h b/gprofng/src/DbeLock.h new file mode 100644 index 0000000..28dfb6e --- /dev/null +++ b/gprofng/src/DbeLock.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_LOCK_H +#define _DBE_LOCK_H + +#include <pthread.h> + +class DbeLock +{ +public: + DbeLock (); + ~DbeLock (); + void aquireLock (); + void releaseLock (); + +private: + pthread_mutex_t lock; +}; + +#endif /* _DBE_LOCK_H */ diff --git a/gprofng/src/DbeSession.cc b/gprofng/src/DbeSession.cc new file mode 100644 index 0000000..4370114 --- /dev/null +++ b/gprofng/src/DbeSession.cc @@ -0,0 +1,3527 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/types.h> +#include <errno.h> +#include <sys/param.h> + +#include "util.h" +#include "Application.h" +#include "Experiment.h" +#include "ExpGroup.h" +#include "Expression.h" +#include "DataObject.h" +#include "Elf.h" +#include "Function.h" +#include "DbeSession.h" +#include "LoadObject.h" +#include "DbeSyncMap.h" +#include "DbeThread.h" +#include "ClassFile.h" +#include "IndexObject.h" +#include "PathTree.h" +#include "Print.h" +#include "QLParser.tab.hh" +#include "DbeView.h" +#include "MemorySpace.h" +#include "Module.h" +#include "SourceFile.h" +#include "StringBuilder.h" +#include "BaseMetric.h" +#include "BaseMetricTreeNode.h" +#include "Command.h" +#include "UserLabel.h" +#include "StringMap.h" +#include "DbeFile.h" +#include "DbeJarFile.h" +#include "IOActivity.h" +#include "HeapActivity.h" + +// This is a universal List structure to organize objects +// of various types, even if different. +struct List +{ + List *next; + void *val; +}; + +struct Countable +{ + Countable (void *_item) + { + item = _item; + ref_count = 0; + } + + void *item; + int ref_count; +}; + +Platform_t DbeSession::platform = +#if ARCH(SPARC) + Sparc; +#elif ARCH(Aarch64) + Aarch64; +#else // ARCH(Intel) + Intel; +#endif + +// This constant determines the size of the data object name hash table. +static const int HTableSize = 8192; +static int DEFAULT_TINY_THRESHOLD = -1; + +unsigned int mpmt_debug_opt = 0; +DbeSession *dbeSession = NULL; + +DbeSession::DbeSession (Settings *_settings, bool _ipc_mode, bool _rdt_mode) +{ + dbeSession = this; + ipc_mode = _ipc_mode; + rdt_mode = _rdt_mode; + settings = new Settings (_settings); + views = new Vector<DbeView*>; + exps = new Vector<Experiment*>; + lobjs = new Vector<LoadObject*>; + objs = new Vector<Histable*>; + dobjs = new Vector<DataObject*>; + metrics = new Vector<Countable*>; + reg_metrics = new Vector<BaseMetric*>; + hwcentries = NULL; + reg_metrics_tree = NULL; // BaseMetric() requires DbeSession::ql_parse + idxobjs = new Vector<HashMap<uint64_t, Histable*>*>; + tmp_files = new Vector<char*>; + search_path = new Vector<char*>; + classpath = new Vector<char*>; + classpath_df = NULL; + expGroups = new Vector<ExpGroup*>; + sourcesMap = new HashMap<char*, SourceFile*>; + sources = new Vector<SourceFile*>; + comp_lobjs = new HashMap<char*, LoadObject*>; + comp_dbelines = new HashMap<char*, DbeLine*>; + comp_sources = new HashMap<char*, SourceFile*>; + loadObjMap = new DbeSyncMap<LoadObject>; + f_special = new Vector<Function*>(LastSpecialFunction); + omp_functions = new Vector<Function*>(OMP_LAST_STATE); + interactive = false; + lib_visibility_used = false; + + // Define all known property names + propNames = new Vector<PropDescr*>; + propNames_name_store (PROP_NONE, NTXT ("")); + propNames_name_store (PROP_ATSTAMP, NTXT ("ATSTAMP")); + propNames_name_store (PROP_ETSTAMP, NTXT ("ETSTAMP")); + propNames_name_store (PROP_TSTAMP, NTXT ("TSTAMP")); + propNames_name_store (PROP_THRID, NTXT ("THRID")); + propNames_name_store (PROP_LWPID, NTXT ("LWPID")); + propNames_name_store (PROP_CPUID, NTXT ("CPUID")); + propNames_name_store (PROP_FRINFO, NTXT ("FRINFO")); + propNames_name_store (PROP_EVT_TIME, NTXT ("EVT_TIME")); + + // Samples + propNames_name_store (PROP_SMPLOBJ, NTXT ("SMPLOBJ")); + propNames_name_store (PROP_SAMPLE, NTXT ("SAMPLE")); + + // GCEvents + propNames_name_store (PROP_GCEVENTOBJ, NTXT ("GCEVENTOBJ")); + propNames_name_store (PROP_GCEVENT, NTXT ("GCEVENT")); + + // Metadata used by some packet types + propNames_name_store (PROP_VOIDP_OBJ, NTXT ("VOIDP_OBJ"), + NULL, TYPE_UINT64, DDFLAG_NOSHOW); + + // Clock profiling properties + propNames_name_store (PROP_UCPU, NTXT ("UCPU")); + propNames_name_store (PROP_SCPU, NTXT ("SCPU")); + propNames_name_store (PROP_TRAP, NTXT ("TRAP")); + propNames_name_store (PROP_TFLT, NTXT ("TFLT")); + propNames_name_store (PROP_DFLT, NTXT ("DFLT")); + propNames_name_store (PROP_KFLT, NTXT ("KFLT")); + propNames_name_store (PROP_ULCK, NTXT ("ULCK")); + propNames_name_store (PROP_TSLP, NTXT ("TSLP")); + propNames_name_store (PROP_WCPU, NTXT ("WCPU")); + propNames_name_store (PROP_TSTP, NTXT ("TSTP")); + + propNames_name_store (PROP_MSTATE, NTXT ("MSTATE")); + propNames_name_store (PROP_NTICK, NTXT ("NTICK")); + propNames_name_store (PROP_OMPSTATE, NTXT ("OMPSTATE")); + + // Synchronization tracing properties + propNames_name_store (PROP_SRQST, NTXT ("SRQST")); + propNames_name_store (PROP_SOBJ, NTXT ("SOBJ")); + + // Hardware counter profiling properties + propNames_name_store (PROP_HWCTAG, NTXT ("HWCTAG")); + propNames_name_store (PROP_HWCINT, NTXT ("HWCINT")); + propNames_name_store (PROP_VADDR, NTXT ("VADDR")); + propNames_name_store (PROP_PADDR, NTXT ("PADDR")); + propNames_name_store (PROP_VIRTPC, NTXT ("VIRTPC")); + propNames_name_store (PROP_PHYSPC, NTXT ("PHYSPC")); + propNames_name_store (PROP_LWP_LGRP_HOME, NTXT ("LWP_LGRP_HOME")); + propNames_name_store (PROP_PS_LGRP_HOME, NTXT ("PS_LGRP_HOME")); + propNames_name_store (PROP_EA_PAGESIZE, NTXT ("EA_PAGESIZE")); + propNames_name_store (PROP_EA_LGRP, NTXT ("EA_LGRP")); + propNames_name_store (PROP_PC_PAGESIZE, NTXT ("PC_PAGESIZE")); + propNames_name_store (PROP_PC_LGRP, NTXT ("PC_LGRP")); + propNames_name_store (PROP_HWCDOBJ, NTXT ("HWCDOBJ")); + propNames_name_store (PROP_MEM_LAT, NTXT ("MEM_LAT")); + propNames_name_store (PROP_MEM_SRC, NTXT ("MEM_SRC")); + + // Heap tracing properties + propNames_name_store (PROP_HTYPE, NTXT ("HTYPE")); + propNames_name_store (PROP_HSIZE, NTXT ("HSIZE")); + propNames_name_store (PROP_HVADDR, NTXT ("HVADDR")); + propNames_name_store (PROP_HOVADDR, NTXT ("HOVADDR")); + propNames_name_store (PROP_HLEAKED, NTXT ("HLEAKED"), + GTXT ("Leaked bytes"), TYPE_UINT64, 0); + propNames_name_store (PROP_HMEM_USAGE, NTXT ("HMEM_USAGE")); + propNames_name_store (PROP_HFREED, NTXT ("HFREED"), + GTXT ("Freed bytes"), TYPE_UINT64, 0); + propNames_name_store (PROP_HCUR_ALLOCS, NTXT ("HCUR_ALLOCS"), + GTXT ("Current allocations"), TYPE_INT64, 0); + propNames_name_store (PROP_HCUR_NET_ALLOC, NTXT ("HCUR_NET_ALLOC"), + NULL, TYPE_INT64, DDFLAG_NOSHOW); + propNames_name_store (PROP_HCUR_LEAKS, NTXT ("HCUR_LEAKS"), + GTXT ("Current leaks"), TYPE_UINT64, 0); + propNames_name_store (PROP_DDSCR_LNK, NTXT ("DDSCR_LNK"), + NULL, TYPE_UINT64, DDFLAG_NOSHOW); + + // IO tracing properties + propNames_name_store (PROP_IOTYPE, NTXT ("IOTYPE")); + propNames_name_store (PROP_IOFD, NTXT ("IOFD")); + propNames_name_store (PROP_IONBYTE, NTXT ("IONBYTE")); + propNames_name_store (PROP_IORQST, NTXT ("IORQST")); + propNames_name_store (PROP_IOOFD, NTXT ("IOOFD")); + propNames_name_store (PROP_IOFNAME, NTXT ("IOFNAME")); + propNames_name_store (PROP_IOVFD, NTXT ("IOVFD")); + propNames_name_store (PROP_IOFSTYPE, NTXT ("IOFSTYPE")); + + // omptrace raw properties + propNames_name_store (PROP_CPRID, NTXT ("CPRID")); + propNames_name_store (PROP_PPRID, NTXT ("PPRID")); + propNames_name_store (PROP_TSKID, NTXT ("TSKID")); + propNames_name_store (PROP_PTSKID, NTXT ("PTSKID")); + propNames_name_store (PROP_PRPC, NTXT ("PRPC")); + + // Data race detection properties + propNames_name_store (PROP_RID, NTXT ("RID")); + propNames_name_store (PROP_RTYPE, NTXT ("RTYPE")); + propNames_name_store (PROP_LEAFPC, NTXT ("LEAFPC")); + propNames_name_store (PROP_RVADDR, NTXT ("RVADDR")); + propNames_name_store (PROP_RCNT, NTXT ("RCNT")); + + // Deadlock detection properties + propNames_name_store (PROP_DID, NTXT ("DID")); + propNames_name_store (PROP_DLTYPE, NTXT ("DLTYPE")); + propNames_name_store (PROP_DTYPE, NTXT ("DTYPE")); + propNames_name_store (PROP_DVADDR, NTXT ("DVADDR")); + + // Synthetic properties (queries only) + propNames_name_store (PROP_STACK, NTXT ("STACK")); + propNames_name_store (PROP_MSTACK, NTXT ("MSTACK")); + propNames_name_store (PROP_USTACK, NTXT ("USTACK")); + propNames_name_store (PROP_XSTACK, NTXT ("XSTACK")); + propNames_name_store (PROP_HSTACK, NTXT ("HSTACK")); + propNames_name_store (PROP_STACKID, NTXT ("STACKID")); + //propNames_name_store( PROP_CPRID, NTXT("CPRID") ); + //propNames_name_store( PROP_TSKID, NTXT("TSKID") ); + propNames_name_store (PROP_JTHREAD, NTXT ("JTHREAD"), + GTXT ("Java thread number"), TYPE_UINT64, 0); + + propNames_name_store (PROP_LEAF, NTXT ("LEAF")); + propNames_name_store (PROP_DOBJ, NTXT ("DOBJ")); + propNames_name_store (PROP_SAMPLE_MAP, NTXT ("SAMPLE_MAP")); + propNames_name_store (PROP_GCEVENT_MAP, NTXT ("GCEVENT_MAP")); + propNames_name_store (PROP_PID, NTXT ("PID"), + GTXT ("Process id"), TYPE_UINT64, 0); + propNames_name_store (PROP_EXPID, NTXT ("EXPID"), + GTXT ("Experiment id"), TYPE_UINT64, DDFLAG_NOSHOW); + propNames_name_store (PROP_EXPID_CMP, NTXT ("EXPID_CMP"), + GTXT ("Comparable Experiment Id"), TYPE_UINT64, + DDFLAG_NOSHOW); //YXXX find better description + propNames_name_store (PROP_EXPGRID, NTXT ("EXPGRID"), + GTXT ("Comparison Group id"), TYPE_UINT64, 0); + propNames_name_store (PROP_PARREG, NTXT ("PARREG")); + propNames_name_store (PROP_TSTAMP_LO, NTXT ("TSTAMP_LO"), + GTXT ("Start Timestamp (nanoseconds)"), TYPE_UINT64, 0); + propNames_name_store (PROP_TSTAMP_HI, NTXT ("TSTAMP_HI"), + GTXT ("End Timestamp (nanoseconds)"), TYPE_UINT64, 0); + propNames_name_store (PROP_TSTAMP2, NTXT ("TSTAMP2"), + GTXT ("End Timestamp (nanoseconds)"), TYPE_UINT64, + DDFLAG_NOSHOW); + propNames_name_store (PROP_FREQ_MHZ, NTXT ("FREQ_MHZ"), + GTXT ("CPU Frequency, MHz"), TYPE_UINT32, 0); + propNames_name_store (PROP_NTICK_USEC, NTXT ("NTICK_USEC"), + GTXT ("Clock Profiling Interval, Microseconds"), + TYPE_UINT64, 0); + + propNames_name_store (PROP_IOHEAPBYTES, NTXT ("IOHEAPBYTES")); + + propNames_name_store (PROP_STACKL, NTXT ("STACKL")); + propNames_name_store (PROP_MSTACKL, NTXT ("MSTACKL")); + propNames_name_store (PROP_USTACKL, NTXT ("USTACKL")); + propNames_name_store (PROP_XSTACKL, NTXT ("XSTACKL")); + + propNames_name_store (PROP_STACKI, NTXT ("STACKI")); + propNames_name_store (PROP_MSTACKI, NTXT ("MSTACKI")); + propNames_name_store (PROP_USTACKI, NTXT ("USTACKI")); + propNames_name_store (PROP_XSTACKI, NTXT ("XSTACKI")); + + // Make sure predefined names are not used for dynamic properties + propNames_name_store (PROP_LAST, NTXT ("")); + + localized_SP_UNKNOWN_NAME = GTXT ("(unknown)"); + + // define Index objects + dyn_indxobj = new Vector<IndexObjType_t*>(); + dyn_indxobj_indx = 0; + char *s = dbe_sprintf (NTXT ("((EXPID_CMP<<%llu) | THRID)"), + (unsigned long long) IndexObject::INDXOBJ_EXPID_SHIFT); + indxobj_define (NTXT ("Threads"), GTXT ("Threads"), s, NULL, NULL); + free (s); + indxobj_define (NTXT ("CPUs"), GTXT ("CPUs"), NTXT ("(CPUID)"), NULL, NULL); + indxobj_define (NTXT ("Samples"), GTXT ("Samples"), NTXT ("(SAMPLE_MAP)"), + NULL, NULL); + indxobj_define (NTXT ("GCEvents"), GTXT ("GCEvents"), NTXT ("(GCEVENT_MAP)"), + NULL, NULL); + indxobj_define (NTXT ("Seconds"), GTXT ("Seconds"), + NTXT ("(TSTAMP/1000000000)"), NULL, NULL); + indxobj_define (NTXT ("Processes"), GTXT ("Processes"), NTXT ("(EXPID_CMP)"), + NULL, NULL); + s = dbe_sprintf (NTXT ("((EXPGRID<<%llu) | (EXPID<<%llu))"), + (unsigned long long) IndexObject::INDXOBJ_EXPGRID_SHIFT, + (unsigned long long) IndexObject::INDXOBJ_EXPID_SHIFT); + indxobj_define (NTXT ("Experiment_IDs"), GTXT ("Experiment_IDs"), s, NULL, NULL); + free (s); + indxobj_define (NTXT ("Datasize"), GTXT ("Datasize"), + "(IOHEAPBYTES==0?0:" + "((IOHEAPBYTES<=(1<<0)?(1<<0):" + "((IOHEAPBYTES<=(1<<2)?(1<<2):" + "((IOHEAPBYTES<=(1<<4)?(1<<4):" + "((IOHEAPBYTES<=(1<<6)?(1<<6):" + "((IOHEAPBYTES<=(1<<8)?(1<<8):" + "((IOHEAPBYTES<=(1<<10)?(1<<10):" + "((IOHEAPBYTES<=(1<<12)?(1<<12):" + "((IOHEAPBYTES<=(1<<14)?(1<<14):" + "((IOHEAPBYTES<=(1<<16)?(1<<16):" + "((IOHEAPBYTES<=(1<<18)?(1<<18):" + "((IOHEAPBYTES<=(1<<20)?(1<<20):" + "((IOHEAPBYTES<=(1<<22)?(1<<22):" + "((IOHEAPBYTES<=(1<<24)?(1<<24):" + "((IOHEAPBYTES<=(1<<26)?(1<<26):" + "((IOHEAPBYTES<=(1<<28)?(1<<28):" + "((IOHEAPBYTES<=(1<<30)?(1<<30):" + "((IOHEAPBYTES<=(1<<32)?(1<<32):" + "((IOHEAPBYTES<=(1<<34)?(1<<34):" + "((IOHEAPBYTES<=(1<<36)?(1<<36):" + "((IOHEAPBYTES<=(1<<38)?(1<<38):" + "((IOHEAPBYTES<=(1<<40)?(1<<40):" + "((IOHEAPBYTES<=(1<<42)?(1<<42):" + "((IOHEAPBYTES<=(1<<44)?(1<<44):" + "((IOHEAPBYTES<=(1<<46)?(1<<46):" + "((IOHEAPBYTES<=(1<<48)?(1<<48):" + "((IOHEAPBYTES<=(1<<50)?(1<<50):" + "(IOHEAPBYTES==-1?-1:(1<<50|1)" + "))))))))))))))))))))))))))))))))))))))))))))))))))))))", + NULL, NULL); + indxobj_define (NTXT ("Duration"), GTXT ("Duration"), + "((TSTAMP_HI-TSTAMP_LO)==0?0:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000?1000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000?10000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000?100000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000?1000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000?10000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000000?100000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000000?1000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000000?10000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000000000?100000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000000000?1000000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000000000?10000000000000:" + "(10000000000001))))))))))))))))))))))))", NULL, NULL); + dyn_indxobj_indx_fixed = dyn_indxobj_indx; + Elf::elf_init (); + defExpName = NULL; + mach_model_loaded = NULL; + tmp_dir_name = NULL; + settings->read_rc (ipc_mode || rdt_mode); + + init (); +} + +DbeSession::~DbeSession () +{ + Destroy (views); + Destroy (exps); + Destroy (dobjs); + Destroy (metrics); + Destroy (search_path); + Destroy (classpath); + Destroy (propNames); + Destroy (expGroups); + Destroy (userLabels); + if (hwcentries) + { + for (long i = 0, sz = hwcentries->size (); i < sz; i++) + { + Hwcentry *h = hwcentries->get (i); + free (h->int_name); + free (h->name); + delete h; + } + delete hwcentries; + } + + if (idxobjs) + { + for (int i = 0; i < idxobjs->size (); ++i) + { + HashMap<uint64_t, Histable*> *hMap = idxobjs->get (i); + if (hMap) + { + hMap->values ()->destroy (); + delete hMap; + } + } + delete idxobjs; + } + + for (int i = 0; i < HTableSize; i++) + { + List *list = dnameHTable[i]; + while (list) + { + List *tmp = list; + list = list->next; + delete tmp; + } + } + delete[] dnameHTable; + delete classpath_df; + Destroy (objs); + Destroy (reg_metrics); + Destroy (dyn_indxobj); + delete lobjs; + delete f_special; + destroy_map (DbeFile *, dbeFiles); + destroy_map (DbeJarFile *, dbeJarFiles); + delete loadObjMap; + delete omp_functions; + delete sourcesMap; + delete sources; + delete comp_lobjs; + delete comp_dbelines; + delete comp_sources; + delete reg_metrics_tree; + delete settings; + free (mach_model_loaded); + + if (defExpName != NULL) + { + StringBuilder *sb = new StringBuilder (); + sb->append (NTXT ("/bin/rm -rf ")); + sb->append (defExpName); + char *cmd = sb->toString (); + system (cmd); + free (cmd); + delete sb; + free (defExpName); + } + unlink_tmp_files (); + delete tmp_files; + dbeSession = NULL; +} + +void +DbeSession::unlink_tmp_files () +{ + if (tmp_files) + { + for (int i = 0, sz = tmp_files->size (); i < sz; i++) + unlink (tmp_files->fetch (i)); + tmp_files->destroy (); + delete tmp_files; + tmp_files = NULL; + } + if (tmp_dir_name) + { + char *cmd = dbe_sprintf (NTXT ("/bin/rm -rf %s"), tmp_dir_name); + system (cmd); + free (cmd); + free (tmp_dir_name); + tmp_dir_name = NULL; + } +} + +char * +DbeSession::get_tmp_file_name (const char *nm, bool for_java) +{ + if (tmp_dir_name == NULL) + { + tmp_dir_name = dbe_sprintf (NTXT ("/tmp/analyzer.%llu.%lld"), + (unsigned long long) getuid (), (long long) getpid ()); + mkdir (tmp_dir_name, S_IRWXU); + } + char *fnm = dbe_sprintf (NTXT ("%s/%s"), tmp_dir_name, nm); + if (for_java) + for (char *s = fnm + strlen (tmp_dir_name) + 1; *s; s++) + if (*s == '/') + *s = '.'; + return fnm; +} + +void +DbeSession::init () +{ + user_exp_id_counter = 0; + status_ompavail = 0; + archive_mode = 0; + +#if DEBUG + char *s = getenv (NTXT ("MPMT_DEBUG")); + if (s) + mpmt_debug_opt = atoi (s); +#endif /* DEBUG */ + dbeFiles = new StringMap<DbeFile*>(); + dbeJarFiles = new StringMap<DbeJarFile*>(128, 128); + + // set up the initial (after .rc file reading) search path + set_search_path (settings->str_search_path, true); + userLabels = NULL; + + // Preset all objects as they may reuse each other + lo_unknown = NULL; + f_unknown = NULL; + j_unknown = NULL; + lo_total = NULL; + sf_unknown = NULL; + f_total = NULL; + f_jvm = NULL; + d_total = NULL; + d_scalars = NULL; + d_unknown = NULL; + expGroups->destroy (); + f_special->reset (); + for (int i = 0; i < LastSpecialFunction; i++) + f_special->append (NULL); + + lo_omp = NULL; + omp_functions->reset (); + for (int i = 0; i < OMP_LAST_STATE; i++) + omp_functions->append (NULL); + + // make sure the metric list is initialized + register_metric (Metric::SIZES); + register_metric (Metric::ADDRESS); + register_metric (Metric::ONAME); + + // This is needed only to maintain loadobject id's + // for <Total> and <Unknown> in tests + (void) get_Unknown_LoadObject (); + (void) get_Total_LoadObject (); + + // Create the data object name hash table. + dnameHTable = new List*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + dnameHTable[i] = NULL; + + d_total = createDataObject (); + d_total->set_name (NTXT ("<Total>")); + + // XXXX <Scalars> only appropriate for Program/Data-oriented analyses + d_scalars = createDataObject (); + d_scalars->set_name (GTXT ("<Scalars>")); + + d_unknown = createDataObject (); + d_unknown->set_name (GTXT ("<Unknown>")); + + // assign d_unknown's children so data_olayout has consistent sorting + for (unsigned pp_code = 1; pp_code < NUM_ABS_PP_CODES + 2; pp_code++) + { + char *errcode; + DataObject* dobj = createDataObject (); + switch (pp_code) + { + case NUM_ABS_PP_CODES + 1: + errcode = PTXT (DOBJ_UNDETERMINED); + break; + case NUM_ABS_PP_CODES: + errcode = PTXT (DOBJ_UNSPECIFIED); + break; + case NUM_ABS_PP_CODES - 1: + errcode = PTXT (DOBJ_UNIDENTIFIED); + break; + default: + errcode = PTXT (ABS_PP_CODES[pp_code]); + } + dobj->parent = d_unknown; + dobj->set_dobjname (errcode, NULL); // dobj->parent must already be set + } + + for (unsigned rt_code = 1; rt_code < NUM_ABS_RT_CODES - 1; rt_code++) + { + DataObject* dobj = createDataObject (); + dobj->parent = d_unknown; + dobj->set_dobjname (PTXT (ABS_RT_CODES[rt_code]), NULL); // dobj->parent must already be set + } +} + +void +DbeSession::reset_data () +{ + for (long i = 0, sz = VecSize (idxobjs); i < sz; ++i) + if (idxobjs->get (i)) + idxobjs->get (i)->reset (); +} + +void +DbeSession::reset () +{ + loadObjMap->reset (); + DbeView *dbev; + int index; + + Vec_loop (DbeView*, views, index, dbev) + { + dbev->reset (); + } + + destroy_map (DbeFile *, dbeFiles); + destroy_map (DbeJarFile *, dbeJarFiles); + exps->destroy (); + lobjs->reset (); // all LoadObjects belong to objs + dobjs->destroy (); // deletes d_unknown and d_total as well + objs->destroy (); + comp_lobjs->clear (); + comp_dbelines->clear (); + comp_sources->clear (); + sourcesMap->clear (); + sources->reset (); + + // Delete the data object name hash table. + for (int i = 0; i < HTableSize; i++) + { + List *list = dnameHTable[i]; + while (list) + { + List *tmp = list; + list = list->next; + delete tmp; + } + } + delete[] dnameHTable; + + // IndexObect definitions remain, objects themselves may go + for (int i = 0; i < idxobjs->size (); ++i) + { + HashMap<uint64_t, Histable*> *v = idxobjs->fetch (i); + if (v != NULL) + { + v->values ()->destroy (); + v->clear (); + } + } + init (); +} + +Vector<SourceFile*> * +DbeSession::get_sources () +{ + return sources; +} + +DbeFile * +DbeSession::getDbeFile (char *filename, int filetype) +{ + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeSession::getDbeFile filetype=0x%x %s\n"), filetype, filename); + if (strncmp (filename, NTXT ("./"), 2) == 0) + filename += 2; + DbeFile *dbeFile = dbeFiles->get (filename); + if (dbeFile == NULL) + { + dbeFile = new DbeFile (filename); + dbeFiles->put (filename, dbeFile); + } + dbeFile->filetype |= filetype; + return dbeFile; +} + +LoadObject * +DbeSession::get_Total_LoadObject () +{ + if (lo_total == NULL) + { + lo_total = createLoadObject (NTXT ("<Total>")); + lo_total->dbeFile->filetype |= DbeFile::F_FICTION; + } + return lo_total; +} + +Function * +DbeSession::get_Total_Function () +{ + if (f_total == NULL) + { + f_total = createFunction (); + f_total->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + f_total->set_name (NTXT ("<Total>")); + Module *mod = get_Total_LoadObject ()->noname; + f_total->module = mod; + mod->functions->append (f_total); + } + return f_total; +} + +LoadObject * +DbeSession::get_Unknown_LoadObject () +{ + if (lo_unknown == NULL) + { + lo_unknown = createLoadObject (GTXT ("<Unknown>")); + lo_unknown->type = LoadObject::SEG_TEXT; // makes it expandable + lo_unknown->dbeFile->filetype |= DbeFile::F_FICTION; + + // force creation of the <Unknown> function + (void) get_Unknown_Function (); + } + return lo_unknown; +} + +SourceFile * +DbeSession::get_Unknown_Source () +{ + if (sf_unknown == NULL) + { + sf_unknown = createSourceFile (localized_SP_UNKNOWN_NAME); + sf_unknown->dbeFile->filetype |= DbeFile::F_FICTION; + sf_unknown->flags |= SOURCE_FLAG_UNKNOWN; + } + return sf_unknown; +} + +Function * +DbeSession::get_Unknown_Function () +{ + if (f_unknown == NULL) + { + f_unknown = createFunction (); + f_unknown->flags |= FUNC_FLAG_SIMULATED; + f_unknown->set_name (GTXT ("<Unknown>")); + Module *mod = get_Unknown_LoadObject ()->noname; + f_unknown->module = mod; + mod->functions->append (f_unknown); + } + return f_unknown; +} + +// LIBRARY_VISIBILITY + +Function * +DbeSession::create_hide_function (LoadObject *lo) +{ + Function *h_function = createFunction (); + h_function->set_name (lo->get_name ()); + h_function->module = lo->noname; + h_function->isHideFunc = true; + lo->noname->functions->append (h_function); + return h_function; +} + +Function * +DbeSession::get_JUnknown_Function () +{ + if (j_unknown == NULL) + { + j_unknown = createFunction (); + j_unknown->flags |= FUNC_FLAG_SIMULATED; + j_unknown->set_name (GTXT ("<no Java callstack recorded>")); + Module *mod = get_Unknown_LoadObject ()->noname; + j_unknown->module = mod; + mod->functions->append (j_unknown); + } + return j_unknown; +} + +Function * +DbeSession::get_jvm_Function () +{ + if (f_jvm == NULL) + { + f_jvm = createFunction (); + f_jvm->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + f_jvm->set_name (GTXT ("<JVM-System>")); + + // Find the JVM LoadObject + LoadObject *jvm = get_Unknown_LoadObject (); + for (int i = 0; i < lobjs->size (); ++i) + { + LoadObject *lo = lobjs->fetch (i); + if (lo->flags & SEG_FLAG_JVM) + { + jvm = lo; + break; + } + } + Module *mod = jvm->noname; + f_jvm->module = mod; + mod->functions->append (f_jvm); + // XXXX is it required? no consistency among all special functions + // jvm->functions->append( f_jvm ); + } + return f_jvm; +} + +Function * +DbeSession::getSpecialFunction (SpecialFunction kind) +{ + if (kind < 0 || kind >= LastSpecialFunction) + return NULL; + + Function *func = f_special->fetch (kind); + if (func == NULL) + { + char *fname; + switch (kind) + { + case TruncatedStackFunc: + fname = GTXT ("<Truncated-stack>"); + break; + case FailedUnwindFunc: + fname = GTXT ("<Stack-unwind-failed>"); + break; + default: + return NULL; + } + func = createFunction (); + func->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + Module *mod = get_Total_LoadObject ()->noname; + func->module = mod; + mod->functions->append (func); + func->set_name (fname); + f_special->store (kind, func); + } + return func; +} + +LoadObject * +DbeSession::get_OMP_LoadObject () +{ + if (lo_omp == NULL) + { + for (int i = 0, sz = lobjs->size (); i < sz; i++) + { + LoadObject *lo = lobjs->fetch (i); + if (lo->flags & SEG_FLAG_OMP) + { + lo_omp = lo; + return lo_omp; + } + } + lo_omp = createLoadObject (GTXT ("<OMP>")); + lo_omp->type = LoadObject::SEG_TEXT; + lo_omp->dbeFile->filetype |= DbeFile::F_FICTION; + } + return lo_omp; +} + +Function * +DbeSession::get_OMP_Function (int n) +{ + if (n < 0 || n >= OMP_LAST_STATE) + return NULL; + + Function *func = omp_functions->fetch (n); + if (func == NULL) + { + char *fname; + switch (n) + { + case OMP_OVHD_STATE: + fname = GTXT ("<OMP-overhead>"); + break; + case OMP_IDLE_STATE: + fname = GTXT ("<OMP-idle>"); + break; + case OMP_RDUC_STATE: + fname = GTXT ("<OMP-reduction>"); + break; + case OMP_IBAR_STATE: + fname = GTXT ("<OMP-implicit_barrier>"); + break; + case OMP_EBAR_STATE: + fname = GTXT ("<OMP-explicit_barrier>"); + break; + case OMP_LKWT_STATE: + fname = GTXT ("<OMP-lock_wait>"); + break; + case OMP_CTWT_STATE: + fname = GTXT ("<OMP-critical_section_wait>"); + break; + case OMP_ODWT_STATE: + fname = GTXT ("<OMP-ordered_section_wait>"); + break; + case OMP_ATWT_STATE: + fname = GTXT ("<OMP-atomic_wait>"); + break; + default: + return NULL; + } + func = createFunction (); + func->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + func->set_name (fname); + + LoadObject *omp = get_OMP_LoadObject (); + func->module = omp->noname; + omp->noname->functions->append (func); + omp->functions->append (func); + omp_functions->store (n, func); + } + return func; +} + +// Divide the original createExperiment() into two steps +// In part1, we just create the data structure, in part2, if +// we decide to keep the experiment around, add it to various +// lists in DbeSession +Experiment * +DbeSession::createExperimentPart1 () +{ + Experiment *exp = new Experiment (); + return exp; +} + +void +DbeSession::createExperimentPart2 (Experiment *exp) +{ + int ind = expGroups->size (); + if (ind > 0) + { + ExpGroup *gr = expGroups->fetch (ind - 1); + exp->groupId = gr->groupId; + gr->append (exp); + } + exp->setExpIdx (exps->size ()); + exp->setUserExpId (++user_exp_id_counter); + exps->append (exp); +} + +Experiment * +DbeSession::createExperiment () +{ + Experiment *exp = new Experiment (); + append (exp); + return exp; +} + +void +DbeSession::append (Experiment *exp) +{ + exp->setExpIdx (exps->size ()); + exp->setUserExpId (++user_exp_id_counter); + exps->append (exp); + if (exp->founder_exp) + { + if (exp->founder_exp->children_exps == NULL) + exp->founder_exp->children_exps = new Vector<Experiment *>; + exp->founder_exp->children_exps->append (exp); + if (exp->founder_exp->groupId > 0) + { + exp->groupId = exp->founder_exp->groupId; + expGroups->get (exp->groupId - 1)->append (exp); + } + } + if (exp->groupId == 0) + { + long ind = VecSize (expGroups); + if (ind > 0) + { + ExpGroup *gr = expGroups->get (ind - 1); + exp->groupId = gr->groupId; + gr->append (exp); + } + } +} + +void +DbeSession::append (Hwcentry *h) +{ + if (hwcentries == NULL) + hwcentries = new Vector<Hwcentry*>; + hwcentries->append (h); +} + +int +DbeSession::ngoodexps () +{ + return exps->size (); +} + +int +DbeSession::createView (int index, int cloneindex) +{ + // ensure that there is no view with that index + DbeView *dbev = getView (index); + if (dbev != NULL) + abort (); + + // find the view to be cloned + dbev = getView (cloneindex); + DbeView *newview; + if (dbev == NULL) + newview = new DbeView (theApplication, settings, index); + else + newview = new DbeView (dbev, index); + views->append (newview); + return index; +} + +DbeView * +DbeSession::getView (int index) +{ + int i; + DbeView *dbev; + Vec_loop (DbeView*, views, i, dbev) + { + if (dbev->vindex == index) + return dbev; + } + return NULL; +} + +void +DbeSession::dropView (int index) +{ + int i; + DbeView *dbev; + + Vec_loop (DbeView*, views, i, dbev) + { + if (dbev->vindex == index) + { + views->remove (i); + delete dbev; + return; + } + } + // view not found; ignore for now +} + +Vector<char*> * +DbeSession::get_group_or_expt (char *path) +{ + Vector<char*> *exp_list = new Vector<char*>; + FILE *fptr; + char *new_path, buf[MAXPATHLEN], name[MAXPATHLEN]; + + fptr = fopen (path, NTXT ("r")); + if (!fptr || !fgets (buf, (int) sizeof (buf), fptr) + || strncmp (buf, SP_GROUP_HEADER, strlen (SP_GROUP_HEADER))) + { + // it's not an experiment group + new_path = dbe_strdup (path); + new_path = canonical_path (new_path); + exp_list->append (new_path); + } + else + { + // it is an experiment group, read the list to get them all + while (fgets (buf, (int) sizeof (buf), fptr)) + { + if ((*buf != '#') && (sscanf (buf, NTXT ("%s"), name) == 1)) + { + new_path = dbe_strdup (name); + new_path = canonical_path (new_path); + exp_list->append (new_path); + } + } + } + if (fptr) + fclose (fptr); + return exp_list; +} + +#define GET_INT_VAL(v, s, len) \ + for (v = len = 0; isdigit(*s); s++, len++) { v = v * 10 + (*s -'0'); } + +static int +dir_name_cmp (const void *a, const void *b) +{ + char *s1 = *((char **) a); + char *s2 = *((char **) b); + while (*s1) + { + if (isdigit (*s1) && isdigit (*s2)) + { + int v1, v2, len1, len2; + GET_INT_VAL (v1, s1, len1); + GET_INT_VAL (v2, s2, len2); + if (v1 != v2) + return v1 - v2; + if (len1 != len2) + return len2 - len1; + continue; + } + if (*s1 != *s2) + break; + s1++; + s2++; + } + return *s1 - *s2; +} + +static int +read_experiment_data_in_parallel (void *arg) +{ + exp_ctx *ctx = (exp_ctx *) arg; + Experiment *dexp = ctx->exp; + bool read_ahead = ctx->read_ahead; + dexp->read_experiment_data (read_ahead); + free (ctx); + return 0; +} + +void +DbeSession::open_experiment (Experiment *exp, char *path) +{ + exp->open (path); + if (exp->get_status () != Experiment::FAILURE) + exp->read_experiment_data (false); + exp->open_epilogue (); + + // Update all DbeViews + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->add_experiment (exp->getExpIdx (), true); + } + + if (exp->get_status () == Experiment::FAILURE) + { + check_tab_avail (); + return; + } + + char *discard_tiny = getenv (NTXT ("SP_ANALYZER_DISCARD_TINY_EXPERIMENTS")); + int user_specified_tiny_threshold = DEFAULT_TINY_THRESHOLD; // in milliseconds + if (discard_tiny != NULL) + { + user_specified_tiny_threshold = (atoi (discard_tiny)); + if (user_specified_tiny_threshold < 0) + user_specified_tiny_threshold = DEFAULT_TINY_THRESHOLD; + } + + // Open descendant experiments + DIR *exp_dir = opendir (path); + if (exp_dir == NULL) + { + check_tab_avail (); + return; + } + + Vector<char*> *exp_names = new Vector<char*>(); + struct dirent *entry = NULL; + while ((entry = readdir (exp_dir)) != NULL) + { + if (entry->d_name[0] != '_') + continue; + size_t len = strlen (entry->d_name); + if (len < 3 || strcmp (entry->d_name + len - 3, NTXT (".er")) != 0) + continue; + exp_names->append (dbe_strdup (entry->d_name)); + } + closedir (exp_dir); + exp_names->sort (dir_name_cmp); + Experiment **t_exp_list = new Experiment *[exp_names->size ()]; + int nsubexps = 0; + + for (int j = 0, jsz = exp_names->size (); j < jsz; j++) + { + t_exp_list[j] = NULL; + + char *lineage_name = exp_names->fetch (j); + struct stat64 sbuf; + char *dpath = dbe_sprintf (NTXT ("%s/%s"), path, lineage_name); + + // look for experiments with no profile collected + if (user_specified_tiny_threshold == DEFAULT_TINY_THRESHOLD) + { + char *frinfoname = dbe_sprintf (NTXT ("%s/%s"), dpath, "data." SP_FRINFO_FILE); + int st = dbe_stat (frinfoname, &sbuf); + free (frinfoname); + if (st == 0) + { + // if no profile/trace data do not process this experiment any further + if (sbuf.st_size == 0) + { + free (dpath); + continue; + } + } + } + else + { // check if dpath is a directory + if (dbe_stat (dpath, &sbuf) != 0) + { + free (dpath); + continue; + } + else if (!S_ISDIR (sbuf.st_mode)) + { + free (dpath); + continue; + } + } + size_t lineage_name_len = strlen (lineage_name); + lineage_name[lineage_name_len - 3] = 0; /* remove .er */ + Experiment *dexp = new Experiment (); + dexp->founder_exp = exp; + if (user_specified_tiny_threshold > DEFAULT_TINY_THRESHOLD) + { + dexp->setTinyThreshold (user_specified_tiny_threshold); + dexp->open (dpath); + if (dexp->isDiscardedTinyExperiment ()) + { + delete dexp; + free (dpath); + continue; + } + } + else + dexp->open (dpath); + append (dexp); + t_exp_list[j] = dexp; + nsubexps++; + dexp->set_clock (exp->clock); + + // DbeView add_experiment() is split into two parts + // add_subexperiment() is called repeeatedly for + // all sub_experiments, later add_experiment_epilogue() finishes up the task + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + bool enabled = settings->check_en_desc (lineage_name, dexp->utargname); + dbev->add_subexperiment (dexp->getExpIdx (), enabled); + } + free (dpath); + } + + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->add_experiment_epilogue (); + } + + DbeThreadPool * threadPool = new DbeThreadPool (-1); + for (int j = 0, jsz = exp_names->size (); j < jsz; j++) + { + if (t_exp_list[j] == NULL) continue; + Experiment *dexp = t_exp_list[j]; + exp_ctx *new_ctx = (exp_ctx*) malloc (sizeof (exp_ctx)); + new_ctx->path = NULL; + new_ctx->exp = dexp; + new_ctx->ds = this; + new_ctx->read_ahead = true; + DbeQueue *q = new DbeQueue (read_experiment_data_in_parallel, new_ctx); + threadPool->put_queue (q); + } + threadPool->wait_queues (); + delete threadPool; + + for (long j = 0, jsz = exp_names->size (); j < jsz; j++) + { + if (t_exp_list[j] == NULL) continue; + Experiment *dexp = t_exp_list[j]; + dexp->open_epilogue (); + } + exp_names->destroy (); + delete[] t_exp_list; + delete exp_names; + + // update setting for leaklist and dataspace + check_tab_avail (); +} + +void +DbeSession::append_mesgs (StringBuilder *sb, char *path, Experiment *exp) +{ + if (exp->fetch_errors () != NULL) + { + // yes, there were errors + char *ststr = pr_mesgs (exp->fetch_errors (), NTXT (""), NTXT ("")); + sb->append (path); + sb->append (NTXT (": ")); + sb->append (ststr); + free (ststr); + } + + Emsg *m = exp->fetch_warnings (); + if (m != NULL) + { + sb->append (path); + sb->append (NTXT (": ")); + if (!is_interactive ()) + sb->append (GTXT ("Experiment has warnings, see header for details\n")); + else + sb->append (GTXT ("Experiment has warnings, see experiment panel for details\n")); + } + + // Check for descendant experiments that are not loaded + int num_desc = VecSize (exp->children_exps); + if ((num_desc > 0) && !settings->check_en_desc (NULL, NULL)) + { + char *s; + if (!is_interactive ()) + s = dbe_sprintf (GTXT ("Has %d descendant(s), use commands controlling selection to load descendant data\n"), num_desc); + else + s = dbe_sprintf (GTXT ("Has %d descendant(s), use filter panel to load descendant data\n"), num_desc); + sb->append (path); + sb->append (NTXT (": ")); + sb->append (s); + free (s); + } +} + +Experiment * +DbeSession::get_exp (int exp_ind) +{ + if (exp_ind < 0 || exp_ind >= exps->size ()) + return NULL; + Experiment *exp = exps->fetch (exp_ind); + exp->setExpIdx (exp_ind); + return exp; +} + +Vector<Vector<char*>*> * +DbeSession::getExperimensGroups () +{ + if (dbeSession->expGroups == NULL || dbeSession->expGroups->size () == 0) + return NULL; + bool compare_mode = expGroups->size () > 1; + Vector<Vector<char*>*> *groups = new Vector<Vector<char*>*> ( + compare_mode ? expGroups->size () : 1); + for (int i = 0; i < expGroups->size (); i++) + { + ExpGroup *grp = expGroups->fetch (i); + Vector<Experiment*> *founders = grp->get_founders (); + if (founders && founders->size () != 0) + { + Vector<char *> *names = new Vector<char*> (founders->size ()); + for (int j = 0; j < founders->size (); j++) + { + Experiment *exp = founders->fetch (j); + names->append (dbe_strdup (exp->get_expt_name ())); + } + if (compare_mode || groups->size () == 0) + groups->append (names); + else + groups->fetch (0)->addAll (names); + } + delete founders; + } + return groups; +} + +char * +DbeSession::setExperimentsGroups (Vector<Vector<char*>*> *groups) +{ + StringBuilder sb; + for (int i = 0; i < groups->size (); i++) + { + Vector<char *> *names = groups->fetch (i); + ExpGroup *grp; + if (names->size () == 1) + grp = new ExpGroup (names->fetch (0)); + else + { + char *nm = dbe_sprintf (GTXT ("Group %d"), i + 1); + grp = new ExpGroup (nm); + free (nm); + } + expGroups->append (grp); + grp->groupId = expGroups->size (); + + for (int j = 0; j < names->size (); j++) + { + char *path = names->fetch (j); + size_t len = strlen (path); + if ((len > 4) && !strcmp (path + len - 4, NTXT (".erg"))) + { + Vector<char*> *lst = get_group_or_expt (path); + for (int j1 = 0; j1 < lst->size (); j1++) + { + Experiment *exp = new Experiment (); + append (exp); + open_experiment (exp, lst->get (j1)); + if (exp->get_status () == Experiment::FAILURE) + append_mesgs (&sb, path, exp); + } + lst->destroy (); + delete lst; + } + else + { + Experiment *exp = new Experiment (); + append (exp); + open_experiment (exp, path); + if (exp->get_status () == Experiment::FAILURE) + append_mesgs (&sb, path, exp); + } + } + } + + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->update_advanced_filter (); + int cmp = dbev->get_settings ()->get_compare_mode (); + dbev->set_compare_mode (CMP_DISABLE); + dbev->set_compare_mode (cmp); + } + return sb.length () == 0 ? NULL : sb.toString (); +} + +char * +DbeSession::drop_experiment (int exp_ind) +{ + DbeView *dbev; + int index; + Experiment *exp2; + + status_ompavail = -1; + Experiment *exp = exps->fetch (exp_ind); + + // If this is a sub experiment, don't do it + if (exp->founder_exp != NULL) // this is a sub experiment; don't do it + return (dbe_strdup (GTXT ("Can not drop subexperiments"))); + + if (VecSize (exp->children_exps) > 0) + for (;;) + { + // search the list of experiments to find all that have this one as founder + bool found = false; + Vec_loop (Experiment*, exps, index, exp2) + { + if (exp2->founder_exp == exp) + { + exp2->founder_exp = NULL; + drop_experiment (index); + found = true; + break; + } + } + if (found == false) + break; + } + + // then proceed to finish the drop + Vec_loop (DbeView*, views, index, dbev) + { + dbev->drop_experiment (exp_ind); + } + + int old_cnt = expGroups->size (); + for (int i = 0; i < old_cnt; i++) + { + ExpGroup *gr = expGroups->fetch (i); + if (gr->groupId == exp->groupId) + { + gr->drop_experiment (exp); + if ((gr->founder == NULL) && (gr->exps->size () == 0)) + { + delete gr; + expGroups->remove (i); + } + break; + } + } + delete exps->remove (exp_ind); + if (old_cnt != expGroups->size ()) + { + for (int i = 0, sz = expGroups->size (); i < sz; i++) + { + ExpGroup *gr = expGroups->fetch (i); + gr->groupId = i + 1; + Vector<Experiment*> *expList = gr->exps; + for (int i1 = 0, sz1 = expList->size (); i1 < sz1; i1++) + expList->fetch (i1)->groupId = gr->groupId; + } + for (int i = 0, sz = views->size (); i < sz; i++) + { + dbev = views->fetch (i); + int cmp = dbev->get_compare_mode (); + dbev->set_compare_mode (CMP_DISABLE); + dbev->set_compare_mode (cmp); + } + } + check_tab_avail (); // update tab availability + return NULL; +} + +int +DbeSession::find_experiment (char *path) +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (strcmp (exp->get_expt_name (), path) == 0) + return exp->getExpIdx (); + } + return -1; +} + +LoadObject * +DbeSession::createLoadObject (const char *nm, int64_t cksum) +{ + return loadObjMap->sync_create_item (nm, cksum); +} + +LoadObject * +DbeSession::createLoadObject (const char *nm, const char *runTimePath, DbeFile *df) +{ + return loadObjMap->sync_create_item (nm, runTimePath, df); +} + +void +DbeSession::append (LoadObject *lobj) +{ + Histable *obj = lobj; // workaround for a C++ problem + objs->append (obj); + lobj->id = objs->size () - 1; + lobjs->append (lobj); + lobj->seg_idx = lobjs->size () - 1; + char *loname = lobj->get_pathname (); + dbeFiles->put (loname, lobj->dbeFile); +} + +DbeJarFile * +DbeSession::get_JarFile (const char *name) +{ + DbeJarFile *jf = dbeJarFiles->get (name); + if (jf == NULL) + { + jf = new DbeJarFile (name); + dbeJarFiles->put (name, jf); + } + return jf; +} + +Module * +DbeSession::createModule (LoadObject *lo, const char *nm) +{ + Module *mod = new Module (); + Histable *obj = mod; // workaround for a C++ problem + objs->append (obj); + mod->id = objs->size () - 1; + mod->loadobject = lo; + mod->set_name (dbe_strdup (nm ? nm : localized_SP_UNKNOWN_NAME)); + lo->seg_modules->append (mod); + return mod; +} + +Module * +DbeSession::createUnknownModule (LoadObject *lo) +{ + Module *mod = createModule (lo, localized_SP_UNKNOWN_NAME); + mod->flags |= MOD_FLAG_UNKNOWN; + mod->set_file_name (dbe_strdup (localized_SP_UNKNOWN_NAME)); + return mod; +} + +SourceFile * +DbeSession::createSourceFile (const char *_path) +{ + char *path = (char *) _path; + if (strncmp (path, NTXT ("./"), 2) == 0) + path += 2; + SourceFile *source = sourcesMap->get (path); + if (source == NULL) + { + source = new SourceFile (path); + (void) sourcesMap->put (path, source); + append (source); + } + return source; +} + +Function * +DbeSession::createFunction () +{ + Function *func = new Function (objs->size ()); + Histable *obj = func; // workaround for a C++ problem + objs->append (obj); + return func; +} + +JMethod * +DbeSession::createJMethod () +{ + JMethod *jmthd = new JMethod (objs->size ()); + Histable *obj = jmthd; // workaround for a C++ problem + objs->append (obj); + return jmthd; +} + +Module * +DbeSession::createClassFile (char *className) +{ + ClassFile *cls = new ClassFile (); + cls->set_name (className); + char *clpath = cls->get_java_file_name (className, true); + cls->dbeFile = getDbeFile (clpath, DbeFile::F_JAVACLASS); + free (clpath); + Histable *obj = cls; // workaround for a C++ problem + objs->append (obj); + cls->id = objs->size () - 1; + return cls; +} + +Histable * +DbeSession::createHistObject (Histable::Type type) +{ + switch (type) + { + case Histable::DOBJECT: + { + DataObject *dataobj = new DataObject (); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; + } + default: + assert (0); + } + return NULL; +} + +DataObject * +DbeSession::createDataObject () +{ + DataObject *dataobj = new DataObject (); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; +} + +DataObject * +DbeSession::createDataObject (DataObject *dobj, DataObject *parent) +{ + DataObject *dataobj = new DataObject (); + dataobj->size = dobj->size; + dataobj->offset = dobj->offset; + dataobj->parent = parent; + dataobj->set_dobjname (dobj->get_typename (), dobj->get_instname ()); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; +} + +DataObject * +DbeSession::createMasterDataObject (DataObject *dobj) +{ + DataObject *parent = NULL; + if (dobj->parent) + { // define master parent first + parent = find_dobj_master (dobj->parent); + if (!parent) + { // clone master from this dataobject parent + parent = createDataObject (dobj->parent); + parent->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, + "Master DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) parent->id, (ull_t) dobj->parent->id, + dobj->parent->get_name ()); + // clone master DataObject elements + Vector<DataObject*> *delem = get_dobj_elements (dobj->parent); + int element_index = 0; + DataObject *element = NULL; + Vec_loop (DataObject*, delem, element_index, element) + { + DataObject *master_element = createDataObject (element, parent); + master_element->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, + "Member DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) master_element->id, (ull_t) element->id, + element->get_name ()); + } + } + else + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) clone found (%llu) %s\n", + (ull_t) parent->id, (ull_t) dobj->parent->id, + dobj->parent->get_name ()); + } + + DataObject *master = find_dobj_master (dobj); + if (!master) + { // clone master from this dataobject + master = createDataObject (dobj, parent); + master->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) master->id, (ull_t) dobj->id, dobj->get_name ()); + } + else + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) clone found (%llu) %s\n", + (ull_t) master->id, (ull_t) dobj->id, dobj->get_name ()); + return master; +} + +void +DbeSession::insert_metric (BaseMetric *mtr, Vector<BaseMetric*> *mlist) +{ + if ((mtr->get_flavors () & Metric::STATIC) == 0) + { + // insert in front of the first STATIC + for (int i = 0, mlist_sz = mlist->size (); i < mlist_sz; i++) + { + BaseMetric *m = mlist->fetch (i); + if (m->get_flavors () & Metric::STATIC) + { + mlist->insert (i, mtr); + return; + } + } + } + mlist->append (mtr); +} + +BaseMetricTreeNode* +DbeSession::get_reg_metrics_tree () +{ + if (reg_metrics_tree == NULL) + // Can't init earlier because BaseMetric() requires DbeSession::ql_parse + reg_metrics_tree = new BaseMetricTreeNode (); + return reg_metrics_tree; +} + +void +DbeSession::update_metric_tree (BaseMetric *m) +{ + get_reg_metrics_tree ()->register_metric (m); +} + +BaseMetric * +DbeSession::register_metric_expr (BaseMetric::Type type, char *cmd, char *expr_spec) +{ + BaseMetric *m = find_metric (type, cmd, expr_spec); + if (m) + return m; + BaseMetric *bm = find_metric (type, cmd, NULL); // clone this version + m = new BaseMetric (*bm); + m->set_expr_spec (expr_spec); + insert_metric (m, reg_metrics); + return m; +} + +BaseMetric * +DbeSession::register_metric (BaseMetric::Type type) +{ + BaseMetric *m = find_metric (type, NULL, NULL); + if (m) + return m; + m = new BaseMetric (type); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +BaseMetric * +DbeSession::register_metric (Hwcentry *ctr, const char* aux, const char* username) +{ + BaseMetric *m = find_metric (BaseMetric::HWCNTR, aux, NULL); + if (m) + // That may be a problem when metrics aren't an exact match. + // For example, memoryspace is disabled in one experiment and not in another. + return m; + if (ctr->timecvt) + { + char *time_cmd = dbe_sprintf (NTXT ("t%s"), aux); + char *time_username = dbe_sprintf (GTXT ("%s Time"), + ctr->metric ? ctr->metric : + (ctr->name ? ctr->name : ctr->int_name)); + BaseMetric *m1; + if (ipc_mode) + { + // Two visible metrics are presented in GUI + m1 = new BaseMetric (ctr, aux, time_cmd, time_username, VAL_TIMEVAL); + insert_metric (m1, reg_metrics); + update_metric_tree (m1); + m = new BaseMetric (ctr, aux, username, VAL_VALUE, m1); + } + else + { + // Only one visible metric is presented in er_print + m1 = new BaseMetric (ctr, aux, time_cmd, time_username, VAL_TIMEVAL | VAL_INTERNAL); + insert_metric (m1, reg_metrics); + m = new BaseMetric (ctr, aux, username, VAL_TIMEVAL | VAL_VALUE, m1); + } + free (time_cmd); + free (time_username); + } + else + m = new BaseMetric (ctr, aux, username, VAL_VALUE); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +BaseMetric * +DbeSession::register_metric (char *name, char *username, char *_def) +{ + BaseMetric *m = find_metric (BaseMetric::DERIVED, name, NULL); + if (m) + return m; + Definition *p = Definition::add_definition (_def); + if (p == NULL) + return NULL; + m = new BaseMetric (name, username, p); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +void +DbeSession::drop_metric (BaseMetric *mtr) +{ + Countable *cnt; + int index; + + Vec_loop (Countable*, metrics, index, cnt) + { + if (mtr == (BaseMetric *) cnt->item) + { + cnt->ref_count--; + if (cnt->ref_count == 0) + { + // Remove this metric from all views + DbeView *dbev; + int index2; + Vec_loop (DbeView*, views, index2, dbev) + { + dbev->reset_metrics (); + } + delete metrics->remove (index); + delete mtr; + return; + } + } + } +} + +BaseMetric * +DbeSession::find_metric (BaseMetric::Type type, const char *cmd, const char *expr_spec) +{ + for (int i = 0, sz = reg_metrics->size (); i < sz; i++) + { + BaseMetric *bm = reg_metrics->fetch (i); + if (bm->get_type () == type && dbe_strcmp (bm->get_expr_spec (), expr_spec) == 0) + { + if ((type == BaseMetric::DERIVED || type == BaseMetric::HWCNTR) + && dbe_strcmp (bm->get_cmd (), cmd) != 0) + continue; + return bm; + } + } + return NULL; +} + +BaseMetric * +DbeSession::find_base_reg_metric (char * mcmd) +{ + for (int i = 0, sz = reg_metrics->size (); i < sz; i++) + { + BaseMetric *bm = reg_metrics->fetch (i); + if (bm->get_expr_spec () != NULL) + continue; // skip compare metrics + if (dbe_strcmp (bm->get_cmd (), mcmd) == 0) + return bm; + } + return NULL; +} + +Vector<BaseMetric*> * +DbeSession::get_base_reg_metrics () +{ + Vector<BaseMetric*> *mlist = new Vector<BaseMetric*>; + Vector<BaseMetric*> *ml = get_all_reg_metrics (); + for (int i = 0, sz = ml->size (); i < sz; i++) + { + BaseMetric *m = ml->fetch (i); + if (m->get_expr_spec () == NULL) + mlist->append (m); + } + return mlist; +} + +void +DbeSession::check_tab_avail () +{ + DbeView *dbev; + int index; + // tell the views to update their tab lists + Vec_loop (DbeView*, views, index, dbev) + { + dbev->get_settings ()->updateTabAvailability (); + } +} + +bool +DbeSession::is_datamode_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->dataspaceavail) + return true; + } + return false; +} + +bool +DbeSession::is_leaklist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->leaklistavail) + return true; + } + return false; +} + +bool +DbeSession::is_heapdata_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->heapdataavail) + return true; + } + return false; +} + +bool +DbeSession::is_iodata_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->iodataavail) + return true; + } + return false; +} + +bool +DbeSession::is_racelist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->racelistavail) + return true; + } + return false; +} + +bool +DbeSession::is_deadlocklist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->deadlocklistavail) + return true; + } + return false; +} + +bool +DbeSession::is_timeline_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->timelineavail) + return true; + } + return false; +} + +bool +DbeSession::is_ifreq_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->ifreqavail) + return true; + } + return false; +} + +bool +DbeSession::is_omp_available () +{ + if (status_ompavail == -1) + { + status_ompavail = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->ompavail) + { + status_ompavail = 1; + break; + } + } + } + return status_ompavail == 1; +} + +bool +DbeSession::has_java () +{ + int status_has_java = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->has_java) + { + status_has_java = 1; + break; + } + } + return status_has_java == 1; +} + +bool +DbeSession::has_ompavail () +{ + int status_has_ompavail = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->ompavail) + { + status_has_ompavail = 1; + break; + } + } + return status_has_ompavail == 1; +} + +int +DbeSession::get_clock (int whichexp) +{ + // XXXX clock frequency should be an attribute of each CPU, + // XXX and not a property of the session + // if whichexp is -1, pick the first exp that has a clock + // otherwise return the clock from the numbered experiment + Experiment *exp; + if (whichexp != -1) + { + exp = get_exp (whichexp); + if (exp != NULL) + return exp->clock; + return 0; + } + int n = nexps (); + for (int i = 0; i < n; i++) + { + exp = get_exp (i); + if (exp != NULL && exp->clock != 0) + return exp->clock; + } + return 0; +} + +LoadObject * +DbeSession::find_lobj_by_name (const char *lobj_name, int64_t cksum) +{ + return loadObjMap->get (lobj_name, cksum); +} + +static unsigned +hash (char *s) +{ + unsigned res = 0; + for (int i = 0; i < 64 && *s; i++) + res = res * 13 + *s++; + return res; +} + +// This method is introduced to fix performance +// problems with the data space profiling in the +// current release. A better design is desired. +void +DbeSession::dobj_updateHT (DataObject *dobj) +{ + unsigned index = hash (dobj->get_unannotated_name ()) % HTableSize; + List *list = new List; + list->val = (void*) dobj; + list->next = dnameHTable[index]; + dnameHTable[index] = list; +} + +DataObject * +DbeSession::find_dobj_by_name (char *dobj_name) +{ + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + if (strcmp (d->get_unannotated_name (), dobj_name) == 0) + return d; + } + return (DataObject *) NULL; +} + +DataObject * +DbeSession::find_dobj_match (DataObject *dobj) +{ + char *dobj_name = dobj->get_unannotated_name (); + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + if (strcmp (d->get_unannotated_name (), dobj_name) == 0 + && d->size == dobj->size && d->offset == dobj->offset + && d->scope == dobj->scope) + return d; + } + return (DataObject *) NULL; +} + +DataObject * +DbeSession::find_dobj_master (DataObject *dobj) +{ + char *dobj_name = dobj->get_unannotated_name (); + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + // XXXX should parent also match? + if (strcmp (d->get_unannotated_name (), dobj_name) == 0 + && d->size == dobj->size && d->offset == dobj->offset + && d->master == NULL && d->scope == NULL) + return d; + } + return (DataObject *) NULL; +} + +Vector<DataObject*>* +DbeSession::get_dobj_elements (DataObject *dobj) +{ + DataObject *d; + int index; + Vector<DataObject*> *elements = new Vector<DataObject*>; + if (dobj == d_total) + return elements; + Vec_loop (DataObject*, dobjs, index, d) + { + if (d->get_parent () && d->get_parent () == dobj) + elements->append (d); + } + return elements; +} + +Vector<LoadObject*>* +DbeSession::get_text_segments () +{ + LoadObject *lo; + int index; + Vector<LoadObject*> *tlobjs = new Vector<LoadObject*>; + Vec_loop (LoadObject*, lobjs, index, lo) + { + if (lo->type == LoadObject::SEG_TEXT) + tlobjs->append (lo); + } + return tlobjs; +} + +static long long +getNumber (const char *s, char * &last) +{ + long long val; + char *sp; + errno = 0; + val = strtoll (s, &sp, 0); + if (errno == EINVAL) + last = NULL; + else + { + while (isspace (*sp)) + sp++; + last = sp; + } + return (val); +} + +bool +DbeSession::find_obj (FILE *dis_file, FILE *inp_file, Histable *&obj, + char *name, const char *sel, Histable::Type type, bool xdefault) +{ + Vector<Histable*> *obj_lst; + int which = -1; + char *last = NULL; + if (type != Histable::FUNCTION && sel) + { + // check that a number has been provided + which = (int) getNumber (sel, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + sel = NULL; + which = 0; + } + which--; + } + obj_lst = new Vector<Histable*>; + switch (type) + { + case Histable::FUNCTION: + obj = map_NametoFunction (name, obj_lst, sel); + break; + case Histable::MODULE: + obj = map_NametoModule (name, obj_lst, which); + break; + case Histable::LOADOBJECT: + obj = map_NametoLoadObject (name, obj_lst, which); + break; + case Histable::DOBJECT: + obj = map_NametoDataObject (name, obj_lst, which); + break; + default: + abort (); // unexpected Histable! + } + + if ((obj == NULL) && (obj_lst->size () > 0)) + { + if (obj_lst->size () == 1) + which = 0; + else + { + if (sel && (which < 0 || which >= obj_lst->size ())) + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + if (xdefault) + { + fprintf (stderr, GTXT ("Default selection \"1\" made\n")); + which = 0; + } + else + { + which = ask_which (dis_file, inp_file, obj_lst, name); + if (which == -1) + { + delete obj_lst; + return false; + } + } + } + obj = obj_lst->fetch (which); + } + delete obj_lst; + return true; +} + +int +DbeSession::ask_which (FILE *dis_file, FILE *inp_file, + Vector<Histable*> *list, char *name) +{ + Histable *hitem; + Function *func; + Module *module; + int which, index, index1; + char *item_name, *lo_name, *fname, *last; + char buf[BUFSIZ]; + for (;;) + { + fprintf (dis_file, GTXT ("Available name list:\n")); + fprintf (dis_file, GTXT ("%8d) Cancel\n"), 0); + Vec_loop (Histable*, list, index, hitem) + { + index1 = index + 1; + item_name = hitem->get_name (); + switch (hitem->get_type ()) + { + case Histable::FUNCTION: + func = (Function *) hitem; + module = func->module; + + // id == -1 indicates er_src invocation + if (module == NULL || (module->lang_code == Sp_lang_java + && module->loadobject->id == -1)) + fprintf (dis_file, NTXT ("%8d) %s\n"), index1, item_name); + else + { + lo_name = module->loadobject->get_pathname (); + fname = (module->file_name && *module->file_name) ? + module->file_name : module->get_name (); + if (fname && *fname) + fprintf (dis_file, NTXT ("%8d) %s %s:0x%llx (%s)\n"), index1, + item_name, lo_name, (ull_t) func->img_offset, fname); + else + fprintf (dis_file, NTXT ("%8d) %s %s:0x%llx\n"), index1, + item_name, lo_name, (ull_t) func->img_offset); + } + break; + case Histable::MODULE: + module = (Module *) hitem; + lo_name = module->loadobject->get_pathname (); + if (name[strlen (name) - 1] == + module->file_name[strlen (module->file_name) - 1]) + fprintf (dis_file, NTXT ("%8d) %s(%s)\n"), index1, + module->file_name, lo_name); + else + fprintf (dis_file, NTXT ("%8d) %s(%s)\n"), index1, item_name, + lo_name); + break; + default: + fprintf (dis_file, NTXT ("%8d) %s\n"), index1, item_name); + break; + } + } + if (inp_file != stdin) + return -1; + fprintf (dis_file, GTXT ("Enter selection: ")); + if (fgets (buf, (int) sizeof (buf), inp_file) == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid number entered:\n")); + return -1; + } + which = (int) getNumber (buf, last); + if (last && *last == '\0') + if (which >= 0 && which <= list->size ()) + return which - 1; + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), buf); + } +} + +static bool +match_basename (char *name, char *full_name, int len = -1) +{ + if (full_name == NULL) + return false; + if (!strchr (name, '/')) + full_name = get_basename (full_name); + if (len == -1) + return streq (name, full_name); + return strncmp (name, full_name, len) == 0; +} + +LoadObject * +DbeSession::map_NametoLoadObject (char *name, Vector<Histable*> *list, int which) +{ + // Search the tree to find the first module whose module name + // matches "name" or whose source file name matches "name" + // Issues: is the name a pathname, or a base name? + // Should we look at suffix to disambiguate? + LoadObject *loitem; + int index; + Vec_loop (LoadObject*, lobjs, index, loitem) + { + // try pathname first + // if failed, try object name next + if (match_basename (name, loitem->get_pathname ()) || + match_basename (name, loitem->get_name ())) + { + if (which == list->size ()) + return loitem; + list->append (loitem); + } + } + return (LoadObject *) NULL; +} + +Module * +DbeSession::map_NametoModule (char *name, Vector<Histable*> *list, int which) +{ + // Search the tree to find the first loadobject whose loadobject name + // matches "name". + + // Issues: is the name a pathname, or a base name? + // Should we look at suffix to disambiguate? + LoadObject *loitem; + Module *mitem; + int index1, index2; + Vec_loop (LoadObject*, lobjs, index1, loitem) + { + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + // try source name first + // if failed, try object name next + if (match_basename (name, mitem->file_name) || + match_basename (name, mitem->get_name ())) + { + if (which == list->size ()) + return mitem; + list->append (mitem); + } + } + } + return (Module *) NULL; +} + +Function * +DbeSession::map_NametoFunction (char *name, Vector<Histable*> *list, + const char *sel) +{ + // Search the tree to find the first function whose + // name matches "name". + // Issues: is the name a full name, or a short name? + // Is it a demangled name? If so, what about spaces + // within the name? + // Is there a way to return all names that match? + // How can the user specify a particular function of that name? + LoadObject *loitem; + Function *fitem, *main_func = NULL; + Module *mitem, *main_mod = NULL; + int index1, index2, index3, which = -1; + if (sel) + { + char *last = NULL; + if (*sel == '@') + { // 'sel' is "@seg_num:address" + which = (int) getNumber (sel + 1, last); + if (last == NULL || *last != ':' || (which < 0) || (which >= lobjs->size ())) + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + uint64_t address = getNumber (last + 1, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + loitem = lobjs->fetch (which); + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + Vec_loop (Function*, mitem->functions, index3, fitem) + { + if (address == fitem->img_offset && match_FName (name, fitem)) + return fitem; + } + } + return NULL; + } + + which = (int) getNumber (sel, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + which--; + } + + int len_path = 0; + char *with_path = name; + name = StrRchr (name, '`'); + if (name != with_path) + len_path = (int) (name - with_path); + else + with_path = NULL; + + Vec_loop (LoadObject*, lobjs, index1, loitem) + { + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + if (with_path) + { // with file name + // try source name first + // if failed, try object name next + if (!match_basename (with_path, mitem->file_name, len_path) && + !match_basename (with_path, mitem->get_name (), len_path)) + continue; + } + Vec_loop (Function*, mitem->functions, index3, fitem) + { + if (match_FName (name, fitem)) + { + if (which == list->size ()) + return fitem; + list->append (fitem); + continue; + } + if (streq (fitem->get_name (), NTXT ("MAIN_")) && mitem->is_fortran ()) + { + main_func = fitem; + main_mod = mitem; + } + } + } + } + + if (main_mod && main_func) + { + main_mod->read_stabs (); + if (streq (main_func->get_match_name (), name) && which <= 1) + return main_func; + } + return (Function *) NULL; +} + +DataObject * +DbeSession::map_NametoDataObject (char *name, Vector<Histable*> *list, + int which) +{ + // Search master list to find dataobjects whose names match "name" + // selecting only the entry corresponding to "which" if it is not -1. + // Issues: is the name fully qualified or only partially? + DataObject *ditem = NULL; + int index; + char *full_name; + Vec_loop (DataObject*, dobjs, index, ditem) + { + if (ditem->scope) continue; // skip non-master dataobjects + + // try fully-qualified dataobject name first + if ((full_name = ditem->get_name ()) != NULL) + { + if (streq (name, full_name)) + { + if (which == list->size ()) + return ditem; + list->append (ditem); + } + } + } + if (list->size () > 0) + return ditem; // return fully-qualified match + + // if fully-qualified name doesn't match anything, try a partial match + Vec_loop (DataObject*, dobjs, index, ditem) + { + if (ditem->scope) continue; // skip non-master dataobjects + + // try fully-qualified dataobject name first + if ((full_name = ditem->get_name ()) != NULL) + { + if (strstr (full_name, name)) + { + if (which == list->size ()) + return ditem; + list->append (ditem); + } + } + } + return (DataObject *) NULL; +} + +bool +DbeSession::match_FName (char *name, Function *func) +{ + size_t len; + char buf[MAXDBUF]; + char *full_name; + if (streq (func->get_name (), name)) // try full name comparison + return true; + if (streq (func->get_mangled_name (), name)) // try mangled name + return true; + if (streq (func->get_match_name (), name)) // try match name + return true; + + Module *md = func->module; // try FORTRAN name + if (md && md->is_fortran ()) + { + char *mangled_name = func->get_mangled_name (); + len = strlen (name); + if (((len + 1) == strlen (mangled_name)) && + (strncmp (name, mangled_name, len) == 0)) + return true; + } + snprintf (buf, sizeof (buf), NTXT ("%s"), func->get_name ()); + full_name = buf; + char *arg = NULL; // find modifier and C++ class name + int i = get_paren (buf); + if (i >= 0) + { + arg = buf + i; + *arg = '\0'; + } + + char *mod = strchr (full_name, ' '); + char *cls = strchr (full_name, ':'); + + if (mod) + { + len = mod - full_name + 1; + if (!strncmp (full_name, name, len)) + name += len; + full_name += len; + if (streq (full_name, name)) // try without modifier + return true; + } + + size_t len_cmp = strlen (name); + if (arg) + { + *arg = '('; + len = arg - full_name; // try without 'args' + if (len_cmp == len && !strncmp (full_name, name, len)) + return true; + if (cls) + { + len = arg - cls - 2; // and without 'class name' + if ((len_cmp == len) && !strncmp (cls + 2, name, len)) + return true; + } + } + + if (cls) + { + len = cls - full_name; // try C++ class name only + if (len_cmp == len && !strncmp (full_name, name, len)) + return true; + if (streq (cls + 2, name)) // try without 'class name' + return true; + } + return false; +} + +bool +DbeSession::add_path (char *path) +{ + return add_path (path, get_search_path ()); +} + +bool +DbeSession::add_classpath (char *path) +{ + return add_path (path, classpath); +} + +Vector<DbeFile*> * +DbeSession::get_classpath () +{ + if (classpath_df == NULL) + classpath_df = new Vector<DbeFile*>; + for (int i = classpath_df->size (), sz = classpath->size (); i < sz; i++) + classpath_df->store (i, getDbeFile (classpath->fetch (i), + DbeFile::F_DIR_OR_JAR)); + return classpath_df; +} + +bool +DbeSession::add_path (char *path, Vector<char*> *pathes) +{ + bool result = false; + Vector <char *> *tokens = split_str (path, ':'); + for (long j = 0, jsz = VecSize (tokens); j < jsz; j++) + { + char *spath = tokens->get (j); + // Don't append path if it's already there + bool got = false; + for (int i = 0, sz = pathes->size (); i < sz; i++) + { + char *nm = pathes->get (i); + if (streq (nm, spath)) + { + got = true; + break; + } + } + if (!got) + { + pathes->append (spath); + result = true; + } + else + free (spath); + } + delete tokens; + return result; +} + +void +DbeSession::set_need_refind () +{ + Vector<DbeFile*> *f_list = dbeFiles->values (); + for (long i = 0, sz = f_list == NULL ? 0 : f_list->size (); i < sz; i++) + { + DbeFile *f = f_list->get (i); + f->set_need_refind (true); + } + delete f_list; + for (long i = 0, sz = sources == NULL ? 0 : sources->size (); i < sz; i++) + { + SourceFile *f = sources->get (i); + if (f && f->dbeFile) + f->dbeFile->set_need_refind (true); + } +} + +void +DbeSession::set_search_path (Vector<char*> *path, bool reset) +{ + if (reset) + search_path->destroy (); + for (int i = 0, sz = path == NULL ? 0 : path->size (); i < sz; i++) + { + char *name = path->fetch (i); + if (add_path (name)) + reset = true; + } + if (reset) + { + set_need_refind (); + + // now reset the string setting for it + StringBuilder sb; + for (int i = 0, sz = search_path == NULL ? 0 : search_path->size (); i < sz; i++) + { + char *name = search_path->fetch (i); + if (sb.length () != 0) + sb.append (':'); + sb.append (name); + } + free (settings->str_search_path); + settings->str_search_path = sb.toString (); + } +} + +void +DbeSession::set_search_path (char *_lpath, bool reset) +{ + Vector<char *> *path = new Vector<char*>; + char *lpath = dbe_strdup (_lpath); + for (char *s = lpath; s;) + { + path->append (s); + s = strchr (s, ':'); + if (s) + { + *s = 0; + s++; + } + } + set_search_path (path, reset); + delete path; + free (lpath); +} + +void +DbeSession::set_pathmaps (Vector<pathmap_t*> *newPathMap) +{ + set_need_refind (); + settings->set_pathmaps (newPathMap); +} + +Vector<pathmap_t*> * +DbeSession::get_pathmaps () +{ + return settings->pathmaps; +} + +void +DbeSession::mobj_define (MemObjType_t *mobj) +{ + settings->mobj_define (mobj, false); + DbeView *dbev; + int index; + Vec_loop (DbeView*, views, index, dbev) + { + dbev->get_settings ()->mobj_define (mobj, false); + } +} + +void +DbeSession::dump_segments (FILE *out) +{ + int index; + LoadObject *loitem; + Vec_loop (LoadObject*, lobjs, index, loitem) + { + fprintf (out, NTXT ("Segment %d -- %s -- %s\n\n"), + index, loitem->get_name (), loitem->get_pathname ()); + loitem->dump_functions (out); + fprintf (out, NTXT ("\n End Segment %d -- %s -- %s\n\n"), + index, loitem->get_name (), loitem->get_pathname ()); + } +} + +void +DbeSession::dump_dataobjects (FILE *out) +{ + DataObject *ditem; + int index; + + fprintf (out, NTXT ("\nMaster list of DataObjects:\n")); + Vec_loop (DataObject*, dobjs, index, ditem) + { + Histable* scope = ditem->get_scope (); + DataObject* parent = ditem->get_parent (); + DataObject* master = ditem->get_master (); + if (parent != NULL) + fprintf (out, "id %6lld: [%4lld] parent = %6lld, offset = %+4lld %s\n", + (ll_t) ditem->id, (ll_t) ditem->get_size (), + (ll_t) parent->id, (ll_t) ditem->get_offset (), + ditem->get_name ()); + else + { + // parent is NULL + fprintf (out, NTXT ("id %6lld: [%4lld] %s "), + (ll_t) ditem->id, (ll_t) ditem->get_size (), + ditem->get_name ()); + if (master != NULL) + fprintf (out, NTXT (" master=%lld "), (ll_t) master->id); + else if (scope != NULL) + fprintf (out, NTXT (" master=?? ")); + else + fprintf (out, NTXT (" MASTER ")); +#if DEBUG + if (scope != NULL) + { + switch (scope->get_type ()) + { + case Histable::LOADOBJECT: + case Histable::FUNCTION: + fprintf (out, NTXT ("%s"), scope->get_name ()); + break; + case Histable::MODULE: + { + char *filename = get_basename (scope->get_name ()); + fprintf (out, NTXT ("%s"), filename); + break; + } + default: + fprintf (out, NTXT (" Unexpected scope %d:%s"), + scope->get_type (), scope->get_name ()); + } + } +#endif + fprintf (out, NTXT ("\n")); + } + } +} + +void +DbeSession::dump_map (FILE *out) +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + exp->dump_map (out); + } +} + +void +DbeSession::dump_stacks (FILE *outfile) +{ + Experiment *exp; + int n = nexps (); + FILE *f = (outfile == NULL ? stderr : outfile); + for (int i = 0; i < n; i++) + { + exp = get_exp (i); + fprintf (f, GTXT ("Experiment %d -- %s\n"), i, exp->get_expt_name ()); + exp->dump_stacks (f); + } +} + +void +DbeSession::propNames_name_store (int propId, const char *propName) +{ + PropDescr *prop = new PropDescr (propId, propName); + prop->flags = PRFLAG_NOSHOW; // do not show descriptions + propNames->store (propId, prop); +} + +void +DbeSession::propNames_name_store (int propId, const char* propName, + const char* propUname, VType_type dataType, + int flags) +{ + PropDescr *prop = new PropDescr (propId, propName); + prop->vtype = dataType; + prop->uname = dbe_strdup (propUname); + prop->flags = flags; + propNames->store (propId, prop); +} + +char * +DbeSession::propNames_name_fetch (int i) +{ + PropDescr *prop = propNames->fetch (i); + if (prop) + return prop->name; + return NULL; +} + +int +DbeSession::registerPropertyName (const char *name) +{ + if (name == NULL) + return PROP_NONE; + for (int i = 0; i < propNames->size (); i++) + { + char *pname = propNames_name_fetch (i); + if (pname && strcasecmp (pname, name) == 0) + return i; + } + int propId = propNames->size (); + propNames_name_store (propId, name); + return propId; +} + +int +DbeSession::getPropIdByName (const char *name) +{ + if (name == NULL) + return PROP_NONE; + for (int i = 0; i < propNames->size (); i++) + { + char *pname = propNames_name_fetch (i); + if (pname && strcasecmp (pname, name) == 0) + return i; + } + return PROP_NONE; +} + +char * +DbeSession::getPropName (int propId) +{ + if (!propNames) + return NULL; + if (propId < 0 || propId >= propNames->size ()) + return NULL; + return dbe_strdup (propNames_name_fetch (propId)); +} + +char * +DbeSession::getPropUName (int propId) +{ + if (!propNames) + return NULL; + if (propId < 0 || propId >= propNames->size ()) + return NULL; + PropDescr *prop = propNames->fetch (propId); + if (prop) + return dbe_strdup (prop->uname); + return NULL; +} + +void +DbeSession::append (UserLabel *lbl) +{ + if (lbl->expr) + { + if (userLabels == NULL) + userLabels = new Vector<UserLabel*> (); + userLabels->append (lbl); + } +} + +void +DbeSession::append (SourceFile *sf) +{ + sources->append (sf); + objs->append (sf); +} + +UserLabel * +DbeSession::findUserLabel (char *name) +{ + for (int i = 0, sz = userLabels ? userLabels->size () : 0; i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + if (strcasecmp (lbl->name, name) == 0) + return lbl; + } + return NULL; +} + +Expression * +DbeSession::findObjDefByName (char *name) +{ + Expression *expr = NULL; + + MemObjType_t *mot = MemorySpace::findMemSpaceByName (name); + if (mot != NULL) + { + char *index_expr_str = mot->index_expr; + expr = ql_parse (index_expr_str); + } + + if (expr == NULL) + { + int indxtype = findIndexSpaceByName (name); + expr = getIndexSpaceExpr (indxtype); + } + if (expr == NULL) + { + UserLabel *ulbl = findUserLabel (name); + if (ulbl) + expr = ulbl->expr; + } + return expr; +} + +Expression * +DbeSession::ql_parse (const char *expr_spec) +{ + /* (This slight duplication means we don't need to worry about copy + constructors for the QL::Result, nor about the lifetime of the + expr_spec.) */ + if (expr_spec != NULL) + { + QL::Result result (expr_spec); + QL::Parser qlparser (result); + if (qlparser () != 0) + return NULL; + return result (); + } + else + { + QL::Result result; + QL::Parser qlparser (result); + if (qlparser () != 0) + return NULL; + return result (); + } +} + +Vector<void*> * +DbeSession::getIndxObjDescriptions () +{ + int size = dyn_indxobj_indx; + if (size == 0) + return NULL; + Vector<int> *type = new Vector<int>(dyn_indxobj_indx); + Vector<char*> *desc = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *i18ndesc = new Vector<char*>(dyn_indxobj_indx); + Vector<char> *mnemonic = new Vector<char>(dyn_indxobj_indx); + Vector<int> *orderList = new Vector<int>(dyn_indxobj_indx); + Vector<char*> *exprList = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *sdesc = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *ldesc = new Vector<char*>(dyn_indxobj_indx); + + for (long i = 0, sz = VecSize (dyn_indxobj); i < sz; i++) + { + IndexObjType_t *tot = dyn_indxobj->get (i); + if (tot->memObj == NULL) + { + type->append ((int) tot->type); + desc->append (dbe_strdup (tot->name)); + i18ndesc->append (dbe_strdup (tot->i18n_name)); + sdesc->append (dbe_strdup (tot->short_description)); + ldesc->append (dbe_strdup (tot->long_description)); + mnemonic->append (tot->mnemonic); + orderList->append (settings->indx_tab_order->fetch (i)); + exprList->append (dbe_strdup (tot->index_expr_str)); + } + } + Vector<void*> *res = new Vector<void*>(8); + res->store (0, type); + res->store (1, desc); + res->store (2, mnemonic); + res->store (3, i18ndesc); + res->store (4, orderList); + res->store (5, exprList); + res->store (6, sdesc); + res->store (7, ldesc); + return (res); +} + +// Static function to get a vector of custom index object definitions +Vector<void*> * +DbeSession::getCustomIndxObjects () +{ + Vector<char*> *name = new Vector<char*>; + Vector<char*> *formula = new Vector<char*>; + for (long i = dyn_indxobj_indx_fixed, sz = VecSize (dyn_indxobj); i < sz; i++) + { + IndexObjType_t *tot = dyn_indxobj->get (i); + if (tot->memObj == NULL) + { + name->append (dbe_strdup (tot->name)); + formula->append (dbe_strdup (tot->index_expr_str)); + } + } + Vector<void*> *res = new Vector<void*>(2); + res->store (0, name); + res->store (1, formula); + return (res); +} + +// Static function to define a new index object type +char * +DbeSession::indxobj_define (const char *mname, char *i18nname, const char *index_expr_str, char *short_description, char *long_description) +{ + if (mname == NULL) + return dbe_strdup (GTXT ("No index object type name has been specified.")); + if (isalpha ((int) (mname[0])) == 0) + return dbe_sprintf (GTXT ("Index Object type name %s does not begin with an alphabetic character"), + mname); + const char *p = mname; + while (*p != 0) + { + if ((isalnum ((int) (*p)) == 0) && (*p != '_')) + return dbe_sprintf (GTXT ("Index Object type name %s contains a non-alphanumeric character"), + mname); + p++; + } + + // make sure the name is not in use + if (MemorySpace::findMemSpaceByName (mname) != NULL) + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + + int idxx = findIndexSpaceByName (mname); + if (idxx >= 0) + { + IndexObjType_t *mt = dyn_indxobj->fetch (idxx); + if (strcmp (mt->index_expr_str, index_expr_str) == 0) + // It's a redefinition, but the new definition is the same + return NULL; + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + } + if (index_expr_str == NULL) + return dbe_strdup (GTXT ("No index-expr has been specified.")); + if (strlen (index_expr_str) == 0) + return dbe_sprintf (GTXT ("Index Object index expression is invalid: %s"), + index_expr_str); + + // verify that the index expression parses correctly + char *expr_str = dbe_strdup (index_expr_str); + Expression *expr = ql_parse (expr_str); + if (expr == NULL) + return dbe_sprintf (GTXT ("Index Object index expression is invalid: %s"), + expr_str); + + // It's OK, create the new table entry + IndexObjType_t *tot = new IndexObjType_t; + tot->type = dyn_indxobj_indx++; + tot->name = dbe_strdup (mname); + tot->i18n_name = dbe_strdup (i18nname); + tot->short_description = dbe_strdup (short_description); + tot->long_description = dbe_strdup (long_description); + tot->index_expr_str = expr_str; + tot->index_expr = expr; + tot->mnemonic = mname[0]; + + // add it to the list + dyn_indxobj->append (tot); + idxobjs->append (new HashMap<uint64_t, Histable*>); + + // tell the session + settings->indxobj_define (tot->type, false); + + DbeView *dbev; + int index; + Vec_loop (DbeView*, views, index, dbev) + { + dbev->addIndexSpace (tot->type); + } + return NULL; +} + +char * +DbeSession::getIndexSpaceName (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->name; +} + +char * +DbeSession::getIndexSpaceDescr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->i18n_name; +} + +Expression * +DbeSession::getIndexSpaceExpr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->index_expr; +} + +char * +DbeSession::getIndexSpaceExprStr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->index_expr_str; +} + +int +DbeSession::findIndexSpaceByName (const char *mname) +{ + int idx; + IndexObjType_t *mt; + Vec_loop (IndexObjType_t*, dyn_indxobj, idx, mt) + { + if (strcasecmp (mt->name, mname) == 0) + return idx; + } + return -1; +} + +void +DbeSession::removeIndexSpaceByName (const char *mname) +{ + IndexObjType_t *indObj = findIndexSpace (mname); + if (indObj) + indObj->name[0] = 0; +} + +IndexObjType_t * +DbeSession::getIndexSpace (int index) +{ + return ((index < 0) || (index >= VecSize (dyn_indxobj))) ? NULL : dyn_indxobj->get (index); +} + +IndexObjType_t * +DbeSession::findIndexSpace (const char *mname) +{ + return getIndexSpace (findIndexSpaceByName (mname)); +} + +void +DbeSession::get_filter_keywords (Vector<void*> *res) +{ + Vector <char*> *kwCategory = (Vector<char*>*) res->fetch (0); + Vector <char*> *kwCategoryI18N = (Vector<char*>*) res->fetch (1); + Vector <char*> *kwDataType = (Vector<char*>*) res->fetch (2); + Vector <char*> *kwKeyword = (Vector<char*>*) res->fetch (3); + Vector <char*> *kwFormula = (Vector<char*>*) res->fetch (4); + Vector <char*> *kwDescription = (Vector<char*>*) res->fetch (5); + Vector <void*> *kwEnumDescs = (Vector<void*>*) res->fetch (6); + + char *vtypeNames[] = VTYPE_TYPE_NAMES; + for (long i = 0, sz = userLabels ? userLabels->size () : 0; i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + kwCategory->append (dbe_strdup (NTXT ("FK_LABEL"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Labels"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_BOOL])); + kwKeyword->append (dbe_strdup (lbl->name)); + kwFormula->append (dbe_strdup (lbl->str_expr)); + kwDescription->append (dbe_strdup (lbl->comment)); + kwEnumDescs->append (NULL); + } + + for (long i = 0, sz = propNames ? propNames->size () : 0; i < sz; i++) + { + PropDescr *prop = propNames->fetch (i); + char *pname = prop ? prop->name : NULL; + if (pname == NULL || *pname == 0 || prop->flags & PRFLAG_NOSHOW) + continue; + int vtypeNum = prop->vtype; + if (vtypeNum < 0 || vtypeNum >= TYPE_LAST) + vtypeNum = TYPE_NONE; + kwCategory->append (dbe_strdup (NTXT ("FK_EVTPROP"))); //Event Property + kwCategoryI18N->append (dbe_strdup (GTXT ("Misc. Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[vtypeNum])); + kwKeyword->append (dbe_strdup (pname)); + kwFormula->append (NULL); + kwDescription->append (dbe_strdup (prop->uname)); + kwEnumDescs->append (NULL); + } + + for (long i = 0, sz = dyn_indxobj ? dyn_indxobj->size () : 0; i < sz; i++) + { + IndexObjType_t *obj = dyn_indxobj->get (i); + if (obj->memObj) + continue; + kwCategory->append (dbe_strdup (NTXT ("FK_IDXOBJ"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Index Object Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_INT64])); + kwKeyword->append (dbe_strdup (obj->name)); + kwFormula->append (dbe_strdup (obj->index_expr_str)); + kwDescription->append (dbe_strdup (obj->i18n_name)); + kwEnumDescs->append (NULL); + } +} + +Histable * +DbeSession::findIndexObject (int idxtype, uint64_t idx) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + return iobjs->get (idx); +} + +Histable * +DbeSession::createIndexObject (int idxtype, int64_t idx) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + + Histable *idxobj = iobjs->get (idx); + if (idxobj == NULL) + { + idxobj = new IndexObject (idxtype, idx); + if (idx == -1) + idxobj->set_name (dbe_strdup (GTXT ("<Unknown>"))); + iobjs->put (idx, idxobj); + } + + return idxobj; +} + +Histable * +DbeSession::createIndexObject (int idxtype, Histable *hobj) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + int64_t idx = hobj ? hobj->id : -1; + Histable *idxobj = iobjs->get (idx); + if (idxobj == NULL) + { + idxobj = new IndexObject (idxtype, hobj); + if (idx == -1) + idxobj->set_name (dbe_strdup (GTXT ("<Unknown>"))); + iobjs->put (idx, idxobj); + } + + return idxobj; +} + +Histable * +DbeSession::findObjectById (Histable::Type type, int subtype, uint64_t id) +{ + switch (type) + { + case Histable::FUNCTION: + case Histable::MODULE: + case Histable::LOADOBJECT: + return ( id < (uint64_t) objs->size ()) ? objs->fetch ((int) id) : NULL; + case Histable::INDEXOBJ: + return findIndexObject (subtype, id); + // ignoring the following cases + case Histable::INSTR: + case Histable::LINE: + case Histable::EADDR: + case Histable::MEMOBJ: + case Histable::PAGE: + case Histable::DOBJECT: + case Histable::SOURCEFILE: + case Histable::IOACTFILE: + case Histable::IOACTVFD: + case Histable::IOCALLSTACK: + case Histable::HEAPCALLSTACK: + case Histable::OTHER: + case Histable::EXPERIMENT: + break; + } + return NULL; +} + +// return a vector of Functions that match the regular expression input string +Vector<JThread *> * +DbeSession::match_java_threads (char *ustr, int matchParent, + Vector<uint64_t> * &grids, + Vector<uint64_t> * &expids) +{ + if (ustr == NULL) + return NULL; + + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<JThread *> *ret = new Vector<JThread*>; + grids = new Vector<uint64_t>; + expids = new Vector<uint64_t>; + + int index; + JThread *jthread; + int expid; + Experiment* exp; + Vec_loop (Experiment*, exps, expid, exp) + { + + Vec_loop (JThread*, exp->get_jthreads (), index, jthread) + { + const char * name; + if (matchParent) + name = jthread->parent_name; + else + name = jthread->group_name; + if (name == NULL) + name = ""; + if (!regexec (®ex_desc, name, 0, NULL, 0)) + { + // this one matches + ret->append (jthread); + grids->append (exp->groupId); + expids->append (exp->getUserExpId ()); + } + } + } + + regfree (®ex_desc); + return ret; +} + +// return a vector of Functions that match the regular expression input string +Vector<Function *> * +DbeSession::match_func_names (const char *ustr, Histable::NameFormat nfmt) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<Function *> *ret = new Vector<Function*>; + + int index; + Histable *obj; + Vec_loop (Histable*, objs, index, obj) + { + if (obj->get_type () == Histable::FUNCTION) + { + Function *func = (Function*) obj; + if (!regexec (®ex_desc, func->get_name (nfmt), 0, NULL, 0)) + // this one matches + ret->append (func); + } + } + regfree (®ex_desc); + return ret; +} + +// return a vector of Functions that match the regular expression input string +Vector<FileData *> * +DbeSession::match_file_names (char *ustr, Histable::NameFormat nfmt) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<FileData *> *ret = new Vector<FileData*>; + int numExps = nexps (); + DefaultMap<int64_t, FileData*>* fDataMap; + Vector<FileData *> *fDataObjs; + FileData *fData; + int size; + for (int i = 0; i < numExps; i++) + { + Experiment *exp = get_exp (i); + fDataMap = exp->getFDataMap (); + fDataObjs = fDataMap->values (); + size = fDataObjs->size (); + for (int j = 0; j < size; j++) + { + fData = fDataObjs->fetch (j); + if (fData + && !regexec (®ex_desc, fData->get_raw_name (nfmt), 0, NULL, 0)) + // this one matches + ret->append (fData); + } + } + regfree (®ex_desc); + return ret; +} + +// return a vector of DataObjects that match the regular expression input string +Vector<DataObject *> * +DbeSession::match_dobj_names (char *ustr) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<DataObject *> *ret = new Vector<DataObject*>; + int index; + DataObject *ditem; + Vec_loop (DataObject*, dobjs, index, ditem) + { + // does this one match + if (!regexec (®ex_desc, ditem->get_name (), 0, NULL, 0)) + // this one matches + ret->append (ditem); + } + regfree (®ex_desc); + return ret; +} + +void +DbeSession::dump (char *msg, Vector<BaseMetric*> *mlist) +{ + if (msg) + fprintf (stderr, "%s\n", msg); + int sz = mlist ? mlist->size () : -1; + for (int i = 0; i < sz; i++) + { + BaseMetric *m = mlist->fetch (i); + char *s = m->dump (); + fprintf (stderr, "%2d %s\n", i, s); + free (s); + } + fprintf (stderr, "======END of mlist[%d] =========\n", sz); +} + +void +DbeSession::dump (char *msg, Vector<Metric*> *mlist) +{ + if (msg) + fprintf (stderr, "%s\n", msg); + int sz = mlist ? mlist->size () : -1; + for (int i = 0; i < sz; i++) + { + Metric *m = mlist->fetch (i); + char *s = m->dump (); + fprintf (stderr, "%2d %s\n", i, s); + free (s); + } + fprintf (stderr, "======END of mlist[%d] =========\n", sz); +} diff --git a/gprofng/src/DbeSession.cc.1 b/gprofng/src/DbeSession.cc.1 new file mode 100644 index 0000000..7d635d2 --- /dev/null +++ b/gprofng/src/DbeSession.cc.1 @@ -0,0 +1,3531 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/types.h> +#include <errno.h> +#include <sys/param.h> + +#include "util.h" +#include "Application.h" +#include "Experiment.h" +#include "ExpGroup.h" +#include "Expression.h" +#include "DataObject.h" +#include "Elf.h" +#include "Function.h" +#include "DbeSession.h" +#include "LoadObject.h" +#include "DbeSyncMap.h" +#include "DbeThread.h" +#include "ClassFile.h" +#include "IndexObject.h" +#include "PathTree.h" +#include "Print.h" +#include "QLParser.tab.hh" +#include "DbeView.h" +#include "MemorySpace.h" +#include "Module.h" +#include "SourceFile.h" +#include "StringBuilder.h" +#include "BaseMetric.h" +#include "BaseMetricTreeNode.h" +#include "Command.h" +#include "UserLabel.h" +#include "StringMap.h" +#include "DbeFile.h" +#include "DbeJarFile.h" +#include "IOActivity.h" +#include "HeapActivity.h" + +// This is a universal List structure to organize objects +// of various types, even if different. +struct List +{ + List *next; + void *val; +}; + +struct Countable +{ + Countable (void *_item) + { + item = _item; + ref_count = 0; + } + + void *item; + int ref_count; +}; + +Platform_t DbeSession::platform = +#if ARCH(SPARC) + Sparc; +#elif ARCH(Aarch64) + Aarch64; +#else // ARCH(Intel) + Intel; +#endif + +// This constant determines the size of the data object name hash table. +static const int HTableSize = 8192; +static int DEFAULT_TINY_THRESHOLD = -1; + +unsigned int mpmt_debug_opt = 0; +DbeSession *dbeSession = NULL; + +DbeSession::DbeSession (Settings *_settings, bool _ipc_mode, bool _rdt_mode) +{ + dbeSession = this; + ipc_mode = _ipc_mode; + rdt_mode = _rdt_mode; + settings = new Settings (_settings); + views = new Vector<DbeView*>; + exps = new Vector<Experiment*>; + lobjs = new Vector<LoadObject*>; + objs = new Vector<Histable*>; + dobjs = new Vector<DataObject*>; + metrics = new Vector<Countable*>; + reg_metrics = new Vector<BaseMetric*>; + hwcentries = NULL; + reg_metrics_tree = NULL; // BaseMetric() requires DbeSession::ql_parse + idxobjs = new Vector<HashMap<uint64_t, Histable*>*>; + tmp_files = new Vector<char*>; + search_path = new Vector<char*>; + classpath = new Vector<char*>; + classpath_df = NULL; + expGroups = new Vector<ExpGroup*>; + sourcesMap = new HashMap<char*, SourceFile*>; + sources = new Vector<SourceFile*>; + comp_lobjs = new HashMap<char*, LoadObject*>; + comp_dbelines = new HashMap<char*, DbeLine*>; + comp_sources = new HashMap<char*, SourceFile*>; + loadObjMap = new DbeSyncMap<LoadObject>; + f_special = new Vector<Function*>(LastSpecialFunction); + omp_functions = new Vector<Function*>(OMP_LAST_STATE); + interactive = false; + lib_visibility_used = false; + + // Define all known property names + propNames = new Vector<PropDescr*>; + propNames_name_store (PROP_NONE, NTXT ("")); + propNames_name_store (PROP_ATSTAMP, NTXT ("ATSTAMP")); + propNames_name_store (PROP_ETSTAMP, NTXT ("ETSTAMP")); + propNames_name_store (PROP_TSTAMP, NTXT ("TSTAMP")); + propNames_name_store (PROP_THRID, NTXT ("THRID")); + propNames_name_store (PROP_LWPID, NTXT ("LWPID")); + propNames_name_store (PROP_CPUID, NTXT ("CPUID")); + propNames_name_store (PROP_FRINFO, NTXT ("FRINFO")); + propNames_name_store (PROP_EVT_TIME, NTXT ("EVT_TIME")); + + // Samples + propNames_name_store (PROP_SMPLOBJ, NTXT ("SMPLOBJ")); + propNames_name_store (PROP_SAMPLE, NTXT ("SAMPLE")); + + // GCEvents + propNames_name_store (PROP_GCEVENTOBJ, NTXT ("GCEVENTOBJ")); + propNames_name_store (PROP_GCEVENT, NTXT ("GCEVENT")); + + // Metadata used by some packet types + propNames_name_store (PROP_VOIDP_OBJ, NTXT ("VOIDP_OBJ"), + NULL, TYPE_UINT64, DDFLAG_NOSHOW); + + // Clock profiling properties + propNames_name_store (PROP_UCPU, NTXT ("UCPU")); + propNames_name_store (PROP_SCPU, NTXT ("SCPU")); + propNames_name_store (PROP_TRAP, NTXT ("TRAP")); + propNames_name_store (PROP_TFLT, NTXT ("TFLT")); + propNames_name_store (PROP_DFLT, NTXT ("DFLT")); + propNames_name_store (PROP_KFLT, NTXT ("KFLT")); + propNames_name_store (PROP_ULCK, NTXT ("ULCK")); + propNames_name_store (PROP_TSLP, NTXT ("TSLP")); + propNames_name_store (PROP_WCPU, NTXT ("WCPU")); + propNames_name_store (PROP_TSTP, NTXT ("TSTP")); + + propNames_name_store (PROP_MSTATE, NTXT ("MSTATE")); + propNames_name_store (PROP_NTICK, NTXT ("NTICK")); + propNames_name_store (PROP_OMPSTATE, NTXT ("OMPSTATE")); + + // Synchronization tracing properties + propNames_name_store (PROP_SRQST, NTXT ("SRQST")); + propNames_name_store (PROP_SOBJ, NTXT ("SOBJ")); + + // Hardware counter profiling properties + propNames_name_store (PROP_HWCTAG, NTXT ("HWCTAG")); + propNames_name_store (PROP_HWCINT, NTXT ("HWCINT")); + propNames_name_store (PROP_VADDR, NTXT ("VADDR")); + propNames_name_store (PROP_PADDR, NTXT ("PADDR")); + propNames_name_store (PROP_VIRTPC, NTXT ("VIRTPC")); + propNames_name_store (PROP_PHYSPC, NTXT ("PHYSPC")); + propNames_name_store (PROP_LWP_LGRP_HOME, NTXT ("LWP_LGRP_HOME")); + propNames_name_store (PROP_PS_LGRP_HOME, NTXT ("PS_LGRP_HOME")); + propNames_name_store (PROP_EA_PAGESIZE, NTXT ("EA_PAGESIZE")); + propNames_name_store (PROP_EA_LGRP, NTXT ("EA_LGRP")); + propNames_name_store (PROP_PC_PAGESIZE, NTXT ("PC_PAGESIZE")); + propNames_name_store (PROP_PC_LGRP, NTXT ("PC_LGRP")); + propNames_name_store (PROP_HWCDOBJ, NTXT ("HWCDOBJ")); + propNames_name_store (PROP_MEM_LAT, NTXT ("MEM_LAT")); + propNames_name_store (PROP_MEM_SRC, NTXT ("MEM_SRC")); + + // Heap tracing properties + propNames_name_store (PROP_HTYPE, NTXT ("HTYPE")); + propNames_name_store (PROP_HSIZE, NTXT ("HSIZE")); + propNames_name_store (PROP_HVADDR, NTXT ("HVADDR")); + propNames_name_store (PROP_HOVADDR, NTXT ("HOVADDR")); + propNames_name_store (PROP_HLEAKED, NTXT ("HLEAKED"), + GTXT ("Leaked bytes"), TYPE_UINT64, 0); + propNames_name_store (PROP_HMEM_USAGE, NTXT ("HMEM_USAGE")); + propNames_name_store (PROP_HFREED, NTXT ("HFREED"), + GTXT ("Freed bytes"), TYPE_UINT64, 0); + propNames_name_store (PROP_HCUR_ALLOCS, NTXT ("HCUR_ALLOCS"), + GTXT ("Current allocations"), TYPE_INT64, 0); + propNames_name_store (PROP_HCUR_NET_ALLOC, NTXT ("HCUR_NET_ALLOC"), + NULL, TYPE_INT64, DDFLAG_NOSHOW); + propNames_name_store (PROP_HCUR_LEAKS, NTXT ("HCUR_LEAKS"), + GTXT ("Current leaks"), TYPE_UINT64, 0); + propNames_name_store (PROP_DDSCR_LNK, NTXT ("DDSCR_LNK"), + NULL, TYPE_UINT64, DDFLAG_NOSHOW); + + // IO tracing properties + propNames_name_store (PROP_IOTYPE, NTXT ("IOTYPE")); + propNames_name_store (PROP_IOFD, NTXT ("IOFD")); + propNames_name_store (PROP_IONBYTE, NTXT ("IONBYTE")); + propNames_name_store (PROP_IORQST, NTXT ("IORQST")); + propNames_name_store (PROP_IOOFD, NTXT ("IOOFD")); + propNames_name_store (PROP_IOFNAME, NTXT ("IOFNAME")); + propNames_name_store (PROP_IOVFD, NTXT ("IOVFD")); + propNames_name_store (PROP_IOFSTYPE, NTXT ("IOFSTYPE")); + + // omptrace raw properties + propNames_name_store (PROP_CPRID, NTXT ("CPRID")); + propNames_name_store (PROP_PPRID, NTXT ("PPRID")); + propNames_name_store (PROP_TSKID, NTXT ("TSKID")); + propNames_name_store (PROP_PTSKID, NTXT ("PTSKID")); + propNames_name_store (PROP_PRPC, NTXT ("PRPC")); + + // Data race detection properties + propNames_name_store (PROP_RID, NTXT ("RID")); + propNames_name_store (PROP_RTYPE, NTXT ("RTYPE")); + propNames_name_store (PROP_LEAFPC, NTXT ("LEAFPC")); + propNames_name_store (PROP_RVADDR, NTXT ("RVADDR")); + propNames_name_store (PROP_RCNT, NTXT ("RCNT")); + + // Deadlock detection properties + propNames_name_store (PROP_DID, NTXT ("DID")); + propNames_name_store (PROP_DLTYPE, NTXT ("DLTYPE")); + propNames_name_store (PROP_DTYPE, NTXT ("DTYPE")); + propNames_name_store (PROP_DVADDR, NTXT ("DVADDR")); + + // Synthetic properties (queries only) + propNames_name_store (PROP_STACK, NTXT ("STACK")); + propNames_name_store (PROP_MSTACK, NTXT ("MSTACK")); + propNames_name_store (PROP_USTACK, NTXT ("USTACK")); + propNames_name_store (PROP_XSTACK, NTXT ("XSTACK")); + propNames_name_store (PROP_HSTACK, NTXT ("HSTACK")); + propNames_name_store (PROP_STACKID, NTXT ("STACKID")); + //propNames_name_store( PROP_CPRID, NTXT("CPRID") ); + //propNames_name_store( PROP_TSKID, NTXT("TSKID") ); + propNames_name_store (PROP_JTHREAD, NTXT ("JTHREAD"), + GTXT ("Java thread number"), TYPE_UINT64, 0); + + propNames_name_store (PROP_LEAF, NTXT ("LEAF")); + propNames_name_store (PROP_DOBJ, NTXT ("DOBJ")); + propNames_name_store (PROP_SAMPLE_MAP, NTXT ("SAMPLE_MAP")); + propNames_name_store (PROP_GCEVENT_MAP, NTXT ("GCEVENT_MAP")); + propNames_name_store (PROP_PID, NTXT ("PID"), + GTXT ("Process id"), TYPE_UINT64, 0); + propNames_name_store (PROP_EXPID, NTXT ("EXPID"), + GTXT ("Experiment id"), TYPE_UINT64, DDFLAG_NOSHOW); + propNames_name_store (PROP_EXPID_CMP, NTXT ("EXPID_CMP"), + GTXT ("Comparable Experiment Id"), TYPE_UINT64, + DDFLAG_NOSHOW); //YXXX find better description + propNames_name_store (PROP_EXPGRID, NTXT ("EXPGRID"), + GTXT ("Comparison Group id"), TYPE_UINT64, 0); + propNames_name_store (PROP_PARREG, NTXT ("PARREG")); + propNames_name_store (PROP_TSTAMP_LO, NTXT ("TSTAMP_LO"), + GTXT ("Start Timestamp (nanoseconds)"), TYPE_UINT64, 0); + propNames_name_store (PROP_TSTAMP_HI, NTXT ("TSTAMP_HI"), + GTXT ("End Timestamp (nanoseconds)"), TYPE_UINT64, 0); + propNames_name_store (PROP_TSTAMP2, NTXT ("TSTAMP2"), + GTXT ("End Timestamp (nanoseconds)"), TYPE_UINT64, + DDFLAG_NOSHOW); + propNames_name_store (PROP_FREQ_MHZ, NTXT ("FREQ_MHZ"), + GTXT ("CPU Frequency, MHz"), TYPE_UINT32, 0); + propNames_name_store (PROP_NTICK_USEC, NTXT ("NTICK_USEC"), + GTXT ("Clock Profiling Interval, Microseconds"), + TYPE_UINT64, 0); + + propNames_name_store (PROP_IOHEAPBYTES, NTXT ("IOHEAPBYTES")); + + propNames_name_store (PROP_STACKL, NTXT ("STACKL")); + propNames_name_store (PROP_MSTACKL, NTXT ("MSTACKL")); + propNames_name_store (PROP_USTACKL, NTXT ("USTACKL")); + propNames_name_store (PROP_XSTACKL, NTXT ("XSTACKL")); + + propNames_name_store (PROP_STACKI, NTXT ("STACKI")); + propNames_name_store (PROP_MSTACKI, NTXT ("MSTACKI")); + propNames_name_store (PROP_USTACKI, NTXT ("USTACKI")); + propNames_name_store (PROP_XSTACKI, NTXT ("XSTACKI")); + + // Make sure predefined names are not used for dynamic properties + propNames_name_store (PROP_LAST, NTXT ("")); + + localized_SP_UNKNOWN_NAME = GTXT ("(unknown)"); + + // define Index objects + dyn_indxobj = new Vector<IndexObjType_t*>(); + dyn_indxobj_indx = 0; + char *s = dbe_sprintf (NTXT ("((EXPID_CMP<<%llu) | THRID)"), + (unsigned long long) IndexObject::INDXOBJ_EXPID_SHIFT); + indxobj_define (NTXT ("Threads"), GTXT ("Threads"), s, NULL, NULL); + free (s); + indxobj_define (NTXT ("CPUs"), GTXT ("CPUs"), NTXT ("(CPUID)"), NULL, NULL); + indxobj_define (NTXT ("Samples"), GTXT ("Samples"), NTXT ("(SAMPLE_MAP)"), + NULL, NULL); + indxobj_define (NTXT ("GCEvents"), GTXT ("GCEvents"), NTXT ("(GCEVENT_MAP)"), + NULL, NULL); + indxobj_define (NTXT ("Seconds"), GTXT ("Seconds"), + NTXT ("(TSTAMP/1000000000)"), NULL, NULL); + indxobj_define (NTXT ("Processes"), GTXT ("Processes"), NTXT ("(EXPID_CMP)"), + NULL, NULL); + s = dbe_sprintf (NTXT ("((EXPGRID<<%llu) | (EXPID<<%llu))"), + (unsigned long long) IndexObject::INDXOBJ_EXPGRID_SHIFT, + (unsigned long long) IndexObject::INDXOBJ_EXPID_SHIFT); + indxobj_define (NTXT ("Experiment_IDs"), GTXT ("Experiment_IDs"), s, NULL, NULL); + free (s); + indxobj_define (NTXT ("Datasize"), GTXT ("Datasize"), + "(IOHEAPBYTES==0?0:" + "((IOHEAPBYTES<=(1<<0)?(1<<0):" + "((IOHEAPBYTES<=(1<<2)?(1<<2):" + "((IOHEAPBYTES<=(1<<4)?(1<<4):" + "((IOHEAPBYTES<=(1<<6)?(1<<6):" + "((IOHEAPBYTES<=(1<<8)?(1<<8):" + "((IOHEAPBYTES<=(1<<10)?(1<<10):" + "((IOHEAPBYTES<=(1<<12)?(1<<12):" + "((IOHEAPBYTES<=(1<<14)?(1<<14):" + "((IOHEAPBYTES<=(1<<16)?(1<<16):" + "((IOHEAPBYTES<=(1<<18)?(1<<18):" + "((IOHEAPBYTES<=(1<<20)?(1<<20):" + "((IOHEAPBYTES<=(1<<22)?(1<<22):" + "((IOHEAPBYTES<=(1<<24)?(1<<24):" + "((IOHEAPBYTES<=(1<<26)?(1<<26):" + "((IOHEAPBYTES<=(1<<28)?(1<<28):" + "((IOHEAPBYTES<=(1<<30)?(1<<30):" + "((IOHEAPBYTES<=(1<<32)?(1<<32):" + "((IOHEAPBYTES<=(1<<34)?(1<<34):" + "((IOHEAPBYTES<=(1<<36)?(1<<36):" + "((IOHEAPBYTES<=(1<<38)?(1<<38):" + "((IOHEAPBYTES<=(1<<40)?(1<<40):" + "((IOHEAPBYTES<=(1<<42)?(1<<42):" + "((IOHEAPBYTES<=(1<<44)?(1<<44):" + "((IOHEAPBYTES<=(1<<46)?(1<<46):" + "((IOHEAPBYTES<=(1<<48)?(1<<48):" + "((IOHEAPBYTES<=(1<<50)?(1<<50):" + "(IOHEAPBYTES==-1?-1:(1<<50|1)" + "))))))))))))))))))))))))))))))))))))))))))))))))))))))", + NULL, NULL); + indxobj_define (NTXT ("Duration"), GTXT ("Duration"), + "((TSTAMP_HI-TSTAMP_LO)==0?0:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000?1000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000?10000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000?100000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000?1000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000?10000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000000?100000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000000?1000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000000?10000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000000000?100000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000000000?1000000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000000000?10000000000000:" + "(10000000000001))))))))))))))))))))))))", NULL, NULL); + dyn_indxobj_indx_fixed = dyn_indxobj_indx; + Elf::elf_init (); + defExpName = NULL; + mach_model_loaded = NULL; + tmp_dir_name = NULL; + settings->read_rc (ipc_mode || rdt_mode); + + init (); +} + +DbeSession::~DbeSession () +{ + Destroy (views); + Destroy (exps); + Destroy (dobjs); + Destroy (metrics); + Destroy (search_path); + Destroy (classpath); + Destroy (propNames); + Destroy (expGroups); + Destroy (userLabels); + if (hwcentries) + { + for (long i = 0, sz = hwcentries->size (); i < sz; i++) + { + Hwcentry *h = hwcentries->get (i); + free (h->int_name); + free (h->name); + delete h; + } + delete hwcentries; + } + + if (idxobjs) + { + for (int i = 0; i < idxobjs->size (); ++i) + { + HashMap<uint64_t, Histable*> *hMap = idxobjs->get (i); + if (hMap) + { + hMap->values ()->destroy (); + delete hMap; + } + } + delete idxobjs; + } + + for (int i = 0; i < HTableSize; i++) + { + List *list = dnameHTable[i]; + while (list) + { + List *tmp = list; + list = list->next; + delete tmp; + } + } + delete[] dnameHTable; + delete classpath_df; + Destroy (objs); + Destroy (reg_metrics); + Destroy (dyn_indxobj); + delete lobjs; + delete f_special; + destroy_map (DbeFile *, dbeFiles); + destroy_map (DbeJarFile *, dbeJarFiles); + delete loadObjMap; + delete omp_functions; + delete sourcesMap; + delete sources; + delete comp_lobjs; + delete comp_dbelines; + delete comp_sources; + delete reg_metrics_tree; + delete settings; + free (mach_model_loaded); + + if (defExpName != NULL) + { + StringBuilder *sb = new StringBuilder (); + sb->append (NTXT ("/bin/rm -rf ")); + sb->append (defExpName); + char *cmd = sb->toString (); + system (cmd); + free (cmd); + delete sb; + free (defExpName); + } + unlink_tmp_files (); + delete tmp_files; + dbeSession = NULL; +} + +void +DbeSession::unlink_tmp_files () +{ + if (tmp_files) + { + for (int i = 0, sz = tmp_files->size (); i < sz; i++) + unlink (tmp_files->fetch (i)); + tmp_files->destroy (); + delete tmp_files; + tmp_files = NULL; + } + if (tmp_dir_name) + { + char *cmd = dbe_sprintf (NTXT ("/bin/rm -rf %s"), tmp_dir_name); + system (cmd); + free (cmd); + free (tmp_dir_name); + tmp_dir_name = NULL; + } +} + +char * +DbeSession::get_tmp_file_name (const char *nm, bool for_java) +{ + if (tmp_dir_name == NULL) + { + tmp_dir_name = dbe_sprintf (NTXT ("/tmp/analyzer.%llu.%lld"), + (unsigned long long) getuid (), (long long) getpid ()); + mkdir (tmp_dir_name, S_IRWXU); + } + char *fnm = dbe_sprintf (NTXT ("%s/%s"), tmp_dir_name, nm); + if (for_java) + for (char *s = fnm + strlen (tmp_dir_name) + 1; *s; s++) + if (*s == '/') + *s = '.'; + return fnm; +} + +void +DbeSession::init () +{ + user_exp_id_counter = 0; + status_ompavail = 0; + archive_mode = 0; + +#if DEBUG + char *s = getenv (NTXT ("MPMT_DEBUG")); + if (s) + mpmt_debug_opt = atoi (s); +#endif /* DEBUG */ + dbeFiles = new StringMap<DbeFile*>(); + dbeJarFiles = new StringMap<DbeJarFile*>(128, 128); + + // set up the initial (after .rc file reading) search path + set_search_path (settings->str_search_path, true); + userLabels = NULL; + + // Preset all objects as they may reuse each other + lo_unknown = NULL; + f_unknown = NULL; + j_unknown = NULL; + lo_total = NULL; + sf_unknown = NULL; + f_total = NULL; + f_jvm = NULL; + d_total = NULL; + d_scalars = NULL; + d_unknown = NULL; + expGroups->destroy (); + f_special->reset (); + for (int i = 0; i < LastSpecialFunction; i++) + f_special->append (NULL); + + lo_omp = NULL; + omp_functions->reset (); + for (int i = 0; i < OMP_LAST_STATE; i++) + omp_functions->append (NULL); + + // make sure the metric list is initialized + register_metric (Metric::SIZES); + register_metric (Metric::ADDRESS); + register_metric (Metric::ONAME); + + // This is needed only to maintain loadobject id's + // for <Total> and <Unknown> in tests + (void) get_Unknown_LoadObject (); + (void) get_Total_LoadObject (); + + // Create the data object name hash table. + dnameHTable = new List*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + dnameHTable[i] = NULL; + + d_total = createDataObject (); + d_total->set_name (NTXT ("<Total>")); + + // XXXX <Scalars> only appropriate for Program/Data-oriented analyses + d_scalars = createDataObject (); + d_scalars->set_name (GTXT ("<Scalars>")); + + d_unknown = createDataObject (); + d_unknown->set_name (GTXT ("<Unknown>")); + + // assign d_unknown's children so data_olayout has consistent sorting + for (unsigned pp_code = 1; pp_code < NUM_ABS_PP_CODES + 2; pp_code++) + { + char *errcode; + DataObject* dobj = createDataObject (); + switch (pp_code) + { + case NUM_ABS_PP_CODES + 1: + errcode = PTXT (DOBJ_UNDETERMINED); + break; + case NUM_ABS_PP_CODES: + errcode = PTXT (DOBJ_UNSPECIFIED); + break; + case NUM_ABS_PP_CODES - 1: + errcode = PTXT (DOBJ_UNIDENTIFIED); + break; + default: + errcode = PTXT (ABS_PP_CODES[pp_code]); + } + dobj->parent = d_unknown; + dobj->set_dobjname (errcode, NULL); // dobj->parent must already be set + } + + for (unsigned rt_code = 1; rt_code < NUM_ABS_RT_CODES - 1; rt_code++) + { + DataObject* dobj = createDataObject (); + dobj->parent = d_unknown; + dobj->set_dobjname (PTXT (ABS_RT_CODES[rt_code]), NULL); // dobj->parent must already be set + } +} + +void +DbeSession::reset_data () +{ + for (long i = 0, sz = VecSize (idxobjs); i < sz; ++i) + if (idxobjs->get (i)) + idxobjs->get (i)->reset (); +} + +void +DbeSession::reset () +{ + loadObjMap->reset (); + DbeView *dbev; + int index; + + Vec_loop (DbeView*, views, index, dbev) + { + dbev->reset (); + } + + destroy_map (DbeFile *, dbeFiles); + destroy_map (DbeJarFile *, dbeJarFiles); + exps->destroy (); + lobjs->reset (); // all LoadObjects belong to objs + dobjs->destroy (); // deletes d_unknown and d_total as well + objs->destroy (); + comp_lobjs->clear (); + comp_dbelines->clear (); + comp_sources->clear (); + sourcesMap->clear (); + sources->reset (); + + // Delete the data object name hash table. + for (int i = 0; i < HTableSize; i++) + { + List *list = dnameHTable[i]; + while (list) + { + List *tmp = list; + list = list->next; + delete tmp; + } + } + delete[] dnameHTable; + + // IndexObect definitions remain, objects themselves may go + for (int i = 0; i < idxobjs->size (); ++i) + { + HashMap<uint64_t, Histable*> *v = idxobjs->fetch (i); + if (v != NULL) + { + v->values ()->destroy (); + v->clear (); + } + } + init (); +} + +Vector<SourceFile*> * +DbeSession::get_sources () +{ + return sources; +} + +DbeFile * +DbeSession::getDbeFile (char *filename, int filetype) +{ + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeSession::getDbeFile filetype=0x%x %s\n"), filetype, filename); + if (strncmp (filename, NTXT ("./"), 2) == 0) + filename += 2; + DbeFile *dbeFile = dbeFiles->get (filename); + if (dbeFile == NULL) + { + dbeFile = new DbeFile (filename); + dbeFiles->put (filename, dbeFile); + } + dbeFile->filetype |= filetype; + return dbeFile; +} + +LoadObject * +DbeSession::get_Total_LoadObject () +{ + if (lo_total == NULL) + { + lo_total = createLoadObject (NTXT ("<Total>")); + lo_total->dbeFile->filetype |= DbeFile::F_FICTION; + } + return lo_total; +} + +Function * +DbeSession::get_Total_Function () +{ + if (f_total == NULL) + { + f_total = createFunction (); + f_total->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + f_total->set_name (NTXT ("<Total>")); + Module *mod = get_Total_LoadObject ()->noname; + f_total->module = mod; + mod->functions->append (f_total); + } + return f_total; +} + +LoadObject * +DbeSession::get_Unknown_LoadObject () +{ + if (lo_unknown == NULL) + { + lo_unknown = createLoadObject (GTXT ("<Unknown>")); + lo_unknown->type = LoadObject::SEG_TEXT; // makes it expandable + lo_unknown->dbeFile->filetype |= DbeFile::F_FICTION; + + // force creation of the <Unknown> function + (void) get_Unknown_Function (); + } + return lo_unknown; +} + +SourceFile * +DbeSession::get_Unknown_Source () +{ + if (sf_unknown == NULL) + { + sf_unknown = createSourceFile (localized_SP_UNKNOWN_NAME); + sf_unknown->dbeFile->filetype |= DbeFile::F_FICTION; + sf_unknown->flags |= SOURCE_FLAG_UNKNOWN; + } + return sf_unknown; +} + +Function * +DbeSession::get_Unknown_Function () +{ + if (f_unknown == NULL) + { + f_unknown = createFunction (); + f_unknown->flags |= FUNC_FLAG_SIMULATED; + f_unknown->set_name (GTXT ("<Unknown>")); + Module *mod = get_Unknown_LoadObject ()->noname; + f_unknown->module = mod; + mod->functions->append (f_unknown); + } + return f_unknown; +} + +// LIBRARY_VISIBILITY + +Function * +DbeSession::create_hide_function (LoadObject *lo) +{ + Function *h_function = createFunction (); + h_function->set_name (lo->get_name ()); + h_function->module = lo->noname; + h_function->isHideFunc = true; + lo->noname->functions->append (h_function); + return h_function; +} + +Function * +DbeSession::get_JUnknown_Function () +{ + if (j_unknown == NULL) + { + j_unknown = createFunction (); + j_unknown->flags |= FUNC_FLAG_SIMULATED; + j_unknown->set_name (GTXT ("<no Java callstack recorded>")); + Module *mod = get_Unknown_LoadObject ()->noname; + j_unknown->module = mod; + mod->functions->append (j_unknown); + } + return j_unknown; +} + +Function * +DbeSession::get_jvm_Function () +{ + if (f_jvm == NULL) + { + f_jvm = createFunction (); + f_jvm->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + f_jvm->set_name (GTXT ("<JVM-System>")); + + // Find the JVM LoadObject + LoadObject *jvm = get_Unknown_LoadObject (); + for (int i = 0; i < lobjs->size (); ++i) + { + LoadObject *lo = lobjs->fetch (i); + if (lo->flags & SEG_FLAG_JVM) + { + jvm = lo; + break; + } + } + Module *mod = jvm->noname; + f_jvm->module = mod; + mod->functions->append (f_jvm); + // XXXX is it required? no consistency among all special functions + // jvm->functions->append( f_jvm ); + } + return f_jvm; +} + +Function * +DbeSession::getSpecialFunction (SpecialFunction kind) +{ + if (kind < 0 || kind >= LastSpecialFunction) + return NULL; + + Function *func = f_special->fetch (kind); + if (func == NULL) + { + char *fname; + switch (kind) + { + case TruncatedStackFunc: + fname = GTXT ("<Truncated-stack>"); + break; + case FailedUnwindFunc: + fname = GTXT ("<Stack-unwind-failed>"); + break; + default: + return NULL; + } + func = createFunction (); + func->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + Module *mod = get_Total_LoadObject ()->noname; + func->module = mod; + mod->functions->append (func); + func->set_name (fname); + f_special->store (kind, func); + } + return func; +} + +LoadObject * +DbeSession::get_OMP_LoadObject () +{ + if (lo_omp == NULL) + { + for (int i = 0, sz = lobjs->size (); i < sz; i++) + { + LoadObject *lo = lobjs->fetch (i); + if (lo->flags & SEG_FLAG_OMP) + { + lo_omp = lo; + return lo_omp; + } + } + lo_omp = createLoadObject (GTXT ("<OMP>")); + lo_omp->type = LoadObject::SEG_TEXT; + lo_omp->dbeFile->filetype |= DbeFile::F_FICTION; + } + return lo_omp; +} + +Function * +DbeSession::get_OMP_Function (int n) +{ + if (n < 0 || n >= OMP_LAST_STATE) + return NULL; + + Function *func = omp_functions->fetch (n); + if (func == NULL) + { + char *fname; + switch (n) + { + case OMP_OVHD_STATE: + fname = GTXT ("<OMP-overhead>"); + break; + case OMP_IDLE_STATE: + fname = GTXT ("<OMP-idle>"); + break; + case OMP_RDUC_STATE: + fname = GTXT ("<OMP-reduction>"); + break; + case OMP_IBAR_STATE: + fname = GTXT ("<OMP-implicit_barrier>"); + break; + case OMP_EBAR_STATE: + fname = GTXT ("<OMP-explicit_barrier>"); + break; + case OMP_LKWT_STATE: + fname = GTXT ("<OMP-lock_wait>"); + break; + case OMP_CTWT_STATE: + fname = GTXT ("<OMP-critical_section_wait>"); + break; + case OMP_ODWT_STATE: + fname = GTXT ("<OMP-ordered_section_wait>"); + break; + case OMP_ATWT_STATE: + fname = GTXT ("<OMP-atomic_wait>"); + break; + default: + return NULL; + } + func = createFunction (); + func->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + func->set_name (fname); + + LoadObject *omp = get_OMP_LoadObject (); + func->module = omp->noname; + omp->noname->functions->append (func); + omp->functions->append (func); + omp_functions->store (n, func); + } + return func; +} + +// Divide the original createExperiment() into two steps +// In part1, we just create the data structure, in part2, if +// we decide to keep the experiment around, add it to various +// lists in DbeSession +Experiment * +DbeSession::createExperimentPart1 () +{ + Experiment *exp = new Experiment (); + return exp; +} + +void +DbeSession::createExperimentPart2 (Experiment *exp) +{ + int ind = expGroups->size (); + if (ind > 0) + { + ExpGroup *gr = expGroups->fetch (ind - 1); + exp->groupId = gr->groupId; + gr->append (exp); + } + exp->setExpIdx (exps->size ()); + exp->setUserExpId (++user_exp_id_counter); + exps->append (exp); +} + +Experiment * +DbeSession::createExperiment () +{ + Experiment *exp = new Experiment (); + append (exp); + return exp; +} + +void +DbeSession::append (Experiment *exp) +{ + exp->setExpIdx (exps->size ()); + exp->setUserExpId (++user_exp_id_counter); + exps->append (exp); + if (exp->founder_exp) + { + if (exp->founder_exp->children_exps == NULL) + exp->founder_exp->children_exps = new Vector<Experiment *>; + exp->founder_exp->children_exps->append (exp); + if (exp->founder_exp->groupId > 0) + { + exp->groupId = exp->founder_exp->groupId; + expGroups->get (exp->groupId - 1)->append (exp); + } + } + if (exp->groupId == 0) + { + long ind = VecSize (expGroups); + if (ind > 0) + { + ExpGroup *gr = expGroups->get (ind - 1); + exp->groupId = gr->groupId; + gr->append (exp); + } + } +} + +void +DbeSession::append (Hwcentry *h) +{ + if (hwcentries == NULL) + hwcentries = new Vector<Hwcentry*>; + hwcentries->append (h); +} + +int +DbeSession::ngoodexps () +{ + int cnt = 0; + for (long i = 0, sz = VecSize (exps); i < sz; i++) + if (exps->get (i)->get_status () == Experiment::SUCCESS) + cnt++; + return cnt; +} + +int +DbeSession::createView (int index, int cloneindex) +{ + // ensure that there is no view with that index + DbeView *dbev = getView (index); + if (dbev != NULL) + abort (); + + // find the view to be cloned + dbev = getView (cloneindex); + DbeView *newview; + if (dbev == NULL) + newview = new DbeView (theApplication, settings, index); + else + newview = new DbeView (dbev, index); + views->append (newview); + return index; +} + +DbeView * +DbeSession::getView (int index) +{ + int i; + DbeView *dbev; + Vec_loop (DbeView*, views, i, dbev) + { + if (dbev->vindex == index) + return dbev; + } + return NULL; +} + +void +DbeSession::dropView (int index) +{ + int i; + DbeView *dbev; + + Vec_loop (DbeView*, views, i, dbev) + { + if (dbev->vindex == index) + { + views->remove (i); + delete dbev; + return; + } + } + // view not found; ignore for now +} + +Vector<char*> * +DbeSession::get_group_or_expt (char *path) +{ + Vector<char*> *exp_list = new Vector<char*>; + FILE *fptr; + char *new_path, buf[MAXPATHLEN], name[MAXPATHLEN]; + + fptr = fopen (path, NTXT ("r")); + if (!fptr || !fgets (buf, (int) sizeof (buf), fptr) + || strncmp (buf, SP_GROUP_HEADER, strlen (SP_GROUP_HEADER))) + { + // it's not an experiment group + new_path = dbe_strdup (path); + new_path = canonical_path (new_path); + exp_list->append (new_path); + } + else + { + // it is an experiment group, read the list to get them all + while (fgets (buf, (int) sizeof (buf), fptr)) + { + if ((*buf != '#') && (sscanf (buf, NTXT ("%s"), name) == 1)) + { + new_path = dbe_strdup (name); + new_path = canonical_path (new_path); + exp_list->append (new_path); + } + } + } + if (fptr) + fclose (fptr); + return exp_list; +} + +#define GET_INT_VAL(v, s, len) \ + for (v = len = 0; isdigit(*s); s++, len++) { v = v * 10 + (*s -'0'); } + +static int +dir_name_cmp (const void *a, const void *b) +{ + char *s1 = *((char **) a); + char *s2 = *((char **) b); + while (*s1) + { + if (isdigit (*s1) && isdigit (*s2)) + { + int v1, v2, len1, len2; + GET_INT_VAL (v1, s1, len1); + GET_INT_VAL (v2, s2, len2); + if (v1 != v2) + return v1 - v2; + if (len1 != len2) + return len2 - len1; + continue; + } + if (*s1 != *s2) + break; + s1++; + s2++; + } + return *s1 - *s2; +} + +static int +read_experiment_data_in_parallel (void *arg) +{ + exp_ctx *ctx = (exp_ctx *) arg; + Experiment *dexp = ctx->exp; + bool read_ahead = ctx->read_ahead; + dexp->read_experiment_data (read_ahead); + free (ctx); + return 0; +} + +void +DbeSession::open_experiment (Experiment *exp, char *path) +{ + exp->open (path); + if (exp->get_status () != Experiment::FAILURE) + exp->read_experiment_data (false); + exp->open_epilogue (); + + // Update all DbeViews + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->add_experiment (exp->getExpIdx (), true); + } + + if (exp->get_status () == Experiment::FAILURE) + { + check_tab_avail (); + return; + } + + char *discard_tiny = getenv (NTXT ("SP_ANALYZER_DISCARD_TINY_EXPERIMENTS")); + int user_specified_tiny_threshold = DEFAULT_TINY_THRESHOLD; // in milliseconds + if (discard_tiny != NULL) + { + user_specified_tiny_threshold = (atoi (discard_tiny)); + if (user_specified_tiny_threshold < 0) + user_specified_tiny_threshold = DEFAULT_TINY_THRESHOLD; + } + + // Open descendant experiments + DIR *exp_dir = opendir (path); + if (exp_dir == NULL) + { + check_tab_avail (); + return; + } + + Vector<char*> *exp_names = new Vector<char*>(); + struct dirent *entry = NULL; + while ((entry = readdir (exp_dir)) != NULL) + { + if (entry->d_name[0] != '_') + continue; + size_t len = strlen (entry->d_name); + if (len < 3 || strcmp (entry->d_name + len - 3, NTXT (".er")) != 0) + continue; + exp_names->append (dbe_strdup (entry->d_name)); + } + closedir (exp_dir); + exp_names->sort (dir_name_cmp); + Experiment **t_exp_list = new Experiment *[exp_names->size ()]; + int nsubexps = 0; + + for (int j = 0, jsz = exp_names->size (); j < jsz; j++) + { + t_exp_list[j] = NULL; + + char *lineage_name = exp_names->fetch (j); + struct stat64 sbuf; + char *dpath = dbe_sprintf (NTXT ("%s/%s"), path, lineage_name); + + // look for experiments with no profile collected + if (user_specified_tiny_threshold == DEFAULT_TINY_THRESHOLD) + { + char *frinfoname = dbe_sprintf (NTXT ("%s/%s"), dpath, "data." SP_FRINFO_FILE); + int st = dbe_stat (frinfoname, &sbuf); + free (frinfoname); + if (st == 0) + { + // if no profile/trace data do not process this experiment any further + if (sbuf.st_size == 0) + { + free (dpath); + continue; + } + } + } + else + { // check if dpath is a directory + if (dbe_stat (dpath, &sbuf) != 0) + { + free (dpath); + continue; + } + else if (!S_ISDIR (sbuf.st_mode)) + { + free (dpath); + continue; + } + } + size_t lineage_name_len = strlen (lineage_name); + lineage_name[lineage_name_len - 3] = 0; /* remove .er */ + Experiment *dexp = new Experiment (); + dexp->founder_exp = exp; + if (user_specified_tiny_threshold > DEFAULT_TINY_THRESHOLD) + { + dexp->setTinyThreshold (user_specified_tiny_threshold); + dexp->open (dpath); + if (dexp->isDiscardedTinyExperiment ()) + { + delete dexp; + free (dpath); + continue; + } + } + else + dexp->open (dpath); + append (dexp); + t_exp_list[j] = dexp; + nsubexps++; + dexp->set_clock (exp->clock); + + // DbeView add_experiment() is split into two parts + // add_subexperiment() is called repeeatedly for + // all sub_experiments, later add_experiment_epilogue() finishes up the task + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + bool enabled = settings->check_en_desc (lineage_name, dexp->utargname); + dbev->add_subexperiment (dexp->getExpIdx (), enabled); + } + free (dpath); + } + + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->add_experiment_epilogue (); + } + + DbeThreadPool * threadPool = new DbeThreadPool (-1); + for (int j = 0, jsz = exp_names->size (); j < jsz; j++) + { + if (t_exp_list[j] == NULL) continue; + Experiment *dexp = t_exp_list[j]; + exp_ctx *new_ctx = (exp_ctx*) malloc (sizeof (exp_ctx)); + new_ctx->path = NULL; + new_ctx->exp = dexp; + new_ctx->ds = this; + new_ctx->read_ahead = true; + DbeQueue *q = new DbeQueue (read_experiment_data_in_parallel, new_ctx); + threadPool->put_queue (q); + } + threadPool->wait_queues (); + delete threadPool; + + for (long j = 0, jsz = exp_names->size (); j < jsz; j++) + { + if (t_exp_list[j] == NULL) continue; + Experiment *dexp = t_exp_list[j]; + dexp->open_epilogue (); + } + exp_names->destroy (); + delete[] t_exp_list; + delete exp_names; + + // update setting for leaklist and dataspace + check_tab_avail (); +} + +void +DbeSession::append_mesgs (StringBuilder *sb, char *path, Experiment *exp) +{ + if (exp->fetch_errors () != NULL) + { + // yes, there were errors + char *ststr = pr_mesgs (exp->fetch_errors (), NTXT (""), NTXT ("")); + sb->append (path); + sb->append (NTXT (": ")); + sb->append (ststr); + free (ststr); + } + + Emsg *m = exp->fetch_warnings (); + if (m != NULL) + { + sb->append (path); + sb->append (NTXT (": ")); + if (!is_interactive ()) + sb->append (GTXT ("Experiment has warnings, see header for details\n")); + else + sb->append (GTXT ("Experiment has warnings, see experiment panel for details\n")); + } + + // Check for descendant experiments that are not loaded + int num_desc = VecSize (exp->children_exps); + if ((num_desc > 0) && !settings->check_en_desc (NULL, NULL)) + { + char *s; + if (!is_interactive ()) + s = dbe_sprintf (GTXT ("Has %d descendant(s), use commands controlling selection to load descendant data\n"), num_desc); + else + s = dbe_sprintf (GTXT ("Has %d descendant(s), use filter panel to load descendant data\n"), num_desc); + sb->append (path); + sb->append (NTXT (": ")); + sb->append (s); + free (s); + } +} + +Experiment * +DbeSession::get_exp (int exp_ind) +{ + if (exp_ind < 0 || exp_ind >= exps->size ()) + return NULL; + Experiment *exp = exps->fetch (exp_ind); + exp->setExpIdx (exp_ind); + return exp; +} + +Vector<Vector<char*>*> * +DbeSession::getExperimensGroups () +{ + if (dbeSession->expGroups == NULL || dbeSession->expGroups->size () == 0) + return NULL; + bool compare_mode = expGroups->size () > 1; + Vector<Vector<char*>*> *groups = new Vector<Vector<char*>*> ( + compare_mode ? expGroups->size () : 1); + for (int i = 0; i < expGroups->size (); i++) + { + ExpGroup *grp = expGroups->fetch (i); + Vector<Experiment*> *founders = grp->get_founders (); + if (founders && founders->size () != 0) + { + Vector<char *> *names = new Vector<char*> (founders->size ()); + for (int j = 0; j < founders->size (); j++) + { + Experiment *exp = founders->fetch (j); + names->append (dbe_strdup (exp->get_expt_name ())); + } + if (compare_mode || groups->size () == 0) + groups->append (names); + else + groups->fetch (0)->addAll (names); + } + delete founders; + } + return groups; +} + +char * +DbeSession::setExperimentsGroups (Vector<Vector<char*>*> *groups) +{ + StringBuilder sb; + for (int i = 0; i < groups->size (); i++) + { + Vector<char *> *names = groups->fetch (i); + ExpGroup *grp; + if (names->size () == 1) + grp = new ExpGroup (names->fetch (0)); + else + { + char *nm = dbe_sprintf (GTXT ("Group %d"), i + 1); + grp = new ExpGroup (nm); + free (nm); + } + expGroups->append (grp); + grp->groupId = expGroups->size (); + + for (int j = 0; j < names->size (); j++) + { + char *path = names->fetch (j); + size_t len = strlen (path); + if ((len > 4) && !strcmp (path + len - 4, NTXT (".erg"))) + { + Vector<char*> *lst = get_group_or_expt (path); + for (int j1 = 0; j1 < lst->size (); j1++) + { + Experiment *exp = new Experiment (); + append (exp); + open_experiment (exp, lst->get (j1)); + if (exp->get_status () == Experiment::FAILURE) + append_mesgs (&sb, path, exp); + } + lst->destroy (); + delete lst; + } + else + { + Experiment *exp = new Experiment (); + append (exp); + open_experiment (exp, path); + if (exp->get_status () == Experiment::FAILURE) + append_mesgs (&sb, path, exp); + } + } + } + + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->update_advanced_filter (); + int cmp = dbev->get_settings ()->get_compare_mode (); + dbev->set_compare_mode (CMP_DISABLE); + dbev->set_compare_mode (cmp); + } + return sb.length () == 0 ? NULL : sb.toString (); +} + +char * +DbeSession::drop_experiment (int exp_ind) +{ + DbeView *dbev; + int index; + Experiment *exp2; + + status_ompavail = -1; + Experiment *exp = exps->fetch (exp_ind); + + // If this is a sub experiment, don't do it + if (exp->founder_exp != NULL) // this is a sub experiment; don't do it + return (dbe_strdup (GTXT ("Can not drop subexperiments"))); + + if (VecSize (exp->children_exps) > 0) + for (;;) + { + // search the list of experiments to find all that have this one as founder + bool found = false; + Vec_loop (Experiment*, exps, index, exp2) + { + if (exp2->founder_exp == exp) + { + exp2->founder_exp = NULL; + drop_experiment (index); + found = true; + break; + } + } + if (found == false) + break; + } + + // then proceed to finish the drop + Vec_loop (DbeView*, views, index, dbev) + { + dbev->drop_experiment (exp_ind); + } + + int old_cnt = expGroups->size (); + for (int i = 0; i < old_cnt; i++) + { + ExpGroup *gr = expGroups->fetch (i); + if (gr->groupId == exp->groupId) + { + gr->drop_experiment (exp); + if ((gr->founder == NULL) && (gr->exps->size () == 0)) + { + delete gr; + expGroups->remove (i); + } + break; + } + } + delete exps->remove (exp_ind); + if (old_cnt != expGroups->size ()) + { + for (int i = 0, sz = expGroups->size (); i < sz; i++) + { + ExpGroup *gr = expGroups->fetch (i); + gr->groupId = i + 1; + Vector<Experiment*> *expList = gr->exps; + for (int i1 = 0, sz1 = expList->size (); i1 < sz1; i1++) + expList->fetch (i1)->groupId = gr->groupId; + } + for (int i = 0, sz = views->size (); i < sz; i++) + { + dbev = views->fetch (i); + int cmp = dbev->get_compare_mode (); + dbev->set_compare_mode (CMP_DISABLE); + dbev->set_compare_mode (cmp); + } + } + check_tab_avail (); // update tab availability + return NULL; +} + +int +DbeSession::find_experiment (char *path) +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (strcmp (exp->get_expt_name (), path) == 0) + return exp->getExpIdx (); + } + return -1; +} + +LoadObject * +DbeSession::createLoadObject (const char *nm, int64_t cksum) +{ + return loadObjMap->sync_create_item (nm, cksum); +} + +LoadObject * +DbeSession::createLoadObject (const char *nm, const char *runTimePath, DbeFile *df) +{ + return loadObjMap->sync_create_item (nm, runTimePath, df); +} + +void +DbeSession::append (LoadObject *lobj) +{ + Histable *obj = lobj; // workaround for a C++ problem + objs->append (obj); + lobj->id = objs->size () - 1; + lobjs->append (lobj); + lobj->seg_idx = lobjs->size () - 1; + char *loname = lobj->get_pathname (); + dbeFiles->put (loname, lobj->dbeFile); +} + +DbeJarFile * +DbeSession::get_JarFile (const char *name) +{ + DbeJarFile *jf = dbeJarFiles->get (name); + if (jf == NULL) + { + jf = new DbeJarFile (name); + dbeJarFiles->put (name, jf); + } + return jf; +} + +Module * +DbeSession::createModule (LoadObject *lo, const char *nm) +{ + Module *mod = new Module (); + Histable *obj = mod; // workaround for a C++ problem + objs->append (obj); + mod->id = objs->size () - 1; + mod->loadobject = lo; + mod->set_name (dbe_strdup (nm ? nm : localized_SP_UNKNOWN_NAME)); + lo->seg_modules->append (mod); + return mod; +} + +Module * +DbeSession::createUnknownModule (LoadObject *lo) +{ + Module *mod = createModule (lo, localized_SP_UNKNOWN_NAME); + mod->flags |= MOD_FLAG_UNKNOWN; + mod->set_file_name (dbe_strdup (localized_SP_UNKNOWN_NAME)); + return mod; +} + +SourceFile * +DbeSession::createSourceFile (const char *_path) +{ + char *path = (char *) _path; + if (strncmp (path, NTXT ("./"), 2) == 0) + path += 2; + SourceFile *source = sourcesMap->get (path); + if (source == NULL) + { + source = new SourceFile (path); + (void) sourcesMap->put (path, source); + append (source); + } + return source; +} + +Function * +DbeSession::createFunction () +{ + Function *func = new Function (objs->size ()); + Histable *obj = func; // workaround for a C++ problem + objs->append (obj); + return func; +} + +JMethod * +DbeSession::createJMethod () +{ + JMethod *jmthd = new JMethod (objs->size ()); + Histable *obj = jmthd; // workaround for a C++ problem + objs->append (obj); + return jmthd; +} + +Module * +DbeSession::createClassFile (char *className) +{ + ClassFile *cls = new ClassFile (); + cls->set_name (className); + char *clpath = cls->get_java_file_name (className, true); + cls->dbeFile = getDbeFile (clpath, DbeFile::F_JAVACLASS); + free (clpath); + Histable *obj = cls; // workaround for a C++ problem + objs->append (obj); + cls->id = objs->size () - 1; + return cls; +} + +Histable * +DbeSession::createHistObject (Histable::Type type) +{ + switch (type) + { + case Histable::DOBJECT: + { + DataObject *dataobj = new DataObject (); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; + } + default: + assert (0); + } + return NULL; +} + +DataObject * +DbeSession::createDataObject () +{ + DataObject *dataobj = new DataObject (); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; +} + +DataObject * +DbeSession::createDataObject (DataObject *dobj, DataObject *parent) +{ + DataObject *dataobj = new DataObject (); + dataobj->size = dobj->size; + dataobj->offset = dobj->offset; + dataobj->parent = parent; + dataobj->set_dobjname (dobj->get_typename (), dobj->get_instname ()); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; +} + +DataObject * +DbeSession::createMasterDataObject (DataObject *dobj) +{ + DataObject *parent = NULL; + if (dobj->parent) + { // define master parent first + parent = find_dobj_master (dobj->parent); + if (!parent) + { // clone master from this dataobject parent + parent = createDataObject (dobj->parent); + parent->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, + "Master DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) parent->id, (ull_t) dobj->parent->id, + dobj->parent->get_name ()); + // clone master DataObject elements + Vector<DataObject*> *delem = get_dobj_elements (dobj->parent); + int element_index = 0; + DataObject *element = NULL; + Vec_loop (DataObject*, delem, element_index, element) + { + DataObject *master_element = createDataObject (element, parent); + master_element->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, + "Member DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) master_element->id, (ull_t) element->id, + element->get_name ()); + } + } + else + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) clone found (%llu) %s\n", + (ull_t) parent->id, (ull_t) dobj->parent->id, + dobj->parent->get_name ()); + } + + DataObject *master = find_dobj_master (dobj); + if (!master) + { // clone master from this dataobject + master = createDataObject (dobj, parent); + master->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) master->id, (ull_t) dobj->id, dobj->get_name ()); + } + else + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) clone found (%llu) %s\n", + (ull_t) master->id, (ull_t) dobj->id, dobj->get_name ()); + return master; +} + +void +DbeSession::insert_metric (BaseMetric *mtr, Vector<BaseMetric*> *mlist) +{ + if ((mtr->get_flavors () & Metric::STATIC) == 0) + { + // insert in front of the first STATIC + for (int i = 0, mlist_sz = mlist->size (); i < mlist_sz; i++) + { + BaseMetric *m = mlist->fetch (i); + if (m->get_flavors () & Metric::STATIC) + { + mlist->insert (i, mtr); + return; + } + } + } + mlist->append (mtr); +} + +BaseMetricTreeNode* +DbeSession::get_reg_metrics_tree () +{ + if (reg_metrics_tree == NULL) + // Can't init earlier because BaseMetric() requires DbeSession::ql_parse + reg_metrics_tree = new BaseMetricTreeNode (); + return reg_metrics_tree; +} + +void +DbeSession::update_metric_tree (BaseMetric *m) +{ + get_reg_metrics_tree ()->register_metric (m); +} + +BaseMetric * +DbeSession::register_metric_expr (BaseMetric::Type type, char *cmd, char *expr_spec) +{ + BaseMetric *m = find_metric (type, cmd, expr_spec); + if (m) + return m; + BaseMetric *bm = find_metric (type, cmd, NULL); // clone this version + m = new BaseMetric (*bm); + m->set_expr_spec (expr_spec); + insert_metric (m, reg_metrics); + return m; +} + +BaseMetric * +DbeSession::register_metric (BaseMetric::Type type) +{ + BaseMetric *m = find_metric (type, NULL, NULL); + if (m) + return m; + m = new BaseMetric (type); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +BaseMetric * +DbeSession::register_metric (Hwcentry *ctr, const char* aux, const char* username) +{ + BaseMetric *m = find_metric (BaseMetric::HWCNTR, aux, NULL); + if (m) + // That may be a problem when metrics aren't an exact match. + // For example, memoryspace is disabled in one experiment and not in another. + return m; + if (ctr->timecvt) + { + char *time_cmd = dbe_sprintf (NTXT ("t%s"), aux); + char *time_username = dbe_sprintf (GTXT ("%s Time"), + ctr->metric ? ctr->metric : + (ctr->name ? ctr->name : ctr->int_name)); + BaseMetric *m1; + if (ipc_mode) + { + // Two visible metrics are presented in GUI + m1 = new BaseMetric (ctr, aux, time_cmd, time_username, VAL_TIMEVAL); + insert_metric (m1, reg_metrics); + update_metric_tree (m1); + m = new BaseMetric (ctr, aux, username, VAL_VALUE, m1); + } + else + { + // Only one visible metric is presented in er_print + m1 = new BaseMetric (ctr, aux, time_cmd, time_username, VAL_TIMEVAL | VAL_INTERNAL); + insert_metric (m1, reg_metrics); + m = new BaseMetric (ctr, aux, username, VAL_TIMEVAL | VAL_VALUE, m1); + } + free (time_cmd); + free (time_username); + } + else + m = new BaseMetric (ctr, aux, username, VAL_VALUE); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +BaseMetric * +DbeSession::register_metric (char *name, char *username, char *_def) +{ + BaseMetric *m = find_metric (BaseMetric::DERIVED, name, NULL); + if (m) + return m; + Definition *p = Definition::add_definition (_def); + if (p == NULL) + return NULL; + m = new BaseMetric (name, username, p); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +void +DbeSession::drop_metric (BaseMetric *mtr) +{ + Countable *cnt; + int index; + + Vec_loop (Countable*, metrics, index, cnt) + { + if (mtr == (BaseMetric *) cnt->item) + { + cnt->ref_count--; + if (cnt->ref_count == 0) + { + // Remove this metric from all views + DbeView *dbev; + int index2; + Vec_loop (DbeView*, views, index2, dbev) + { + dbev->reset_metrics (); + } + delete metrics->remove (index); + delete mtr; + return; + } + } + } +} + +BaseMetric * +DbeSession::find_metric (BaseMetric::Type type, const char *cmd, const char *expr_spec) +{ + for (int i = 0, sz = reg_metrics->size (); i < sz; i++) + { + BaseMetric *bm = reg_metrics->fetch (i); + if (bm->get_type () == type && dbe_strcmp (bm->get_expr_spec (), expr_spec) == 0) + { + if ((type == BaseMetric::DERIVED || type == BaseMetric::HWCNTR) + && dbe_strcmp (bm->get_cmd (), cmd) != 0) + continue; + return bm; + } + } + return NULL; +} + +BaseMetric * +DbeSession::find_base_reg_metric (char * mcmd) +{ + for (int i = 0, sz = reg_metrics->size (); i < sz; i++) + { + BaseMetric *bm = reg_metrics->fetch (i); + if (bm->get_expr_spec () != NULL) + continue; // skip compare metrics + if (dbe_strcmp (bm->get_cmd (), mcmd) == 0) + return bm; + } + return NULL; +} + +Vector<BaseMetric*> * +DbeSession::get_base_reg_metrics () +{ + Vector<BaseMetric*> *mlist = new Vector<BaseMetric*>; + Vector<BaseMetric*> *ml = get_all_reg_metrics (); + for (int i = 0, sz = ml->size (); i < sz; i++) + { + BaseMetric *m = ml->fetch (i); + if (m->get_expr_spec () == NULL) + mlist->append (m); + } + return mlist; +} + +void +DbeSession::check_tab_avail () +{ + DbeView *dbev; + int index; + // tell the views to update their tab lists + Vec_loop (DbeView*, views, index, dbev) + { + dbev->get_settings ()->updateTabAvailability (); + } +} + +bool +DbeSession::is_datamode_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->dataspaceavail) + return true; + } + return false; +} + +bool +DbeSession::is_leaklist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->leaklistavail) + return true; + } + return false; +} + +bool +DbeSession::is_heapdata_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->heapdataavail) + return true; + } + return false; +} + +bool +DbeSession::is_iodata_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->iodataavail) + return true; + } + return false; +} + +bool +DbeSession::is_racelist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->racelistavail) + return true; + } + return false; +} + +bool +DbeSession::is_deadlocklist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->deadlocklistavail) + return true; + } + return false; +} + +bool +DbeSession::is_timeline_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->timelineavail) + return true; + } + return false; +} + +bool +DbeSession::is_ifreq_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->ifreqavail) + return true; + } + return false; +} + +bool +DbeSession::is_omp_available () +{ + if (status_ompavail == -1) + { + status_ompavail = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->ompavail) + { + status_ompavail = 1; + break; + } + } + } + return status_ompavail == 1; +} + +bool +DbeSession::has_java () +{ + int status_has_java = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->has_java) + { + status_has_java = 1; + break; + } + } + return status_has_java == 1; +} + +bool +DbeSession::has_ompavail () +{ + int status_has_ompavail = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->ompavail) + { + status_has_ompavail = 1; + break; + } + } + return status_has_ompavail == 1; +} + +int +DbeSession::get_clock (int whichexp) +{ + // XXXX clock frequency should be an attribute of each CPU, + // XXX and not a property of the session + // if whichexp is -1, pick the first exp that has a clock + // otherwise return the clock from the numbered experiment + Experiment *exp; + if (whichexp != -1) + { + exp = get_exp (whichexp); + if (exp != NULL) + return exp->clock; + return 0; + } + int n = nexps (); + for (int i = 0; i < n; i++) + { + exp = get_exp (i); + if (exp != NULL && exp->clock != 0) + return exp->clock; + } + return 0; +} + +LoadObject * +DbeSession::find_lobj_by_name (const char *lobj_name, int64_t cksum) +{ + return loadObjMap->get (lobj_name, cksum); +} + +static unsigned +hash (char *s) +{ + unsigned res = 0; + for (int i = 0; i < 64 && *s; i++) + res = res * 13 + *s++; + return res; +} + +// This method is introduced to fix performance +// problems with the data space profiling in the +// current release. A better design is desired. +void +DbeSession::dobj_updateHT (DataObject *dobj) +{ + unsigned index = hash (dobj->get_unannotated_name ()) % HTableSize; + List *list = new List; + list->val = (void*) dobj; + list->next = dnameHTable[index]; + dnameHTable[index] = list; +} + +DataObject * +DbeSession::find_dobj_by_name (char *dobj_name) +{ + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + if (strcmp (d->get_unannotated_name (), dobj_name) == 0) + return d; + } + return (DataObject *) NULL; +} + +DataObject * +DbeSession::find_dobj_match (DataObject *dobj) +{ + char *dobj_name = dobj->get_unannotated_name (); + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + if (strcmp (d->get_unannotated_name (), dobj_name) == 0 + && d->size == dobj->size && d->offset == dobj->offset + && d->scope == dobj->scope) + return d; + } + return (DataObject *) NULL; +} + +DataObject * +DbeSession::find_dobj_master (DataObject *dobj) +{ + char *dobj_name = dobj->get_unannotated_name (); + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + // XXXX should parent also match? + if (strcmp (d->get_unannotated_name (), dobj_name) == 0 + && d->size == dobj->size && d->offset == dobj->offset + && d->master == NULL && d->scope == NULL) + return d; + } + return (DataObject *) NULL; +} + +Vector<DataObject*>* +DbeSession::get_dobj_elements (DataObject *dobj) +{ + DataObject *d; + int index; + Vector<DataObject*> *elements = new Vector<DataObject*>; + if (dobj == d_total) + return elements; + Vec_loop (DataObject*, dobjs, index, d) + { + if (d->get_parent () && d->get_parent () == dobj) + elements->append (d); + } + return elements; +} + +Vector<LoadObject*>* +DbeSession::get_text_segments () +{ + LoadObject *lo; + int index; + Vector<LoadObject*> *tlobjs = new Vector<LoadObject*>; + Vec_loop (LoadObject*, lobjs, index, lo) + { + if (lo->type == LoadObject::SEG_TEXT) + tlobjs->append (lo); + } + return tlobjs; +} + +static long long +getNumber (const char *s, char * &last) +{ + long long val; + char *sp; + errno = 0; + val = strtoll (s, &sp, 0); + if (errno == EINVAL) + last = NULL; + else + { + while (isspace (*sp)) + sp++; + last = sp; + } + return (val); +} + +bool +DbeSession::find_obj (FILE *dis_file, FILE *inp_file, Histable *&obj, + char *name, const char *sel, Histable::Type type, bool xdefault) +{ + Vector<Histable*> *obj_lst; + int which = -1; + char *last = NULL; + if (type != Histable::FUNCTION && sel) + { + // check that a number has been provided + which = (int) getNumber (sel, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + sel = NULL; + which = 0; + } + which--; + } + obj_lst = new Vector<Histable*>; + switch (type) + { + case Histable::FUNCTION: + obj = map_NametoFunction (name, obj_lst, sel); + break; + case Histable::MODULE: + obj = map_NametoModule (name, obj_lst, which); + break; + case Histable::LOADOBJECT: + obj = map_NametoLoadObject (name, obj_lst, which); + break; + case Histable::DOBJECT: + obj = map_NametoDataObject (name, obj_lst, which); + break; + default: + abort (); // unexpected Histable! + } + + if ((obj == NULL) && (obj_lst->size () > 0)) + { + if (obj_lst->size () == 1) + which = 0; + else + { + if (sel && (which < 0 || which >= obj_lst->size ())) + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + if (xdefault) + { + fprintf (stderr, GTXT ("Default selection \"1\" made\n")); + which = 0; + } + else + { + which = ask_which (dis_file, inp_file, obj_lst, name); + if (which == -1) + { + delete obj_lst; + return false; + } + } + } + obj = obj_lst->fetch (which); + } + delete obj_lst; + return true; +} + +int +DbeSession::ask_which (FILE *dis_file, FILE *inp_file, + Vector<Histable*> *list, char *name) +{ + Histable *hitem; + Function *func; + Module *module; + int which, index, index1; + char *item_name, *lo_name, *fname, *last; + char buf[BUFSIZ]; + for (;;) + { + fprintf (dis_file, GTXT ("Available name list:\n")); + fprintf (dis_file, GTXT ("%8d) Cancel\n"), 0); + Vec_loop (Histable*, list, index, hitem) + { + index1 = index + 1; + item_name = hitem->get_name (); + switch (hitem->get_type ()) + { + case Histable::FUNCTION: + func = (Function *) hitem; + module = func->module; + + // id == -1 indicates er_src invocation + if (module == NULL || (module->lang_code == Sp_lang_java + && module->loadobject->id == -1)) + fprintf (dis_file, NTXT ("%8d) %s\n"), index1, item_name); + else + { + lo_name = module->loadobject->get_pathname (); + fname = (module->file_name && *module->file_name) ? + module->file_name : module->get_name (); + if (fname && *fname) + fprintf (dis_file, NTXT ("%8d) %s %s:0x%llx (%s)\n"), index1, + item_name, lo_name, (ull_t) func->img_offset, fname); + else + fprintf (dis_file, NTXT ("%8d) %s %s:0x%llx\n"), index1, + item_name, lo_name, (ull_t) func->img_offset); + } + break; + case Histable::MODULE: + module = (Module *) hitem; + lo_name = module->loadobject->get_pathname (); + if (name[strlen (name) - 1] == + module->file_name[strlen (module->file_name) - 1]) + fprintf (dis_file, NTXT ("%8d) %s(%s)\n"), index1, + module->file_name, lo_name); + else + fprintf (dis_file, NTXT ("%8d) %s(%s)\n"), index1, item_name, + lo_name); + break; + default: + fprintf (dis_file, NTXT ("%8d) %s\n"), index1, item_name); + break; + } + } + if (inp_file != stdin) + return -1; + fprintf (dis_file, GTXT ("Enter selection: ")); + if (fgets (buf, (int) sizeof (buf), inp_file) == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid number entered:\n")); + return -1; + } + which = (int) getNumber (buf, last); + if (last && *last == '\0') + if (which >= 0 && which <= list->size ()) + return which - 1; + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), buf); + } +} + +static bool +match_basename (char *name, char *full_name, int len = -1) +{ + if (full_name == NULL) + return false; + if (!strchr (name, '/')) + full_name = get_basename (full_name); + if (len == -1) + return streq (name, full_name); + return strncmp (name, full_name, len) == 0; +} + +LoadObject * +DbeSession::map_NametoLoadObject (char *name, Vector<Histable*> *list, int which) +{ + // Search the tree to find the first module whose module name + // matches "name" or whose source file name matches "name" + // Issues: is the name a pathname, or a base name? + // Should we look at suffix to disambiguate? + LoadObject *loitem; + int index; + Vec_loop (LoadObject*, lobjs, index, loitem) + { + // try pathname first + // if failed, try object name next + if (match_basename (name, loitem->get_pathname ()) || + match_basename (name, loitem->get_name ())) + { + if (which == list->size ()) + return loitem; + list->append (loitem); + } + } + return (LoadObject *) NULL; +} + +Module * +DbeSession::map_NametoModule (char *name, Vector<Histable*> *list, int which) +{ + // Search the tree to find the first loadobject whose loadobject name + // matches "name". + + // Issues: is the name a pathname, or a base name? + // Should we look at suffix to disambiguate? + LoadObject *loitem; + Module *mitem; + int index1, index2; + Vec_loop (LoadObject*, lobjs, index1, loitem) + { + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + // try source name first + // if failed, try object name next + if (match_basename (name, mitem->file_name) || + match_basename (name, mitem->get_name ())) + { + if (which == list->size ()) + return mitem; + list->append (mitem); + } + } + } + return (Module *) NULL; +} + +Function * +DbeSession::map_NametoFunction (char *name, Vector<Histable*> *list, + const char *sel) +{ + // Search the tree to find the first function whose + // name matches "name". + // Issues: is the name a full name, or a short name? + // Is it a demangled name? If so, what about spaces + // within the name? + // Is there a way to return all names that match? + // How can the user specify a particular function of that name? + LoadObject *loitem; + Function *fitem, *main_func = NULL; + Module *mitem, *main_mod = NULL; + int index1, index2, index3, which = -1; + if (sel) + { + char *last = NULL; + if (*sel == '@') + { // 'sel' is "@seg_num:address" + which = (int) getNumber (sel + 1, last); + if (last == NULL || *last != ':' || (which < 0) || (which >= lobjs->size ())) + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + uint64_t address = getNumber (last + 1, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + loitem = lobjs->fetch (which); + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + Vec_loop (Function*, mitem->functions, index3, fitem) + { + if (address == fitem->img_offset && match_FName (name, fitem)) + return fitem; + } + } + return NULL; + } + + which = (int) getNumber (sel, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + which--; + } + + int len_path = 0; + char *with_path = name; + name = StrRchr (name, '`'); + if (name != with_path) + len_path = (int) (name - with_path); + else + with_path = NULL; + + Vec_loop (LoadObject*, lobjs, index1, loitem) + { + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + if (with_path) + { // with file name + // try source name first + // if failed, try object name next + if (!match_basename (with_path, mitem->file_name, len_path) && + !match_basename (with_path, mitem->get_name (), len_path)) + continue; + } + Vec_loop (Function*, mitem->functions, index3, fitem) + { + if (match_FName (name, fitem)) + { + if (which == list->size ()) + return fitem; + list->append (fitem); + continue; + } + if (streq (fitem->get_name (), NTXT ("MAIN_")) && mitem->is_fortran ()) + { + main_func = fitem; + main_mod = mitem; + } + } + } + } + + if (main_mod && main_func) + { + main_mod->read_stabs (); + if (streq (main_func->get_match_name (), name) && which <= 1) + return main_func; + } + return (Function *) NULL; +} + +DataObject * +DbeSession::map_NametoDataObject (char *name, Vector<Histable*> *list, + int which) +{ + // Search master list to find dataobjects whose names match "name" + // selecting only the entry corresponding to "which" if it is not -1. + // Issues: is the name fully qualified or only partially? + DataObject *ditem = NULL; + int index; + char *full_name; + Vec_loop (DataObject*, dobjs, index, ditem) + { + if (ditem->scope) continue; // skip non-master dataobjects + + // try fully-qualified dataobject name first + if ((full_name = ditem->get_name ()) != NULL) + { + if (streq (name, full_name)) + { + if (which == list->size ()) + return ditem; + list->append (ditem); + } + } + } + if (list->size () > 0) + return ditem; // return fully-qualified match + + // if fully-qualified name doesn't match anything, try a partial match + Vec_loop (DataObject*, dobjs, index, ditem) + { + if (ditem->scope) continue; // skip non-master dataobjects + + // try fully-qualified dataobject name first + if ((full_name = ditem->get_name ()) != NULL) + { + if (strstr (full_name, name)) + { + if (which == list->size ()) + return ditem; + list->append (ditem); + } + } + } + return (DataObject *) NULL; +} + +bool +DbeSession::match_FName (char *name, Function *func) +{ + size_t len; + char buf[MAXDBUF]; + char *full_name; + if (streq (func->get_name (), name)) // try full name comparison + return true; + if (streq (func->get_mangled_name (), name)) // try mangled name + return true; + if (streq (func->get_match_name (), name)) // try match name + return true; + + Module *md = func->module; // try FORTRAN name + if (md && md->is_fortran ()) + { + char *mangled_name = func->get_mangled_name (); + len = strlen (name); + if (((len + 1) == strlen (mangled_name)) && + (strncmp (name, mangled_name, len) == 0)) + return true; + } + snprintf (buf, sizeof (buf), NTXT ("%s"), func->get_name ()); + full_name = buf; + char *arg = NULL; // find modifier and C++ class name + int i = get_paren (buf); + if (i >= 0) + { + arg = buf + i; + *arg = '\0'; + } + + char *mod = strchr (full_name, ' '); + char *cls = strchr (full_name, ':'); + + if (mod) + { + len = mod - full_name + 1; + if (!strncmp (full_name, name, len)) + name += len; + full_name += len; + if (streq (full_name, name)) // try without modifier + return true; + } + + size_t len_cmp = strlen (name); + if (arg) + { + *arg = '('; + len = arg - full_name; // try without 'args' + if (len_cmp == len && !strncmp (full_name, name, len)) + return true; + if (cls) + { + len = arg - cls - 2; // and without 'class name' + if ((len_cmp == len) && !strncmp (cls + 2, name, len)) + return true; + } + } + + if (cls) + { + len = cls - full_name; // try C++ class name only + if (len_cmp == len && !strncmp (full_name, name, len)) + return true; + if (streq (cls + 2, name)) // try without 'class name' + return true; + } + return false; +} + +bool +DbeSession::add_path (char *path) +{ + return add_path (path, get_search_path ()); +} + +bool +DbeSession::add_classpath (char *path) +{ + return add_path (path, classpath); +} + +Vector<DbeFile*> * +DbeSession::get_classpath () +{ + if (classpath_df == NULL) + classpath_df = new Vector<DbeFile*>; + for (int i = classpath_df->size (), sz = classpath->size (); i < sz; i++) + classpath_df->store (i, getDbeFile (classpath->fetch (i), + DbeFile::F_DIR_OR_JAR)); + return classpath_df; +} + +bool +DbeSession::add_path (char *path, Vector<char*> *pathes) +{ + bool result = false; + Vector <char *> *tokens = split_str (path, ':'); + for (long j = 0, jsz = VecSize (tokens); j < jsz; j++) + { + char *spath = tokens->get (j); + // Don't append path if it's already there + bool got = false; + for (int i = 0, sz = pathes->size (); i < sz; i++) + { + char *nm = pathes->get (i); + if (streq (nm, spath)) + { + got = true; + break; + } + } + if (!got) + { + pathes->append (spath); + result = true; + } + else + free (spath); + } + delete tokens; + return result; +} + +void +DbeSession::set_need_refind () +{ + Vector<DbeFile*> *f_list = dbeFiles->values (); + for (long i = 0, sz = f_list == NULL ? 0 : f_list->size (); i < sz; i++) + { + DbeFile *f = f_list->get (i); + f->set_need_refind (true); + } + delete f_list; + for (long i = 0, sz = sources == NULL ? 0 : sources->size (); i < sz; i++) + { + SourceFile *f = sources->get (i); + if (f && f->dbeFile) + f->dbeFile->set_need_refind (true); + } +} + +void +DbeSession::set_search_path (Vector<char*> *path, bool reset) +{ + if (reset) + search_path->destroy (); + for (int i = 0, sz = path == NULL ? 0 : path->size (); i < sz; i++) + { + char *name = path->fetch (i); + if (add_path (name)) + reset = true; + } + if (reset) + { + set_need_refind (); + + // now reset the string setting for it + StringBuilder sb; + for (int i = 0, sz = search_path == NULL ? 0 : search_path->size (); i < sz; i++) + { + char *name = search_path->fetch (i); + if (sb.length () != 0) + sb.append (':'); + sb.append (name); + } + free (settings->str_search_path); + settings->str_search_path = sb.toString (); + } +} + +void +DbeSession::set_search_path (char *_lpath, bool reset) +{ + Vector<char *> *path = new Vector<char*>; + char *lpath = dbe_strdup (_lpath); + for (char *s = lpath; s;) + { + path->append (s); + s = strchr (s, ':'); + if (s) + { + *s = 0; + s++; + } + } + set_search_path (path, reset); + delete path; + free (lpath); +} + +void +DbeSession::set_pathmaps (Vector<pathmap_t*> *newPathMap) +{ + set_need_refind (); + settings->set_pathmaps (newPathMap); +} + +Vector<pathmap_t*> * +DbeSession::get_pathmaps () +{ + return settings->pathmaps; +} + +void +DbeSession::mobj_define (MemObjType_t *mobj) +{ + settings->mobj_define (mobj, false); + DbeView *dbev; + int index; + Vec_loop (DbeView*, views, index, dbev) + { + dbev->get_settings ()->mobj_define (mobj, false); + } +} + +void +DbeSession::dump_segments (FILE *out) +{ + int index; + LoadObject *loitem; + Vec_loop (LoadObject*, lobjs, index, loitem) + { + fprintf (out, NTXT ("Segment %d -- %s -- %s\n\n"), + index, loitem->get_name (), loitem->get_pathname ()); + loitem->dump_functions (out); + fprintf (out, NTXT ("\n End Segment %d -- %s -- %s\n\n"), + index, loitem->get_name (), loitem->get_pathname ()); + } +} + +void +DbeSession::dump_dataobjects (FILE *out) +{ + DataObject *ditem; + int index; + + fprintf (out, NTXT ("\nMaster list of DataObjects:\n")); + Vec_loop (DataObject*, dobjs, index, ditem) + { + Histable* scope = ditem->get_scope (); + DataObject* parent = ditem->get_parent (); + DataObject* master = ditem->get_master (); + if (parent != NULL) + fprintf (out, "id %6lld: [%4lld] parent = %6lld, offset = %+4lld %s\n", + (ll_t) ditem->id, (ll_t) ditem->get_size (), + (ll_t) parent->id, (ll_t) ditem->get_offset (), + ditem->get_name ()); + else + { + // parent is NULL + fprintf (out, NTXT ("id %6lld: [%4lld] %s "), + (ll_t) ditem->id, (ll_t) ditem->get_size (), + ditem->get_name ()); + if (master != NULL) + fprintf (out, NTXT (" master=%lld "), (ll_t) master->id); + else if (scope != NULL) + fprintf (out, NTXT (" master=?? ")); + else + fprintf (out, NTXT (" MASTER ")); +#if DEBUG + if (scope != NULL) + { + switch (scope->get_type ()) + { + case Histable::LOADOBJECT: + case Histable::FUNCTION: + fprintf (out, NTXT ("%s"), scope->get_name ()); + break; + case Histable::MODULE: + { + char *filename = get_basename (scope->get_name ()); + fprintf (out, NTXT ("%s"), filename); + break; + } + default: + fprintf (out, NTXT (" Unexpected scope %d:%s"), + scope->get_type (), scope->get_name ()); + } + } +#endif + fprintf (out, NTXT ("\n")); + } + } +} + +void +DbeSession::dump_map (FILE *out) +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + exp->dump_map (out); + } +} + +void +DbeSession::dump_stacks (FILE *outfile) +{ + Experiment *exp; + int n = nexps (); + FILE *f = (outfile == NULL ? stderr : outfile); + for (int i = 0; i < n; i++) + { + exp = get_exp (i); + fprintf (f, GTXT ("Experiment %d -- %s\n"), i, exp->get_expt_name ()); + exp->dump_stacks (f); + } +} + +void +DbeSession::propNames_name_store (int propId, const char *propName) +{ + PropDescr *prop = new PropDescr (propId, propName); + prop->flags = PRFLAG_NOSHOW; // do not show descriptions + propNames->store (propId, prop); +} + +void +DbeSession::propNames_name_store (int propId, const char* propName, + const char* propUname, VType_type dataType, + int flags) +{ + PropDescr *prop = new PropDescr (propId, propName); + prop->vtype = dataType; + prop->uname = dbe_strdup (propUname); + prop->flags = flags; + propNames->store (propId, prop); +} + +char * +DbeSession::propNames_name_fetch (int i) +{ + PropDescr *prop = propNames->fetch (i); + if (prop) + return prop->name; + return NULL; +} + +int +DbeSession::registerPropertyName (const char *name) +{ + if (name == NULL) + return PROP_NONE; + for (int i = 0; i < propNames->size (); i++) + { + char *pname = propNames_name_fetch (i); + if (pname && strcasecmp (pname, name) == 0) + return i; + } + int propId = propNames->size (); + propNames_name_store (propId, name); + return propId; +} + +int +DbeSession::getPropIdByName (const char *name) +{ + if (name == NULL) + return PROP_NONE; + for (int i = 0; i < propNames->size (); i++) + { + char *pname = propNames_name_fetch (i); + if (pname && strcasecmp (pname, name) == 0) + return i; + } + return PROP_NONE; +} + +char * +DbeSession::getPropName (int propId) +{ + if (!propNames) + return NULL; + if (propId < 0 || propId >= propNames->size ()) + return NULL; + return dbe_strdup (propNames_name_fetch (propId)); +} + +char * +DbeSession::getPropUName (int propId) +{ + if (!propNames) + return NULL; + if (propId < 0 || propId >= propNames->size ()) + return NULL; + PropDescr *prop = propNames->fetch (propId); + if (prop) + return dbe_strdup (prop->uname); + return NULL; +} + +void +DbeSession::append (UserLabel *lbl) +{ + if (lbl->expr) + { + if (userLabels == NULL) + userLabels = new Vector<UserLabel*> (); + userLabels->append (lbl); + } +} + +void +DbeSession::append (SourceFile *sf) +{ + sources->append (sf); + objs->append (sf); +} + +UserLabel * +DbeSession::findUserLabel (char *name) +{ + for (int i = 0, sz = userLabels ? userLabels->size () : 0; i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + if (strcasecmp (lbl->name, name) == 0) + return lbl; + } + return NULL; +} + +Expression * +DbeSession::findObjDefByName (char *name) +{ + Expression *expr = NULL; + + MemObjType_t *mot = MemorySpace::findMemSpaceByName (name); + if (mot != NULL) + { + char *index_expr_str = mot->index_expr; + expr = ql_parse (index_expr_str); + } + + if (expr == NULL) + { + int indxtype = findIndexSpaceByName (name); + expr = getIndexSpaceExpr (indxtype); + } + if (expr == NULL) + { + UserLabel *ulbl = findUserLabel (name); + if (ulbl) + expr = ulbl->expr; + } + return expr; +} + +Expression * +DbeSession::ql_parse (const char *expr_spec) +{ + /* (This slight duplication means we don't need to worry about copy + constructors for the QL::Result, nor about the lifetime of the + expr_spec.) */ + if (expr_spec != NULL) + { + QL::Result result (expr_spec); + QL::Parser qlparser (result); + if (qlparser () != 0) + return NULL; + return result (); + } + else + { + QL::Result result; + QL::Parser qlparser (result); + if (qlparser () != 0) + return NULL; + return result (); + } +} + +Vector<void*> * +DbeSession::getIndxObjDescriptions () +{ + int size = dyn_indxobj_indx; + if (size == 0) + return NULL; + Vector<int> *type = new Vector<int>(dyn_indxobj_indx); + Vector<char*> *desc = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *i18ndesc = new Vector<char*>(dyn_indxobj_indx); + Vector<char> *mnemonic = new Vector<char>(dyn_indxobj_indx); + Vector<int> *orderList = new Vector<int>(dyn_indxobj_indx); + Vector<char*> *exprList = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *sdesc = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *ldesc = new Vector<char*>(dyn_indxobj_indx); + + for (long i = 0, sz = VecSize (dyn_indxobj); i < sz; i++) + { + IndexObjType_t *tot = dyn_indxobj->get (i); + if (tot->memObj == NULL) + { + type->append ((int) tot->type); + desc->append (dbe_strdup (tot->name)); + i18ndesc->append (dbe_strdup (tot->i18n_name)); + sdesc->append (dbe_strdup (tot->short_description)); + ldesc->append (dbe_strdup (tot->long_description)); + mnemonic->append (tot->mnemonic); + orderList->append (settings->indx_tab_order->fetch (i)); + exprList->append (dbe_strdup (tot->index_expr_str)); + } + } + Vector<void*> *res = new Vector<void*>(8); + res->store (0, type); + res->store (1, desc); + res->store (2, mnemonic); + res->store (3, i18ndesc); + res->store (4, orderList); + res->store (5, exprList); + res->store (6, sdesc); + res->store (7, ldesc); + return (res); +} + +// Static function to get a vector of custom index object definitions +Vector<void*> * +DbeSession::getCustomIndxObjects () +{ + Vector<char*> *name = new Vector<char*>; + Vector<char*> *formula = new Vector<char*>; + for (long i = dyn_indxobj_indx_fixed, sz = VecSize (dyn_indxobj); i < sz; i++) + { + IndexObjType_t *tot = dyn_indxobj->get (i); + if (tot->memObj == NULL) + { + name->append (dbe_strdup (tot->name)); + formula->append (dbe_strdup (tot->index_expr_str)); + } + } + Vector<void*> *res = new Vector<void*>(2); + res->store (0, name); + res->store (1, formula); + return (res); +} + +// Static function to define a new index object type +char * +DbeSession::indxobj_define (const char *mname, char *i18nname, const char *index_expr_str, char *short_description, char *long_description) +{ + if (mname == NULL) + return dbe_strdup (GTXT ("No index object type name has been specified.")); + if (isalpha ((int) (mname[0])) == 0) + return dbe_sprintf (GTXT ("Index Object type name %s does not begin with an alphabetic character"), + mname); + const char *p = mname; + while (*p != 0) + { + if ((isalnum ((int) (*p)) == 0) && (*p != '_')) + return dbe_sprintf (GTXT ("Index Object type name %s contains a non-alphanumeric character"), + mname); + p++; + } + + // make sure the name is not in use + if (MemorySpace::findMemSpaceByName (mname) != NULL) + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + + int idxx = findIndexSpaceByName (mname); + if (idxx >= 0) + { + IndexObjType_t *mt = dyn_indxobj->fetch (idxx); + if (strcmp (mt->index_expr_str, index_expr_str) == 0) + // It's a redefinition, but the new definition is the same + return NULL; + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + } + if (index_expr_str == NULL) + return dbe_strdup (GTXT ("No index-expr has been specified.")); + if (strlen (index_expr_str) == 0) + return dbe_sprintf (GTXT ("Index Object index expression is invalid: %s"), + index_expr_str); + + // verify that the index expression parses correctly + char *expr_str = dbe_strdup (index_expr_str); + Expression *expr = ql_parse (expr_str); + if (expr == NULL) + return dbe_sprintf (GTXT ("Index Object index expression is invalid: %s"), + expr_str); + + // It's OK, create the new table entry + IndexObjType_t *tot = new IndexObjType_t; + tot->type = dyn_indxobj_indx++; + tot->name = dbe_strdup (mname); + tot->i18n_name = dbe_strdup (i18nname); + tot->short_description = dbe_strdup (short_description); + tot->long_description = dbe_strdup (long_description); + tot->index_expr_str = expr_str; + tot->index_expr = expr; + tot->mnemonic = mname[0]; + + // add it to the list + dyn_indxobj->append (tot); + idxobjs->append (new HashMap<uint64_t, Histable*>); + + // tell the session + settings->indxobj_define (tot->type, false); + + DbeView *dbev; + int index; + Vec_loop (DbeView*, views, index, dbev) + { + dbev->addIndexSpace (tot->type); + } + return NULL; +} + +char * +DbeSession::getIndexSpaceName (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->name; +} + +char * +DbeSession::getIndexSpaceDescr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->i18n_name; +} + +Expression * +DbeSession::getIndexSpaceExpr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->index_expr; +} + +char * +DbeSession::getIndexSpaceExprStr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->index_expr_str; +} + +int +DbeSession::findIndexSpaceByName (const char *mname) +{ + int idx; + IndexObjType_t *mt; + Vec_loop (IndexObjType_t*, dyn_indxobj, idx, mt) + { + if (strcasecmp (mt->name, mname) == 0) + return idx; + } + return -1; +} + +void +DbeSession::removeIndexSpaceByName (const char *mname) +{ + IndexObjType_t *indObj = findIndexSpace (mname); + if (indObj) + indObj->name[0] = 0; +} + +IndexObjType_t * +DbeSession::getIndexSpace (int index) +{ + return ((index < 0) || (index >= VecSize (dyn_indxobj))) ? NULL : dyn_indxobj->get (index); +} + +IndexObjType_t * +DbeSession::findIndexSpace (const char *mname) +{ + return getIndexSpace (findIndexSpaceByName (mname)); +} + +void +DbeSession::get_filter_keywords (Vector<void*> *res) +{ + Vector <char*> *kwCategory = (Vector<char*>*) res->fetch (0); + Vector <char*> *kwCategoryI18N = (Vector<char*>*) res->fetch (1); + Vector <char*> *kwDataType = (Vector<char*>*) res->fetch (2); + Vector <char*> *kwKeyword = (Vector<char*>*) res->fetch (3); + Vector <char*> *kwFormula = (Vector<char*>*) res->fetch (4); + Vector <char*> *kwDescription = (Vector<char*>*) res->fetch (5); + Vector <void*> *kwEnumDescs = (Vector<void*>*) res->fetch (6); + + char *vtypeNames[] = VTYPE_TYPE_NAMES; + for (long i = 0, sz = userLabels ? userLabels->size () : 0; i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + kwCategory->append (dbe_strdup (NTXT ("FK_LABEL"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Labels"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_BOOL])); + kwKeyword->append (dbe_strdup (lbl->name)); + kwFormula->append (dbe_strdup (lbl->str_expr)); + kwDescription->append (dbe_strdup (lbl->comment)); + kwEnumDescs->append (NULL); + } + + for (long i = 0, sz = propNames ? propNames->size () : 0; i < sz; i++) + { + PropDescr *prop = propNames->fetch (i); + char *pname = prop ? prop->name : NULL; + if (pname == NULL || *pname == 0 || prop->flags & PRFLAG_NOSHOW) + continue; + int vtypeNum = prop->vtype; + if (vtypeNum < 0 || vtypeNum >= TYPE_LAST) + vtypeNum = TYPE_NONE; + kwCategory->append (dbe_strdup (NTXT ("FK_EVTPROP"))); //Event Property + kwCategoryI18N->append (dbe_strdup (GTXT ("Misc. Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[vtypeNum])); + kwKeyword->append (dbe_strdup (pname)); + kwFormula->append (NULL); + kwDescription->append (dbe_strdup (prop->uname)); + kwEnumDescs->append (NULL); + } + + for (long i = 0, sz = dyn_indxobj ? dyn_indxobj->size () : 0; i < sz; i++) + { + IndexObjType_t *obj = dyn_indxobj->get (i); + if (obj->memObj) + continue; + kwCategory->append (dbe_strdup (NTXT ("FK_IDXOBJ"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Index Object Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_INT64])); + kwKeyword->append (dbe_strdup (obj->name)); + kwFormula->append (dbe_strdup (obj->index_expr_str)); + kwDescription->append (dbe_strdup (obj->i18n_name)); + kwEnumDescs->append (NULL); + } +} + +Histable * +DbeSession::findIndexObject (int idxtype, uint64_t idx) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + return iobjs->get (idx); +} + +Histable * +DbeSession::createIndexObject (int idxtype, int64_t idx) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + + Histable *idxobj = iobjs->get (idx); + if (idxobj == NULL) + { + idxobj = new IndexObject (idxtype, idx); + if (idx == -1) + idxobj->set_name (dbe_strdup (GTXT ("<Unknown>"))); + iobjs->put (idx, idxobj); + } + + return idxobj; +} + +Histable * +DbeSession::createIndexObject (int idxtype, Histable *hobj) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + int64_t idx = hobj ? hobj->id : -1; + Histable *idxobj = iobjs->get (idx); + if (idxobj == NULL) + { + idxobj = new IndexObject (idxtype, hobj); + if (idx == -1) + idxobj->set_name (dbe_strdup (GTXT ("<Unknown>"))); + iobjs->put (idx, idxobj); + } + + return idxobj; +} + +Histable * +DbeSession::findObjectById (Histable::Type type, int subtype, uint64_t id) +{ + switch (type) + { + case Histable::FUNCTION: + case Histable::MODULE: + case Histable::LOADOBJECT: + return ( id < (uint64_t) objs->size ()) ? objs->fetch ((int) id) : NULL; + case Histable::INDEXOBJ: + return findIndexObject (subtype, id); + // ignoring the following cases + case Histable::INSTR: + case Histable::LINE: + case Histable::EADDR: + case Histable::MEMOBJ: + case Histable::PAGE: + case Histable::DOBJECT: + case Histable::SOURCEFILE: + case Histable::IOACTFILE: + case Histable::IOACTVFD: + case Histable::IOCALLSTACK: + case Histable::HEAPCALLSTACK: + case Histable::OTHER: + case Histable::EXPERIMENT: + break; + } + return NULL; +} + +// return a vector of Functions that match the regular expression input string +Vector<JThread *> * +DbeSession::match_java_threads (char *ustr, int matchParent, + Vector<uint64_t> * &grids, + Vector<uint64_t> * &expids) +{ + if (ustr == NULL) + return NULL; + + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<JThread *> *ret = new Vector<JThread*>; + grids = new Vector<uint64_t>; + expids = new Vector<uint64_t>; + + int index; + JThread *jthread; + int expid; + Experiment* exp; + Vec_loop (Experiment*, exps, expid, exp) + { + + Vec_loop (JThread*, exp->get_jthreads (), index, jthread) + { + const char * name; + if (matchParent) + name = jthread->parent_name; + else + name = jthread->group_name; + if (name == NULL) + name = ""; + if (!regexec (®ex_desc, name, 0, NULL, 0)) + { + // this one matches + ret->append (jthread); + grids->append (exp->groupId); + expids->append (exp->getUserExpId ()); + } + } + } + + regfree (®ex_desc); + return ret; +} + +// return a vector of Functions that match the regular expression input string +Vector<Function *> * +DbeSession::match_func_names (const char *ustr, Histable::NameFormat nfmt) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<Function *> *ret = new Vector<Function*>; + + int index; + Histable *obj; + Vec_loop (Histable*, objs, index, obj) + { + if (obj->get_type () == Histable::FUNCTION) + { + Function *func = (Function*) obj; + if (!regexec (®ex_desc, func->get_name (nfmt), 0, NULL, 0)) + // this one matches + ret->append (func); + } + } + regfree (®ex_desc); + return ret; +} + +// return a vector of Functions that match the regular expression input string +Vector<FileData *> * +DbeSession::match_file_names (char *ustr, Histable::NameFormat nfmt) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<FileData *> *ret = new Vector<FileData*>; + int numExps = nexps (); + DefaultMap<int64_t, FileData*>* fDataMap; + Vector<FileData *> *fDataObjs; + FileData *fData; + int size; + for (int i = 0; i < numExps; i++) + { + Experiment *exp = get_exp (i); + fDataMap = exp->getFDataMap (); + fDataObjs = fDataMap->values (); + size = fDataObjs->size (); + for (int j = 0; j < size; j++) + { + fData = fDataObjs->fetch (j); + if (fData + && !regexec (®ex_desc, fData->get_raw_name (nfmt), 0, NULL, 0)) + // this one matches + ret->append (fData); + } + } + regfree (®ex_desc); + return ret; +} + +// return a vector of DataObjects that match the regular expression input string +Vector<DataObject *> * +DbeSession::match_dobj_names (char *ustr) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<DataObject *> *ret = new Vector<DataObject*>; + int index; + DataObject *ditem; + Vec_loop (DataObject*, dobjs, index, ditem) + { + // does this one match + if (!regexec (®ex_desc, ditem->get_name (), 0, NULL, 0)) + // this one matches + ret->append (ditem); + } + regfree (®ex_desc); + return ret; +} + +void +DbeSession::dump (char *msg, Vector<BaseMetric*> *mlist) +{ + if (msg) + fprintf (stderr, "%s\n", msg); + int sz = mlist ? mlist->size () : -1; + for (int i = 0; i < sz; i++) + { + BaseMetric *m = mlist->fetch (i); + char *s = m->dump (); + fprintf (stderr, "%2d %s\n", i, s); + free (s); + } + fprintf (stderr, "======END of mlist[%d] =========\n", sz); +} + +void +DbeSession::dump (char *msg, Vector<Metric*> *mlist) +{ + if (msg) + fprintf (stderr, "%s\n", msg); + int sz = mlist ? mlist->size () : -1; + for (int i = 0; i < sz; i++) + { + Metric *m = mlist->fetch (i); + char *s = m->dump (); + fprintf (stderr, "%2d %s\n", i, s); + free (s); + } + fprintf (stderr, "======END of mlist[%d] =========\n", sz); +} diff --git a/gprofng/src/DbeSession.h b/gprofng/src/DbeSession.h new file mode 100644 index 0000000..0a5c01c --- /dev/null +++ b/gprofng/src/DbeSession.h @@ -0,0 +1,481 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* The DbeSession class is instantiated by a DbeApplication, and contains + * all the data referring to a set of loaded experiments + * + * It manages a set of tables for the Experiments, for the LoadObjects + * referenced in them, and for the other objects representing the + * elements in the program hierarchy that can have metrics associated + * with them. It also has a master list of all the metrics available + * from all the loaded experiments. + * + * It gets an instance of the Settings class, instantiated as a copy + * of the one in the DbeApplication that instantiated the DbeSession. + * + * In addition, it manages a vector of DbeView's (q.v.); each DbeView + * represents a window into the DbeSession, and has its own set of + * Settings, and FilterSets for the Experiments, and is the access point + * for all processed data. + */ + +#ifndef _DBESESSION_H +#define _DBESESSION_H + + +#include <stdio.h> +#include "dbe_structs.h" +#include "vec.h" +#include "Hist_data.h" +#include "Histable.h" +#include "BaseMetric.h" +#include "BaseMetricTreeNode.h" +#include "MemorySpace.h" +#include "hwcentry.h" +#include "dbe_types.h" +#include "Settings.h" +#include "HashMap.h" +#include "Table.h" +#include "Experiment.h" + +class DbeSession; +class Experiment; +class Expression; +class ExpGroup; +class Function; +class JMethod; +class Histable; +class DbeView; +class Module; +class LoadObject; +class DataObject; +class SourceFile; +class Settings; +class StringBuilder; +class UserLabel; +class DbeFile; +class DbeJarFile; +class FileData; +class HeapData; +template <typename ITEM> class DbeSyncMap; +template <class ITEM> class Vector; + +struct DispTab; +struct List; +struct Countable; +class IndexObjType_t; + +typedef struct +{ + char *path; + Experiment *exp; + DbeSession *ds; + bool read_ahead; +} exp_ctx; + +class DbeSession +{ +public: + DbeSession (Settings *_settings, bool _ipc_mode, bool _rdt_mode); + ~DbeSession (); + + void reset (); + void reset_data (); + + void + set_interactive (bool _interactive) + { + interactive = _interactive; + } + + bool + is_interactive () + { + return interactive; + } + + bool is_datamode_available (); + bool is_leaklist_available (); + bool is_heapdata_available (); + bool is_iodata_available (); + bool is_racelist_available (); + bool is_deadlocklist_available (); + bool is_timeline_available (); + bool is_ifreq_available (); + bool is_omp_available (); + bool has_java (); + bool has_ompavail (); + + // XXX get_clock should be removed, to support cpus with different clocks + // XXX means reworking time-convertible HWC code + int get_clock (int id); + + // Access functions for DbeView's + int createView (); + int createView (int index, int cloneindex); + DbeView *getView (int index); + void dropView (int index); + + // Access functions controlling the experiment list + Vector<char*> *get_group_or_expt (char *path); // load an experiment or group + + void open_experiment (Experiment *exp, char *path); + Experiment *get_exp (int exp_ind); + Vector<Vector<char*>*> *getExperimensGroups (); + char *setExperimentsGroups (Vector<Vector<char*>*> *groups); + char *drop_experiment (int exp_ind); + int find_experiment (char *path); + + int + nexps () + { + return exps->size (); + } + int ngoodexps (); + + // Access functions controlling the DataObject list + DataObject *createDataObject (); + DataObject *createDataObject (DataObject *d, DataObject *p = NULL); + DataObject *createMasterDataObject (DataObject *); + Vector<DataObject*> *get_dobj_elements (DataObject *); + + DataObject * + get_Total_DataObject () + { + return d_total; + }; + + DataObject * + get_Unknown_DataObject () + { + return d_unknown; + }; + + DataObject * + get_Scalars_DataObject () + { + return d_scalars; + }; + + DataObject *find_dobj_by_name (char *dobj_name); + DataObject *find_dobj_match (DataObject *dobj); + DataObject *find_dobj_master (DataObject *dobj); + + int + ndobjs () + { + return dobjs->size (); + } + + // check if no -xhwcprof should be ignored + bool + check_ignore_no_xhwcprof () + { + return settings->get_ignore_no_xhwcprof (); + }; + + // check if FS warning should be comment, or real warning + bool + check_ignore_fs_warn () + { + return settings->get_ignore_fs_warn (); + }; + + // Access functions controlling the LoadObject list + DbeSyncMap<LoadObject> *loadObjMap; + void append (LoadObject *lobj); + LoadObject *createLoadObject (const char *nm, int64_t cksum = 0); + LoadObject *createLoadObject (const char *nm, const char *runTimePath, DbeFile *df); + + Vector<LoadObject *> * + get_LoadObjects () + { + return lobjs; + }; + + void dobj_updateHT (DataObject *dobj); + LoadObject *get_Total_LoadObject (); + Vector<LoadObject*> *get_text_segments (); + LoadObject *get_Unknown_LoadObject (); + LoadObject *find_lobj_by_name (const char *lobj_name, int64_t cksum = 0); + + // Access functions controlling the Tab list + Vector<DispTab*> * + get_TabList () + { + return settings->get_TabList (); + }; + + Vector<bool> * + get_MemTabList () + { + return settings->get_MemTabState (); + }; + + void mobj_define (MemObjType_t *); + + // Access functions controlling metrics + BaseMetric *find_base_reg_metric (char *mcmd); + Vector<BaseMetric*> *get_base_reg_metrics (); // excludes comparison (expr) variants + + Vector<BaseMetric*> * + get_all_reg_metrics () + { + return reg_metrics; // includes comparison (expr) variants + }; + + BaseMetricTreeNode *get_reg_metrics_tree (); + BaseMetric *register_metric_expr (BaseMetric::Type type, char *aux, char *expr_spec); + BaseMetric *register_metric (BaseMetric::Type type); + BaseMetric *register_metric (char *name, char *username, char *_def); + BaseMetric *register_metric (Hwcentry *ctr, const char* cmdname, const char* username); + void drop_metric (BaseMetric *); + Module *createModule (LoadObject *lo, const char *nm); + Module *createUnknownModule (LoadObject *lo); + Module *createClassFile (char *className); + DbeFile *getDbeFile (char *filename, int filetype); + SourceFile *get_Unknown_Source (); + SourceFile *createSourceFile (const char *path); + Histable *createHistObject (Histable::Type); + Function *createFunction (); + Function *create_hide_function (LoadObject *lo); + Function *get_Total_Function (); + Function *get_Unknown_Function (); + Function *get_JUnknown_Function (); + Function *get_jvm_Function (); + LoadObject *get_OMP_LoadObject (); + Function *get_OMP_Function (int); + JMethod *createJMethod (); + Histable *createIndexObject (int idxtype, int64_t idx); + Histable *createIndexObject (int idxtype, Histable *hobj); + + enum SpecialFunction + { + TruncatedStackFunc, + FailedUnwindFunc, + LastSpecialFunction + }; + Function *getSpecialFunction (SpecialFunction); + + Histable * + findObjectById (uint64_t _id) + { + long id = (long) _id; + return (id >= 0 && id < objs->size ()) ? objs->fetch (id) : NULL; + } + + Histable *findObjectById (Histable::Type type, int subtype, uint64_t id); + + // Other access functions + bool find_obj (FILE *dis_file, FILE *inp_file, Histable *&obj, char *name, + const char *sel, Histable::Type type, bool xdefault); + int ask_which (FILE *dis_file, FILE *inp_file, Vector<Histable*> *list, char *name); + LoadObject *map_NametoLoadObject (char *name, Vector<Histable*> *, int which); + Module *map_NametoModule (char *name, Vector<Histable*> *, int which); + Function *map_NametoFunction (char *, Vector<Histable*> *, const char *); + DataObject *map_NametoDataObject (char *name, Vector<Histable*> *, int which); + bool match_FName (char *name, Function *func); + + // Functions to convert a string to all matching Functions/DataObjects + Vector<Function *> *match_func_names (const char *ustr, Histable::NameFormat nfmt); + Vector<DataObject *> *match_dobj_names (char *); + + // Functions to convert a string to all matching JThreads + Vector<JThread*> *match_java_threads (char *ustr, int matchParent, + Vector<uint64_t> * &grids, + Vector<uint64_t> * &expids); + // Function to convert a string to all matching File names + Vector<FileData *> *match_file_names (char *ustr, Histable::NameFormat nfmt); + + // Access functions concerning the search path + Vector<char*> * + get_search_path () + { + return search_path; + } + + Vector<DbeFile*>*get_classpath (); + void set_search_path (Vector<char*> *path, bool reset); + void set_search_path (char *lpath, bool reset); + bool add_classpath (char *path); + bool add_path (char *path); + void set_pathmaps (Vector<pathmap_t*> *newPathMap); + Vector<pathmap_t*> *get_pathmaps (); + + // functions to aid debugging + void dump_stacks (FILE *); + void dump_dataobjects (FILE *); + void dump_segments (FILE *); + void dump_map (FILE *); + + // Find dynamic property by name + int registerPropertyName (const char *name); + int getPropIdByName (const char *name); + char* getPropName (int propId); + char* getPropUName (int propId); + + Vector<UserLabel*> *userLabels; // List of er_labels + UserLabel *findUserLabel (char *name); + DbeJarFile *get_JarFile (const char *name); + void append (UserLabel *lbl); + void append (SourceFile *sf); + void append (Experiment *exp); + void append (Hwcentry *exp); + void set_need_refind (); + + // Find user defined object by name + Expression *findObjDefByName (char *); + void get_filter_keywords (Vector<void*> *res); + + // Get the Settings class object + Settings * + get_settings () + { + return settings; + } + + // static members, used to define or fetch the various IndexSpaces + Vector<void*> *getIndxObjDescriptions (void); + Vector<void*> *getCustomIndxObjects (void); + char *indxobj_define (const char *, char *, const char *, char *, char *); + char *getIndexSpaceName (int index); + char *getIndexSpaceDescr (int index); + Expression *getIndexSpaceExpr (int index); + char *getIndexSpaceExprStr (int index); + int findIndexSpaceByName (const char *mname); + void removeIndexSpaceByName (const char *mname); + IndexObjType_t *getIndexSpace (int index); + IndexObjType_t *findIndexSpace (const char *mname); + Expression *ql_parse (const char *expr_spec); + BaseMetric *find_metric (BaseMetric::Type type, const char *cmd, const char *expr_spec = NULL); + static void dump (char *msg, Vector<Metric*> *mlist); + static void dump (char *msg, Vector<BaseMetric*> *mlist); + static Platform_t platform; // Sparc, Intel + Vector<ExpGroup *> *expGroups; + HashMap<char*, LoadObject *> *comp_lobjs; // list of comparable LoadObjects + HashMap<char*, DbeLine *> *comp_dbelines; // list of comparable DbeLines + HashMap<char*, SourceFile*>*comp_sources; // list of comparable SourceFiles + char *localized_SP_UNKNOWN_NAME; + + void + set_lib_visibility_used () + { + lib_visibility_used = true; + } + + bool + is_lib_visibility_used () + { + return lib_visibility_used; + } + + void unlink_tmp_files (); + char *get_tmp_file_name (const char *nm, bool for_java); + + Vector<char *> *tmp_files; + int status_ompavail; + int archive_mode; + bool ipc_mode; + bool rdt_mode; + + // data and methods concerning the machine model + // methods are in source file MachineModel.cc + Vector<char*> *list_mach_models (); // scan . and system lib directory for models + char *load_mach_model (char *); + + char * + get_mach_model () + { + return dbe_strdup (mach_model_loaded); + }; + Vector<SourceFile *> *get_sources (); + +private: + void init (); + void check_tab_avail (); + bool add_path (char *path, Vector<char*> *pathes); + Experiment *createExperiment (); + + // Divide the regular createExperiment into two parts - + // Part1 creates just the Experiment data structure + // Part2 updates related fields and vectors + Experiment *createExperimentPart1 (); + void createExperimentPart2 (Experiment *exp); + + Histable *findIndexObject (int idxtype, uint64_t idx); + void append_mesgs (StringBuilder *sb, char *path, Experiment *exp); + static void insert_metric (BaseMetric *mtr, Vector<BaseMetric*> *mlist); + void update_metric_tree (BaseMetric *m); + + char *find_mach_model (char *); // fine machine model file by name + Vector<Experiment*> *exps; // Master list of experiments + Vector<Histable*> *objs; // Master list of Functions,Modules,Segments + Vector<DataObject*> *dobjs; // Master list of DataObjects + Vector<LoadObject*> *lobjs; // Auxiliary list of LoadObjects + Vector<Hwcentry*> *hwcentries; + Vector<HashMap<uint64_t, Histable*>*> *idxobjs; // Master list of IndexObjects + HashMap<char*, SourceFile*> *sourcesMap; // list of Source which were not archived + Vector<SourceFile*> *sources; // list of SourceFiles + Map<const char*, DbeJarFile*>*dbeJarFiles; + Vector<Countable*> *metrics; + Vector<BaseMetric*> *reg_metrics; // Master list of BaseMetrics + BaseMetricTreeNode* reg_metrics_tree; // Hierarchy of BaseMetrics + Vector<char*> *search_path; + Vector<char*> *classpath; + Vector<DbeFile*> *classpath_df; + Map<const char*, DbeFile*>*dbeFiles; + Vector<DbeView*> *views; // Master list of DbeViews + bool interactive; // interactive mode + bool lib_visibility_used; + LoadObject *lo_total; // Total LoadObject + Function *f_total; // Total function + LoadObject *lo_unknown; // Unknown LoadObject + Function *f_unknown; // Unknown function + SourceFile *sf_unknown; // Unknown source file + Function *f_jvm; // pseudo-function <JVM-System> + Vector<Function*> *f_special; // pseudo-functions + Function *j_unknown; // pseudo-function <no Java callstack> + LoadObject *lo_omp; // OMP LoadObject (libmtsk) + Vector<Function*> *omp_functions; // OMP-overhead, etc. + DataObject *d_unknown; // Unknown dataobject + DataObject *d_scalars; // Scalars dataobject + DataObject *d_total; // Total dataobject + List **dnameHTable; // DataObject name hash table + Settings *settings; // setting/defaults structure + Vector<IndexObjType_t*> *dyn_indxobj; // Index Object definitions + int dyn_indxobj_indx; + int dyn_indxobj_indx_fixed; + + void propNames_name_store (int propId, const char *propName); + void propNames_name_store (int propId, const char *propName, + const char *propUName, VType_type vType, int flags); + char* propNames_name_fetch (int propId); + Vector<PropDescr*> *propNames; + char *defExpName; + int user_exp_id_counter; + char *mach_model_loaded; + char *tmp_dir_name; +}; + +// For now, there's only one, so keep its pointer +extern DbeSession *dbeSession; + +extern Vector<char *> *split_str (char *str, char delimiter); +#endif /* _DBESESSION_H */ diff --git a/gprofng/src/DbeSyncMap.h b/gprofng/src/DbeSyncMap.h new file mode 100644 index 0000000..f13f7b4 --- /dev/null +++ b/gprofng/src/DbeSyncMap.h @@ -0,0 +1,224 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DbeSyncMap_h +#define _DbeSyncMap_h + +#include "DbeLock.h" +#include "DbeLinkList.h" +#include "vec.h" + +typedef unsigned long hash_ty; + +template <class ITEM> class DbeSyncMap : public DbeLock +{ +public: + DbeSyncMap (int _chunkSize = DefaultChunkSize); + virtual ~DbeSyncMap (); + void reset (); + ITEM *get (const char *nm, int64_t chksum); + ITEM *sync_create_item (const char *nm, int64_t chksum); + ITEM *get (const char *nm, const char *path, DbeFile *df); + ITEM *sync_create_item (const char *nm, const char *path, DbeFile *df); + virtual void dump (); + + Vector<ITEM *> * + values () + { + return items; + }; + +private: + hash_ty hash (const char *key); + + DbeLinkList<ITEM *> **chunk; + Vector<ITEM *> *items; + long chunkSize; + + enum + { + DefaultChunkSize = 1024 + }; +}; + +template <class ITEM> +DbeSyncMap<ITEM>::DbeSyncMap (int _chunkSize) +{ + chunkSize = _chunkSize; + chunk = new DbeLinkList<ITEM *> * [chunkSize]; + for (long i = 0; i < chunkSize; i++) + chunk[i] = NULL; + items = new Vector<ITEM *>(512); +} + +template <class ITEM> +DbeSyncMap<ITEM>::~DbeSyncMap () +{ + for (long i = 0; i < chunkSize; i++) + Destroy (chunk[i]); + delete[] chunk; + delete items; +} + +template <class ITEM> +void +DbeSyncMap<ITEM>::reset () +{ + for (long i = 0; i < chunkSize; i++) + { + Destroy (chunk[i]); + chunk[i] = NULL; + } + items->reset (); +} + +template <class ITEM> +ITEM * +DbeSyncMap<ITEM>::get (const char *nm, int64_t chksum) +{ + hash_ty h = hash (nm); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (item->compare (nm, chksum)) + return item; + } + return NULL; +} + +template <class ITEM> +hash_ty +DbeSyncMap<ITEM>::hash (const char *key) +{ + return (hash_ty) (crc64 (key, strlen (key)) % chunkSize); +} + +template <class ITEM> +ITEM * +DbeSyncMap<ITEM>::sync_create_item (const char *nm, int64_t chksum) +{ + hash_ty h = hash (nm); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (item->compare (nm, chksum)) + return item; + } + aquireLock (); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (item->compare (nm, chksum)) + { + releaseLock (); + return item; + } + } + ITEM *item = ITEM::create_item (nm, chksum); + DbeLinkList<ITEM *> *dl = new DbeLinkList<ITEM *>(item); + dl->set_next (chunk[h]); + chunk[h] = dl; + items->append (item); + releaseLock (); + return item; +} + +template <class ITEM> +ITEM * +DbeSyncMap<ITEM>::get (const char *nm, const char *path, DbeFile *df) +{ + int mask = 1 + (path != NULL ? 2 : 0) + (df != NULL ? 4 : 0); + hash_ty h = hash (nm); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (mask == item->compare (nm, path, df)) + return item; + } + return NULL; +} + +template <class ITEM> +ITEM * +DbeSyncMap<ITEM>::sync_create_item (const char *nm, const char *path, DbeFile *df) +{ + int mask = CMP_PATH; + if (path != NULL) + mask += CMP_RUNTIMEPATH; + if (df != NULL) + mask += CMP_CHKSUM; + hash_ty h = hash (nm); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (mask == item->compare (nm, path, df)) + return item; + } + aquireLock (); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (mask == item->compare (nm, path, df)) + { + releaseLock (); + return item; + } + } + ITEM *item = ITEM::create_item (nm, path, df); + DbeLinkList<ITEM *> *dl = new DbeLinkList<ITEM *>(item); + dl->set_next (chunk[h]); + chunk[h] = dl; + items->append (item); + releaseLock (); + return item; +} + +template <class ITEM> +void +DbeSyncMap<ITEM>::dump () +{ + Dprintf (1, NTXT ("\nDbeSyncMap::dump: vals=%ld\n"), (long) VecSize (items)); + int tot = 0; + int max_cnt = 0; + for (long i = 0; i < chunkSize; i++) + { + DbeLinkList<ITEM *> *lp = chunk[i]; + if (lp) + { + int cnt = 0; + for (DbeLinkList<ITEM *> *lp1 = lp; lp1; lp1 = lp1->get_next ()) + cnt++; + tot += cnt; + if (max_cnt < cnt) + max_cnt = cnt; + cnt = 1; + for (DbeLinkList<ITEM *> *lp1 = lp; lp1; lp1 = lp1->get_next ()) + { + ITEM *p = lp1->get_item (); + Dprintf (1, NTXT (" %2d %s\n"), cnt, p->get_name ()); + cnt++; + } + } + } + Dprintf (1, NTXT ("\nDbeSyncMap::dump: vals=%ld max_cnt=%d tot=%d\n"), + (long) VecSize (items), max_cnt, tot); +} + +#endif /* _DbeSyncMap_h */ diff --git a/gprofng/src/DbeThread.cc b/gprofng/src/DbeThread.cc new file mode 100644 index 0000000..7eb3cb7 --- /dev/null +++ b/gprofng/src/DbeThread.cc @@ -0,0 +1,224 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include "DbeThread.h" +#include "util.h" +#include "vec.h" + +static void +cleanup_free_mutex (void* arg) { + // pthread_mutex_t *p_mutex = (pthread_mutex_t *) arg; + // if (p_mutex) + // pthread_mutex_unlock (p_mutex); +} + +static void* +thread_pool_loop (void* arg) +{ + DbeThreadPool *thrp = (DbeThreadPool*) arg; + Dprintf (DEBUG_THREADS, "thread_pool_loop:%d starting thread=%llu\n", + __LINE__, (unsigned long long) pthread_self ()); + + /* set my cancel state to 'enabled', and cancel type to 'defered'. */ + pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL); + + /* set thread cleanup handler */ + pthread_cleanup_push (cleanup_free_mutex, (void*) & (thrp->p_mutex)); + for (;;) + { + DbeQueue *q = thrp->get_queue (); + if (q) + { /* a request is pending */ + Dprintf (DEBUG_THREADS, + "thread_pool_loop:%d thread=%llu queue=%d start\n", + __LINE__, (unsigned long long) pthread_self (), q->id); + q->func (q->arg); + Dprintf (DEBUG_THREADS, + "thread_pool_loop:%d thread=%llu queue=%d done\n", + __LINE__, (unsigned long long) pthread_self (), q->id); + delete q; + continue; + } + if (thrp->no_new_queues) + { + Dprintf (DEBUG_THREADS, "thread_pool_loop:%d exit thread=%llu\n", + __LINE__, (unsigned long long) pthread_self ()); + pthread_exit (NULL); + } + Dprintf (DEBUG_THREADS, + "thread_pool_loop:%d before pthread_cond_wait thread=%llu\n", + __LINE__, (unsigned long long) pthread_self ()); + pthread_mutex_lock (&thrp->p_mutex); + pthread_cond_wait (&thrp->p_cond_var, &thrp->p_mutex); + Dprintf (DEBUG_THREADS, + "thread_pool_loop:%d after pthread_cond_wait thread=%llu\n", + __LINE__, (unsigned long long) pthread_self ()); + pthread_mutex_unlock (&thrp->p_mutex); + } + + // never reached, but we must use it here. See `man pthread_cleanup_push` + pthread_cleanup_pop (0); +} + +DbeThreadPool::DbeThreadPool (int _max_threads) +{ + static const int DBE_NTHREADS_DEFAULT = 4; + char *s = getenv ("GPROFNG_DBE_NTHREADS"); + if (s) + { + max_threads = atoi (s); + if (max_threads < 0) + max_threads = 0; + if (_max_threads > 0 && max_threads < _max_threads) + max_threads = _max_threads; + } + else + { + max_threads = _max_threads; + if (max_threads < 0) + max_threads = DBE_NTHREADS_DEFAULT; + } + Dprintf (DEBUG_THREADS, "DbeThreadPool:%d max_threads %d ---> %d\n", + __LINE__, _max_threads, max_threads); + pthread_mutex_init (&p_mutex, NULL); + pthread_cond_init (&p_cond_var, NULL); + threads = new Vector <pthread_t>(max_threads); + queue = NULL; + last_queue = NULL; + no_new_queues = false; + queues_cnt = 0; + total_queues = 0; +} + +DbeThreadPool::~DbeThreadPool () +{ + delete threads; +} + +DbeQueue * +DbeThreadPool::get_queue () +{ + pthread_mutex_lock (&p_mutex); + DbeQueue *q = queue; + Dprintf (DEBUG_THREADS, + "get_queue:%d thr: %lld id=%d queues_cnt=%d threads_cnt=%d max_threads=%d\n", + __LINE__, (unsigned long long) pthread_self (), + q ? q->id : -1, queues_cnt, (int) threads->size (), max_threads); + if (q) + { + queue = q->next; + queues_cnt--; + } + pthread_mutex_unlock (&p_mutex); + return q; +} + +void +DbeThreadPool::put_queue (DbeQueue *q) +{ + if (max_threads == 0) + { + // nothing runs in parallel + q->id = ++total_queues; + Dprintf (DEBUG_THREADS, NTXT ("put_queue:%d thr=%lld max_threads=%d queue (%d) runs on the worked thread\n"), + __LINE__, (unsigned long long) pthread_self (), max_threads, q->id); + q->func (q->arg); + delete q; + return; + } + + pthread_mutex_lock (&p_mutex); + // nothing runs in parallel + q->id = ++total_queues; + Dprintf (DEBUG_THREADS, "put_queue:%d thr=%lld max_threads=%d queue (%d)\n", + __LINE__, (unsigned long long) pthread_self (), max_threads, q->id); + if (queue) + { + last_queue->next = q; + last_queue = q; + } + else + { + queue = q; + last_queue = q; + } + queues_cnt++; + Dprintf (DEBUG_THREADS, + "put_queue:%d id=%d queues_cnt=%d threads_cnt=%d max_threads=%d\n", + __LINE__, q->id, queues_cnt, (int) threads->size (), max_threads); + if (queues_cnt > threads->size () && threads->size () < max_threads) + { + pthread_t thr; + int r = pthread_create (&thr, NULL, thread_pool_loop, (void *) this); + Dprintf (DEBUG_THREADS, + "put_queue:%d pthread_create returns %d thr=%llu\n", + __LINE__, r, (unsigned long long) thr); + if (r) + fprintf (stderr, GTXT ("pthread_create failed. errnum=%d (%s)\n"), r, + STR (strerror (r))); + else + threads->append (thr); + } + pthread_cond_signal (&p_cond_var); + pthread_mutex_unlock (&p_mutex); +} + +void +DbeThreadPool::wait_queues () +{ + pthread_mutex_lock (&p_mutex); + no_new_queues = true; + pthread_mutex_unlock (&p_mutex); + pthread_cond_broadcast (&p_cond_var); + for (;;) // Run requests on the worked thread too + { + DbeQueue *q = get_queue (); + if (q == NULL) + break; + Dprintf (DEBUG_THREADS, "wait_queues:%d thread=%llu queue=%d start\n", + __LINE__, (unsigned long long) pthread_self (), q->id); + q->func (q->arg); + Dprintf (DEBUG_THREADS, "wait_queues:%d thread=%llu queue=%d done\n", + __LINE__, (unsigned long long) pthread_self (), q->id); + delete q; + } + for (int i = 0, sz = threads->size (); i < sz; i++) + { + void *retval; + pthread_join (threads->get (i), &retval); + } +} + +DbeQueue::DbeQueue (int (*_func) (void *arg), void *_arg) +{ + func = _func; + arg = _arg; + next = NULL; +} + +DbeQueue::~DbeQueue () { } diff --git a/gprofng/src/DbeThread.h b/gprofng/src/DbeThread.h new file mode 100644 index 0000000..8ee148b --- /dev/null +++ b/gprofng/src/DbeThread.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBETHREAD_H +#define _DBETHREAD_H +#include <pthread.h> +#include "DbeLinkList.h" + +template <class ITEM> class Vector; + +class DbeQueue +{ +public: + DbeQueue (int (*_func)(void *arg), void *_arg); + ~DbeQueue (); + + int (*func) (void *arg); + void *arg; + int id; + DbeQueue *next; +}; + +class DbeThreadPool +{ +public: + DbeThreadPool (int _max_threads); + ~DbeThreadPool (); + DbeQueue *get_queue (); + void put_queue (DbeQueue *q); + void wait_queues (); + + pthread_mutex_t p_mutex; + pthread_cond_t p_cond_var; + volatile bool no_new_queues; +private: + Vector<pthread_t> *threads; + int max_threads; + DbeQueue *volatile queue; + DbeQueue *volatile last_queue; + volatile int queues_cnt; + volatile int total_queues; +}; + +#endif diff --git a/gprofng/src/DbeView.cc b/gprofng/src/DbeView.cc new file mode 100644 index 0000000..68613a5 --- /dev/null +++ b/gprofng/src/DbeView.cc @@ -0,0 +1,3126 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "util.h" +#include "Application.h" +#include "DbeSession.h" +#include "CallStack.h" +#include "Command.h" +#include "DataObject.h" +#include "Experiment.h" +#include "ExpGroup.h" +#include "FilterExp.h" +#include "FilterSet.h" +#include "Function.h" +#include "DbeView.h" +#include "PathTree.h" +#include "DataSpace.h" +#include "MemorySpace.h" +#include "IOActivity.h" +#include "HeapActivity.h" +#include "Print.h" +#include "MetricList.h" +#include "Module.h" +#include "Filter.h" +#include "LoadObject.h" +#include "dbe_types.h" +#include "StringBuilder.h" + +DbeView::DbeView (Application *_app, Settings *_settings, int _vindex) +{ + init (); + phaseIdx = 0; + settings = new Settings (_settings); + ptree = new PathTree (this); + dspace = new DataSpace (this); + memspaces = new Vector<MemorySpace*>; + iospace = new IOActivity (this); + heapspace = new HeapActivity (this); + filters = new Vector<FilterSet*>; + lo_expands = new Vector<enum LibExpand>; + cur_filter_str = NULL; + prev_filter_str = NULL; + cur_filter_expr = NULL; + filter_active = false; + noParFilter = false; + dataViews = new Vector<Vector<DataView*>*>; + names_src[0] = NULL; + names_src[1] = NULL; + names_src[2] = NULL; + names_dis[0] = NULL; + names_dis[1] = NULL; + names_dis[2] = NULL; + marks = new Vector<int>; + marks2dsrc = new Vector<int_pair_t>; + marks2dsrc_inc = new Vector<int_pair_t>; + marks2ddis = new Vector<int_pair_t>; + marks2ddis_inc = new Vector<int_pair_t>; + app = _app; + + // set the view's index + vindex = _vindex; + + // clear the precomputed data + func_data = NULL; + line_data = NULL; + pc_data = NULL; + src_data = NULL; + dis_data = NULL; + fitem_data = NULL; + callers = NULL; + callees = NULL; + dobj_data = NULL; + dlay_data = NULL; + iofile_data = NULL; + iovfd_data = NULL; + iocs_data = NULL; + heapcs_data = NULL; + + // and clear the selections + sel_obj = NULL; + sel_dobj = NULL; + sel_binctx = NULL; + func_scope = false; + lastSelInstr = NULL; + lastSelFunc = NULL; + + // Initialize index spaces + int sz = settings->get_IndxTabState ()->size (); + indxspaces = new Vector<PathTree*>(sz); + indx_data = new Vector<Hist_data*>(sz); + sel_idxobj = new Vector<Histable*>(sz); + for (int i = 0; i < sz; i++) + { + PathTree *is = new PathTree (this, i); + indxspaces->store (i, is); + indx_data->store (i, NULL); + sel_idxobj->store (i, NULL); + } + reset (); + + lobjectsNoJava = NULL; + + // set lo_expands for already existing LoadObjects + int idx; + LoadObject *lo; + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + Vec_loop (LoadObject*, lobjs, idx, lo) + { + lo_expands->store (lo->seg_idx, LIBEX_SHOW); + set_lo_expand (lo->seg_idx, LIBEX_SHOW); + } + delete lobjs; +} + +DbeView::DbeView (DbeView *dbev, int _vindex) +{ + init (); + phaseIdx = 0; + settings = new Settings (dbev->settings); + ptree = new PathTree (this); + dspace = new DataSpace (this); + iospace = new IOActivity (this); + heapspace = new HeapActivity (this); + memspaces = new Vector<MemorySpace*>; + filters = new Vector<FilterSet*>; + lo_expands = new Vector<enum LibExpand>; + cur_filter_str = NULL; + prev_filter_str = NULL; + cur_filter_expr = NULL; + noParFilter = false; + dataViews = new Vector<Vector<DataView*>*>; + names_src[0] = NULL; + names_src[1] = NULL; + names_src[2] = NULL; + names_dis[0] = NULL; + names_dis[1] = NULL; + names_dis[2] = NULL; + marks = new Vector<int>; + marks2dsrc = new Vector<int_pair_t>; + marks2dsrc_inc = new Vector<int_pair_t>; + marks2ddis = new Vector<int_pair_t>; + marks2ddis_inc = new Vector<int_pair_t>; + app = dbev->app; + + // set the view's index + vindex = _vindex; + + // clear the precomputed data + func_data = NULL; + line_data = NULL; + pc_data = NULL; + src_data = NULL; + dis_data = NULL; + fitem_data = NULL; + callers = NULL; + callees = NULL; + dobj_data = NULL; + dlay_data = NULL; + iofile_data = NULL; + iovfd_data = NULL; + iocs_data = NULL; + heapcs_data = NULL; + + // and clear the selections + sel_obj = NULL; + sel_dobj = NULL; + sel_binctx = NULL; + func_scope = false; + lastSelInstr = NULL; + lastSelFunc = NULL; + + // create the vector of IndexSpaces + int sz = dbev->indxspaces->size (); + indxspaces = new Vector<PathTree*>(sz); + indx_data = new Vector<Hist_data*>(sz); + sel_idxobj = new Vector<Histable*>(sz); + for (int i = 0; i < sz; i++) + { + PathTree *is = new PathTree (this, i); + indxspaces->store (i, is); + indx_data->store (i, NULL); + sel_idxobj->store (i, NULL); + } + reset (); + + // now copy the relevant information from the original view + for (int i = 0; i < dbeSession->nexps (); i++) + add_experiment (i, dbev->get_exp_enable (i)); + update_advanced_filter (); + delete lo_expands; + lo_expands = dbev->lo_expands->copy (); + lobjectsNoJava = NULL; +} + +DbeView::~DbeView () +{ + delete settings; + delete ptree; + delete dspace; + delete iospace; + delete heapspace; + Destroy (memspaces); + Destroy (filters); + delete lo_expands; + free (cur_filter_str); + free (prev_filter_str); + delete cur_filter_expr; + for (int exp_id = 0; exp_id < dataViews->size (); ++exp_id) + { + Vector<DataView*> *expDataViewList = dataViews->fetch (exp_id); + Destroy (expDataViewList); + } + delete dataViews; + delete reg_metrics; + metrics_lists->destroy (); + delete metrics_lists; + metrics_ref_lists->destroy (); + delete metrics_ref_lists; + delete derived_metrics; + delete marks; + delete marks2dsrc; + delete marks2dsrc_inc; + delete marks2ddis; + delete marks2ddis_inc; + + // Index spaces + indxspaces->destroy (); + delete indxspaces; + + indx_data->destroy (); + delete indx_data; + delete sel_idxobj; + delete lobjectsNoJava; +} + +void +DbeView::init () +{ + phaseIdx = 0; + reg_metrics = new Vector<BaseMetric*>; + metrics_lists = new Vector<MetricList*>; + metrics_ref_lists = new Vector<MetricList*>; + for (int i = 0; i <= MET_HEAP; i++) + { + metrics_lists->append (NULL); + metrics_ref_lists->append (NULL); + } + derived_metrics = new DerivedMetrics; + derived_metrics->add_definition (GTXT ("CPI"), GTXT ("Cycles Per Instruction"), GTXT ("cycles/insts")); + derived_metrics->add_definition (GTXT ("IPC"), GTXT ("Instructions Per Cycle"), GTXT ("insts/cycles")); + derived_metrics->add_definition (GTXT ("K_CPI"), GTXT ("Kernel Cycles Per Instruction"), GTXT ("K_cycles/K_insts")); + derived_metrics->add_definition (GTXT ("K_IPC"), GTXT ("Kernel Instructions Per Cycle"), GTXT ("K_insts/K_cycles")); +} + +bool +DbeView::set_libexpand (char *liblist, enum LibExpand flag) +{ + bool changed = settings->set_libexpand (liblist, flag, false); + // Show/hide performance optimization: + // No need to call update_lo_expand for every library because dbev->set_libexpand() + // is called from a loop in Dbe.cc SetLoadObjectState for every load object. + // It is sufficient to call update_lo_expand() just once at the end of the loop. + // At all other places such as er_print.cc which calls specific set_libexpand() + // explicitly call update_lo_expands(); + return changed; +} + +bool +DbeView::set_libdefaults () +{ + bool changed = settings->set_libdefaults (); + if (changed == true) + update_lo_expands (); + return changed; +} + +void +DbeView::update_lo_expands () +{ + int index; + LoadObject *lo; + + // search all load objects + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + Vec_loop (LoadObject*, lobjs, index, lo) + { + // now search the settings list for this one + enum LibExpand flag = settings->get_lo_setting (lo->get_pathname ()); + set_lo_expand (lo->seg_idx, flag); + } + delete lobjs; +} + +enum LibExpand +DbeView::get_lo_expand (int idx) +{ + if (idx < lo_expands->size ()) + return lo_expands->get (idx); + return LIBEX_SHOW; +} + +bool +DbeView::set_lo_expand (int idx, enum LibExpand flag) +{ + // LIBRARY_VISIBILITY + if (flag == LIBEX_HIDE) + { + resetShowAll (); + dbeSession->set_lib_visibility_used (); + } + // if no change + if (idx < lo_expands->size () && flag == get_lo_expand (idx)) + return false; + setShowHideChanged (); // this is necessary if called from er_print + + // change the flag + lo_expands->store (idx, flag); + + // and reset the data + fflush (stderr); + purge_events (); + reset_data (true); + return true; +} + +void +DbeView::reset () +{ + phaseIdx++; + + // reset all the per-experiment arrays + filters->destroy (); + lo_expands->reset (); + free (cur_filter_str); + cur_filter_str = NULL; + free (prev_filter_str); + prev_filter_str = NULL; + delete cur_filter_expr; + cur_filter_expr = NULL; + noParFilter = false; + for (int exp_id = 0; exp_id < dataViews->size (); ++exp_id) + { + Vector<DataView*> *expDataViewList = dataViews->fetch (exp_id); + if (expDataViewList) + expDataViewList->destroy (); + } + dataViews->destroy (); + reset_metrics (); + + // now reset any cached data + reset_data (true); + ompDisMode = false; + showAll = true; + showHideChanged = false; + newViewMode = false; +} + +void +DbeView::reset_data (bool all) +{ + // clear the precomputed data + if (func_data != NULL) + { + delete func_data; + func_data = NULL; + } + if (line_data != NULL) + { + delete line_data; + line_data = NULL; + } + if (pc_data != NULL) + { + delete pc_data; + pc_data = NULL; + } + if (src_data != NULL) + { + delete src_data; + src_data = NULL; + } + if (dis_data != NULL) + { + delete dis_data; + dis_data = NULL; + } + if (fitem_data != NULL) + { + delete fitem_data; + fitem_data = NULL; + } + if (callers != NULL) + { + delete callers; + callers = NULL; + } + if (callees != NULL) + { + delete callees; + callees = NULL; + } + if (dobj_data != NULL) + { + delete dobj_data; + dobj_data = NULL; + } + if (dlay_data != NULL) + { + delete dlay_data; + dlay_data = NULL; + } + if (iofile_data != NULL) + { + delete iofile_data; + iofile_data = NULL; + } + if (iovfd_data != NULL) + { + delete iovfd_data; + iovfd_data = NULL; + } + if (iocs_data != NULL) + { + delete iocs_data; + iocs_data = NULL; + } + if (heapcs_data != NULL) + { + delete heapcs_data; + heapcs_data = NULL; + } + + // and reset the selections + if (all) + { + sel_obj = NULL; + sel_dobj = NULL; + lastSelInstr = NULL; + lastSelFunc = NULL; + // Set selected object <Total> if possible + Function * ft = dbeSession->get_Total_Function (); + set_sel_obj (ft); + } + sel_binctx = NULL; + + dspace->reset (); + iospace->reset (); + heapspace->reset (); + + // loop over MemorySpaces, resetting each one + for (long i = 0, sz = VecSize (memspaces); i < sz; i++) + { + MemorySpace *ms = memspaces->get (i); + ms->reset (); + } + + // loop over IndexSpaces, resetting cached data + indx_data->destroy (); + for (long i = 0, sz = VecSize (indxspaces); i < sz; i++) + { + indx_data->store (i, NULL); + sel_idxobj->store (i, NULL); + } +} + +Vector<BaseMetric*> * +DbeView::get_all_reg_metrics () +{ + Vector<BaseMetric*> *mlist = dbeSession->get_all_reg_metrics (); + return mlist; +} + +BaseMetric * +DbeView::register_metric_expr (BaseMetric::Type type, char *cmd, char *expr_spec) +{ + BaseMetric *bm = dbeSession->register_metric_expr (type, cmd, expr_spec); + return bm; +} + +Metric * +DbeView::get_compare_metric (Metric *mtr, int groupNum) +{ + if (groupNum == 0 || !mtr->comparable ()) + return new Metric (*mtr); + ExpGroup *gr = dbeSession->expGroups->get (groupNum - 1); + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("EXPGRID==%d"), gr->groupId); + BaseMetric *bm = register_metric_expr (mtr->get_type (), mtr->get_cmd (), buf); + Metric *m = new Metric (bm, mtr->get_subtype ()); + m->set_raw_visbits (mtr->get_visbits ()); + if (m->legend == NULL) + m->legend = dbe_strdup (get_basename (gr->name)); + return m; +} + +MetricList * +DbeView::get_metric_ref (MetricType mtype) +{ + if (metrics_ref_lists->fetch (MET_COMMON) == NULL) + { + Vector<BaseMetric*> *base_metrics = dbeSession->get_base_reg_metrics (); + metrics_ref_lists->store (MET_SRCDIS, new MetricList (base_metrics, MET_SRCDIS)); + metrics_ref_lists->store (MET_COMMON, new MetricList (base_metrics, MET_COMMON)); + metrics_ref_lists->store (MET_NORMAL, new MetricList (base_metrics, MET_NORMAL)); + metrics_ref_lists->store (MET_CALL, new MetricList (base_metrics, MET_CALL)); + metrics_ref_lists->store (MET_CALL_AGR, new MetricList (base_metrics, MET_CALL_AGR)); + metrics_ref_lists->store (MET_DATA, new MetricList (base_metrics, MET_DATA)); + metrics_ref_lists->store (MET_INDX, new MetricList (base_metrics, MET_INDX)); + metrics_ref_lists->store (MET_IO, new MetricList (base_metrics, MET_IO)); + metrics_ref_lists->store (MET_HEAP, new MetricList (base_metrics, MET_HEAP)); + delete base_metrics; + } + return metrics_ref_lists->fetch (mtype); +} + +// logically, the function list must be created first, and it +// will create the other two; +MetricList * +DbeView::get_metric_list (MetricType mtype) +{ + if (metrics_lists->fetch (MET_COMMON) == NULL) + { + Vector<BaseMetric*> *base_metrics = dbeSession->get_base_reg_metrics (); + metrics_lists->store (MET_SRCDIS, new MetricList (base_metrics, MET_SRCDIS)); + metrics_lists->store (MET_COMMON, new MetricList (base_metrics, MET_COMMON)); + metrics_lists->store (MET_NORMAL, new MetricList (base_metrics, MET_NORMAL)); + metrics_lists->store (MET_CALL, new MetricList (base_metrics, MET_CALL)); + metrics_lists->store (MET_CALL_AGR, new MetricList (base_metrics, MET_CALL_AGR)); + metrics_lists->store (MET_DATA, new MetricList (base_metrics, MET_DATA)); + metrics_lists->store (MET_INDX, new MetricList (base_metrics, MET_INDX)); + metrics_lists->store (MET_IO, new MetricList (base_metrics, MET_IO)); + metrics_lists->store (MET_HEAP, new MetricList (base_metrics, MET_HEAP)); + delete base_metrics; + + // set the defaults + if (settings->str_dmetrics == NULL) + settings->str_dmetrics = strdup (Command::DEFAULT_METRICS); + char *status = setMetrics (settings->str_dmetrics, true); + if (status != NULL) + { + fprintf (stderr, "XXX setMetrics(\"%s\") failed: %s\n", settings->str_dmetrics, status); + abort (); + } + + // set the default sort + setSort (settings->str_dsort, MET_NORMAL, true); + } + return metrics_lists->fetch (mtype); +} + +MetricList * +DbeView::get_metric_list (int dsptype, int subtype) +{ + MetricList *mlist; + switch (dsptype) + { + case DSP_DISASM: + case DSP_SOURCE: + case DSP_SOURCE_DISASM: + mlist = get_metric_list (MET_COMMON); + mlist = new MetricList (mlist); + if (subtype != 0) + { + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + if (m->comparable ()) + { + Metric *m1 = get_compare_metric (m, subtype); + mlist->put (i, m1); + delete m; + } + } + } + break; + default: + mlist = get_metric_list (MET_NORMAL); + mlist = new MetricList (mlist); + break; + } + return mlist; +} + +void +DbeView::reset_metrics () +{ + for (int i = 0, sz = metrics_lists->size (); i < sz; i++) + { + delete metrics_lists->fetch (i); + metrics_lists->store (i, NULL); + } + for (int i = 0, sz = metrics_ref_lists->size (); i < sz; i++) + { + delete metrics_ref_lists->fetch (i); + metrics_ref_lists->store (i, NULL); + } +} + +bool +DbeView::comparingExperiments () +{ + if (dbeSession->expGroups->size () <= 1) + return false; + return 0 != (settings->get_compare_mode () & (CMP_DELTA | CMP_ENABLE | CMP_RATIO)); +} + +void +DbeView::set_compare_mode (int mode) +{ + if (mode == get_compare_mode ()) + return; + settings->set_compare_mode (mode); + if (comparingExperiments ()) + { + Vector<BaseMetric*> *bm_list = dbeSession->get_base_reg_metrics (); + for (int i = 0, sz = bm_list->size (); i < sz; i++) + { + BaseMetric *m = bm_list->fetch (i); + if (m->get_expr_spec () || !m->comparable ()) + continue; + for (int i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (i1); + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("EXPGRID==%d"), gr->groupId); + register_metric_expr (m->get_type (), m->get_cmd (), buf); + } + } + } + MetricList *mlist = get_metric_list (MET_NORMAL); + MetricList *gmlist = get_metric_list (MET_CALL); + MetricList *dmlist = get_metric_list (MET_DATA); + MetricList *imlist = get_metric_list (MET_INDX); + if (comparingExperiments ()) + { + add_compare_metrics (mlist); + add_compare_metrics (gmlist); + add_compare_metrics (dmlist); + add_compare_metrics (imlist); + } + else + { + remove_compare_metrics (mlist); + remove_compare_metrics (gmlist); + remove_compare_metrics (dmlist); + remove_compare_metrics (imlist); + } +} + +void +DbeView::ifreq (FILE *outfile) +{ + if (!dbeSession->is_ifreq_available ()) + { + fprintf (outfile, GTXT ("No instruction frequency data available\n")); + return; + } + for (int index = 0; index < filters->size (); index++) + { + Experiment *exp = dbeSession->get_exp (index); + if (exp->broken || !get_exp_enable (index) || !exp->ifreqavail) + continue; + + // this experiment has the data; print it + fprintf (outfile, + GTXT ("Instruction frequency data from experiment %s\n\n"), + exp->get_expt_name ()); + fprintf (outfile, NTXT ("%s"), pr_mesgs (exp->fetch_ifreq (), "", "")); + } +} + +// When adding multiple sub-experiments of an experiment, it is +// not necessary to do the following every-time. It is sufficient to call reset_metrics() +// and call get_metric_ref() and get_metric_list() in the end after all the sub-experiments +// have been added +void +DbeView::add_experiment_epilogue () +{ + bool flag_LIBEX_HIDE = false; + bool flag_ShowHideChanged = false; + Vector<LoadObject*> *lobjs = dbeSession->get_LoadObjects (); + for (long i = lo_expands->size (), sz = lobjs ? lobjs->size () : 0; i < sz; i++) + { + flag_ShowHideChanged = true; + LoadObject *lo = lobjs->get (i); + enum LibExpand flag = settings->get_lo_setting (lo->get_pathname ()); + if (flag == LIBEX_HIDE) + flag_LIBEX_HIDE = true; + lo_expands->store (lo->seg_idx, flag); + } + if (flag_LIBEX_HIDE) + { + resetShowAll (); + dbeSession->set_lib_visibility_used (); + } + if (flag_ShowHideChanged) + { + setShowHideChanged (); // this is necessary if called from er_print + purge_events (); + reset_data (true); + } + reset_metrics (); + (void) get_metric_ref (MET_NORMAL); + (void) get_metric_ref (MET_CALL); + (void) get_metric_ref (MET_CALL_AGR); + (void) get_metric_ref (MET_DATA); + (void) get_metric_ref (MET_INDX); + (void) get_metric_ref (MET_IO); + (void) get_metric_ref (MET_HEAP); + (void) get_metric_list (MET_NORMAL); + (void) get_metric_list (MET_CALL); + (void) get_metric_list (MET_CALL_AGR); + (void) get_metric_list (MET_DATA); + (void) get_metric_list (MET_INDX); + (void) get_metric_list (MET_IO); + (void) get_metric_list (MET_HEAP); +} + +// When adding multiple sub-experiments of an experiment, avoid invoking the steps in +// add_experiment_epilogue() every time and instead call it separately in the end +// after all sub-experiments have been added +void +DbeView::add_subexperiment (int index, bool enabled) +{ + // phaseIdx doesn't change, PathTree can handle adding + // new experiments without reset + + // Set up the FilterSet for the experiments + Experiment *exp = dbeSession->get_exp (index); + FilterSet *filterset = new FilterSet (this, exp); + filterset->set_enabled (enabled); + filters->store (index, filterset); + + assert (index == dataViews->size ()); + Vector<DataView*> *expDataViewList = new Vector<DataView*>; + for (int data_id = 0; data_id < DATA_LAST; ++data_id) + expDataViewList->append (NULL); //experiment data_id's are not known yet + dataViews->store (index, expDataViewList); +} + +void +DbeView::add_experiment (int index, bool enabled) +{ + // phaseIdx doesn't change, PathTree can handle adding + // new experiments without reset + + // delete any cached data + reset_data (true); + + // Set up the FilterSet for the experiments + Experiment *exp = dbeSession->get_exp (index); + FilterSet *filterset = new FilterSet (this, exp); + filterset->set_enabled (enabled); + filters->store (index, filterset); + + assert (index == dataViews->size ()); + Vector<DataView*> *expDataViewList = new Vector<DataView*>; + for (int data_id = 0; data_id < DATA_LAST; ++data_id) + expDataViewList->append (NULL); //experiment data_id's are not known yet + dataViews->store (index, expDataViewList); + + reset_metrics (); + (void) get_metric_ref (MET_NORMAL); + (void) get_metric_ref (MET_CALL); + (void) get_metric_ref (MET_CALL_AGR); + (void) get_metric_ref (MET_DATA); + (void) get_metric_ref (MET_INDX); + (void) get_metric_ref (MET_IO); + (void) get_metric_ref (MET_HEAP); + (void) get_metric_list (MET_NORMAL); + (void) get_metric_list (MET_CALL); + (void) get_metric_list (MET_CALL_AGR); + (void) get_metric_list (MET_DATA); + (void) get_metric_list (MET_INDX); + (void) get_metric_list (MET_IO); + (void) get_metric_list (MET_HEAP); +} + +void +DbeView::drop_experiment (int index) +{ + phaseIdx++; + filters->remove (index); + + // reset any cached data + reset_data (true); + + Vector<DataView*> *expDataViewList = dataViews->remove (index); + if (expDataViewList) + { + expDataViewList->destroy (); + delete expDataViewList; + } +} + +bool +DbeView::get_exp_enable (int n) +{ + return filters ? filters->fetch (n)->get_enabled () : true; +} + +void +DbeView::set_exp_enable (int n, bool e) +{ + FilterSet *fs = filters->fetch (n); + if (fs->get_enabled () != e) + { + fs->set_enabled (e); + purge_events (n); + phaseIdx++; + } +} + +void +DbeView::reset_metric_list (MetricList *mlist, int cmp_mode) +{ + MetricType mtype = mlist->get_type (); + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + delete metrics_lists->fetch (MET_COMMON); + metrics_lists->store (MET_COMMON, new MetricList (mlist)); + remove_compare_metrics (metrics_lists->fetch (MET_COMMON)); + break; + // ignoring the following cases (why?) + case MET_SRCDIS: + case MET_CALL: + case MET_DATA: + case MET_INDX: + case MET_CALL_AGR: + case MET_IO: + case MET_HEAP: + break; + } + + if (cmp_mode != -1) + { + settings->set_compare_mode (cmp_mode); + if (comparingExperiments ()) + add_compare_metrics (mlist); + } + + switch (mtype) + { + case MET_NORMAL: + delete metrics_lists->fetch (mtype); + metrics_lists->store (mtype, mlist); + // fall through to next case + case MET_COMMON: + metrics_lists->fetch (MET_SRCDIS)->set_metrics (mlist); + metrics_lists->fetch (MET_CALL)->set_metrics (mlist); + metrics_lists->fetch (MET_CALL_AGR)->set_metrics (mlist); + remove_compare_metrics (metrics_lists->fetch (MET_CALL_AGR)); + metrics_lists->fetch (MET_DATA)->set_metrics (mlist); + metrics_lists->fetch (MET_INDX)->set_metrics (mlist); + metrics_lists->fetch (MET_IO)->set_metrics (mlist); + metrics_lists->fetch (MET_HEAP)->set_metrics (mlist); + break; + case MET_CALL_AGR: + delete metrics_lists->fetch (MET_CALL_AGR); + metrics_lists->store (MET_CALL_AGR, mlist); + remove_compare_metrics (mlist); + break; + case MET_SRCDIS: + case MET_CALL: + case MET_DATA: + case MET_INDX: + case MET_IO: + case MET_HEAP: + delete metrics_lists->fetch (mtype); + metrics_lists->store (mtype, mlist); + break; + default: + abort (); + } + reset_data (false); +} + +void +DbeView::add_compare_metrics (MetricList *mlist) +{ + if (mlist == NULL || !comparingExperiments ()) + return; + int sort_ref_index = mlist->get_sort_ref_index (); + Vector<Metric*> *items = mlist->get_items (); + Vector<Metric*> *newItems = new Vector<Metric*>(); + int mode = get_compare_mode (); + int cmp_vbits = 0; + if ((mode & CMP_DELTA) != 0) + cmp_vbits = VAL_DELTA; + else if ((mode & CMP_RATIO) != 0) + cmp_vbits = VAL_RATIO; + for (long i = 0, sz = items->size (); i < sz; i++) + { + Metric *mtr = items->get (i); + if (sort_ref_index == i) + mlist->set_sort_ref_index (newItems->size ()); + int vbits = mtr->get_visbits () & ~(VAL_DELTA | VAL_RATIO); + mtr->set_raw_visbits (vbits); + if (!mtr->comparable ()) + { + newItems->append (mtr); + continue; + } + if (mtr->get_expr_spec ()) + { + if (strcmp (mtr->get_expr_spec (), NTXT ("EXPGRID==1")) != 0) + { + if ((cmp_vbits & VAL_RATIO) != 0) + // for ratios, make sure VAL_TIMEVAL is off and VAL_VALUE is on + mtr->set_raw_visbits ((vbits | VAL_VALUE | cmp_vbits) & ~VAL_TIMEVAL); + else + { + int ind = mlist->get_listorder (mtr->get_cmd (), mtr->get_subtype (), NTXT ("EXPGRID==1")); + if (ind >= 0) + // take VAL_VALUE and VAL_TIMEVAL from base experiment + mtr->set_raw_visbits (cmp_vbits + | (vbits & ~(VAL_VALUE | VAL_TIMEVAL)) + | (mlist->get (ind)->get_visbits () + & (VAL_VALUE | VAL_TIMEVAL))); + else + mtr->set_raw_visbits (cmp_vbits | vbits); + } + } + newItems->append (mtr); + continue; + } + for (long i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++) + { + Metric *m = get_compare_metric (mtr, i1 + 1); + switch (m->get_vtype ()) + { + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + m->set_raw_visbits (vbits); + break; + default: + if (i1 == 0) + m->set_raw_visbits (vbits); + else if (cmp_vbits == VAL_RATIO + && ((vbits & (VAL_VALUE | VAL_TIMEVAL)) + == (VAL_VALUE | VAL_TIMEVAL))) + // make ratios for VAL_VALUE only + m->set_raw_visbits ((vbits | VAL_VALUE | cmp_vbits) & ~VAL_TIMEVAL); + else + m->set_raw_visbits (vbits | cmp_vbits); + break; + } + newItems->append (m); + } + } + items->reset (); + items->addAll (newItems); + delete newItems; + phaseIdx++; + reset_data (false); +} + +MetricList * +DbeView::get_compare_mlist (MetricList *met_list, int grInd) +{ + MetricList *mlist = new MetricList (met_list->get_type ()); + mlist->set_sort_ref_index (met_list->get_sort_ref_index ()); + mlist->set_sort_rev (met_list->get_sort_rev ()); + + Vector<Metric*> *items_old = met_list->get_items (); + for (int i = 0, sz = items_old->size (); i < sz; i++) + { + Metric *m = get_compare_metric (items_old->get (i), grInd + 1); + mlist->append (m); + } + return mlist; +} + +void +DbeView::remove_compare_metrics (MetricList *mlist) +{ + Vector<Metric*> *items = mlist->get_items (); + Vector<Metric*> *items_old = items->copy (); + items->reset (); + int sort_index = mlist->get_sort_ref_index (); + mlist->set_sort_ref_index (0); + for (int i = 0, sz = items_old->size (); i < sz; i++) + { + Metric *m = items_old->fetch (i); + if (m->get_expr_spec () == NULL) + { + // this is a 'non-compare' metric; add it + items->append (m); + if (sort_index == i) + mlist->set_sort_ref_index (items->size () - 1); + continue; + } + // is the 'non-compare' version of the metric already in the list? + int ind = mlist->get_listorder (m->get_cmd (), m->get_subtype ()); + if (ind == -1) + { + // not in the list; add it + BaseMetric *bm = dbeSession->find_metric (m->get_type (), m->get_cmd (), NULL); + Metric *new_met = new Metric (bm, m->get_subtype ()); + new_met->set_raw_visbits (m->get_visbits () & ~(CMP_DELTA | CMP_RATIO)); + items->append (new_met); + if (sort_index == i) + mlist->set_sort_ref_index (items->size () - 1); + } + delete m; + } + delete items_old; + reset_data (false); +} + +// setMetrics -- set the metric list according to specification +// The previous sort is preserved, if possible +// Otherwise, the default sort setting is used +// Returns NULL if OK, or an error string if not +//YXXX only MET_NORMAL appears to be used... code could be simplified +char * +DbeView::setMetrics (char *mspec, bool fromRcFile) +{ + char *ret; + MetricType mtype = MET_NORMAL; + // note that setting the default is done here, while all else is in MetricList + if (mspec == NULL) abort (); + if (strcasecmp (mspec, Command::DEFAULT_CMD) == 0) + { + mspec = settings->get_default_metrics (); + fromRcFile = true; + } + MetricList *mlist = get_metric_list (mtype); + mlist = new MetricList (mlist); + ret = mlist->set_metrics (mspec, fromRcFile, derived_metrics); + if (ret == NULL) + { + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + delete metrics_lists->fetch (MET_COMMON); + metrics_lists->store (MET_COMMON, new MetricList (mlist)); + break; + // ignoring the following cases (why?) + case MET_SRCDIS: + case MET_CALL: + case MET_DATA: + case MET_INDX: + case MET_CALL_AGR: + case MET_IO: + case MET_HEAP: + break; + } + add_compare_metrics (mlist); + + //YXXX looks like cut/paste code here, see reset_metric_list() + switch (mtype) + { + case MET_NORMAL: + delete metrics_lists->fetch (mtype); + metrics_lists->store (mtype, mlist); + //YXXX is lack of break intentional? If so, add comment... + case MET_COMMON: + metrics_lists->fetch (MET_SRCDIS)->set_metrics (mlist); + metrics_lists->fetch (MET_CALL)->set_metrics (mlist); + metrics_lists->fetch (MET_CALL_AGR)->set_metrics (mlist); + remove_compare_metrics (metrics_lists->fetch (MET_CALL_AGR)); + metrics_lists->fetch (MET_DATA)->set_metrics (mlist); + metrics_lists->fetch (MET_INDX)->set_metrics (mlist); + metrics_lists->fetch (MET_IO)->set_metrics (mlist); + metrics_lists->fetch (MET_HEAP)->set_metrics (mlist); + break; + case MET_CALL_AGR: + delete metrics_lists->fetch (MET_CALL_AGR); + metrics_lists->store (MET_CALL_AGR, mlist); + remove_compare_metrics (mlist); + break; + case MET_SRCDIS: + case MET_CALL: + case MET_DATA: + case MET_INDX: + case MET_IO: + case MET_HEAP: + delete metrics_lists->fetch (mtype); + metrics_lists->store (mtype, mlist); + break; + default: + abort (); + } + reset_data (false); + } + else + delete mlist; + return ret; +} + + +// Set Sort by name (er_print) +char * +DbeView::setSort (char * sort_list, MetricType mtype, bool fromRcFile) +{ + MetricList *mlist = NULL; + + // note that setting the default is done here, while all else is in MetricList + if ((sort_list == NULL) || (strcmp (sort_list, Command::DEFAULT_CMD) == 0)) + { + if (settings->str_dsort == NULL) + settings->str_dsort = strdup (Command::DEFAULT_METRICS); + sort_list = settings->get_default_sort (); + } + mlist = get_metric_list (mtype); + + if (mlist == NULL) + abort (); + + // set the new sort + char *ret = mlist->set_sort (sort_list, fromRcFile); + if (ret != NULL) + return ret; + + // now resort all cached data + resortData (mtype); + return NULL; +} + +// Set sort from the visible index (Analyzer) +void +DbeView::setSort (int visindex, MetricType mtype, bool reverse) +{ + MetricList *mlist = get_metric_list (mtype); + Vector<Metric*> *items = mlist->get_items (); + if (visindex >= items->size ()) + return; + mlist->set_sort (visindex, reverse); + resortData (mtype); + if (mtype == MET_NORMAL) + { + int idx_cc = -1; + MetricList *mlist_cc = get_metric_list (MET_CALL); + Vector<Metric*> *items_cc = mlist_cc->get_items (); + for (int i = 0; i < items_cc->size (); i++) + { + char * name_cc = items_cc->fetch (i)->get_username (); + char * name_normal = items->fetch (visindex)->get_username (); + if (0 == strncmp (name_cc, name_normal, strlen (name_cc))) + { + idx_cc = i; + break; + } + } + if (idx_cc != -1) + { + mlist_cc->set_sort (idx_cc, reverse); + resortData (MET_CALL); + // Change a sort metric for MET_CALL_AGR + Metric *m = items_cc->fetch (idx_cc); + MetricList *cList = get_metric_list (MET_CALL_AGR); + Metric *m1 = cList->find_metric (m->get_cmd (), m->get_subtype ()); + if (m1) + cList->set_sort_metric (m1->get_cmd (), m1->get_subtype (), reverse); + } + } + if (mtype == MET_CALL) + { + int idx_norm = -1; + MetricList *mlist_norm = get_metric_list (MET_NORMAL); + Vector<Metric*> *items_norm = mlist_norm->get_items (); + for (int i = 0; i < items_norm->size (); i++) + { + char * name_norm = items_norm->fetch (i)->get_username (); + char * name_cc = items->fetch (visindex)->get_username (); + if (mlist_norm->get_sort_ref_index () == i + && 0 == strncmp (name_norm, name_cc, strlen (name_norm))) + { + idx_norm = i; + break; + } + } + if (idx_norm == -1) + { + for (int i = 0; i < items_norm->size (); i++) + { + char * name_norm = items_norm->fetch (i)->get_username (); + char * name_cc = items->fetch (visindex)->get_username (); + if (0 == strncmp (name_norm, name_cc, strlen (name_norm))) + { + idx_norm = i; + break; + } + } + } + if (idx_norm != -1) + { + mlist_norm->set_sort (idx_norm, reverse); + resortData (MET_NORMAL); + } + // Change a sort metric for MET_CALL_AGR + Metric *m = items->fetch (visindex); + MetricList *cList = get_metric_list (MET_CALL_AGR); + Metric *m1 = cList->find_metric (m->get_cmd (), m->get_subtype ()); + if (m1) + cList->set_sort_metric (m1->get_cmd (), m1->get_subtype (), reverse); + } +} + +void +DbeView::resortData (MetricType mtype) +{ + int idx; + Hist_data *data; + + MetricList *mlist = get_metric_list (mtype); + switch (mtype) + { + case MET_NORMAL: + if (func_data != NULL) + func_data->resort (mlist); + if (line_data != NULL) + line_data->resort (mlist); + if (pc_data != NULL) + pc_data->resort (mlist); + break; + case MET_CALL: + case MET_CALL_AGR: + if (fitem_data != NULL) + fitem_data->resort (mlist); + if (callers != NULL) + callers->resort (mlist); + if (callees != NULL) + callees->resort (mlist); + break; + case MET_DATA: + if (dobj_data != NULL) + dobj_data->resort (mlist); + if (dlay_data != NULL) + { + delete dlay_data; + dlay_data = NULL; + } + break; + case MET_INDX: + Vec_loop (Hist_data*, indx_data, idx, data) + { + if (data) + data->resort (mlist); + } + break; + case MET_IO: + if (iofile_data != NULL) + iofile_data->resort (mlist); + if (iovfd_data != NULL) + iovfd_data->resort (mlist); + if (iocs_data != NULL) + iocs_data->resort (mlist); + break; + case MET_HEAP: + if (heapcs_data != NULL) + heapcs_data->resort (mlist); + break; + case MET_COMMON: + case MET_SRCDIS: + break; + } +} + +// Get the sort metric name +char * +DbeView::getSort (MetricType mtype) +{ + MetricList *mlist = get_metric_list (mtype); + return mlist->get_sort_name (); +} + +// Get the sort command (to use for resetting) +char * +DbeView::getSortCmd (MetricType mtype) +{ + MetricList *mlist = get_metric_list (mtype); + return mlist->get_sort_cmd (); +} + +int +DbeView::get_sel_ind (Histable *selObj, int type, int subtype) +{ + Hist_data *data; + switch (type) + { + case DSP_FUNCTION: + data = func_data; + break; + case DSP_LINE: + data = line_data; + break; + case DSP_PC: + data = pc_data; + break; + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dis_data; + break; + case DSP_DLAYOUT: + data = dlay_data; + break; + case DSP_DATAOBJ: + data = dobj_data; + break; + case DSP_IOACTIVITY: + data = iofile_data; + break; + case DSP_IOVFD: + data = iovfd_data; + break; + case DSP_IOCALLSTACK: + data = iocs_data; + break; + case DSP_HEAPCALLSTACK: + data = heapcs_data; + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + data = get_indxobj_data (subtype); + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return -1; + Vector<Hist_data::HistItem*> *hi_data = data->get_hist_items (); + for (int i = 0, sz = hi_data->size (); i < sz; i++) + { + Hist_data::HistItem *hi = hi_data->fetch (i); + if (hi->obj == selObj) + return i; + } + return -1; +} + +MetricList * +DbeView::get_metric_list (MetricType mtype, bool compare, int gr_num) +{ + MetricList *mlist; + switch (mtype) + { + case MET_COMMON:// comparison mode, src & disasm views + if (gr_num == 0) + {// signifies same src file (or load obj) used by all groups + // show compare metrics in columns (not in separate source panels) + mlist = get_metric_list (MET_NORMAL); + break; + } + // once source panel per group; get metrics for this group + mlist = get_metric_list (mtype); + if (compare) + { + mlist = get_compare_mlist (mlist, gr_num - 1); + int mode = get_compare_mode (); + if ((mode & (CMP_DELTA | CMP_RATIO)) != 0) + { + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + char *expr_spec = m->get_expr_spec (); + if (expr_spec && (strcmp (expr_spec, NTXT ("EXPGRID==1")) != 0)) + { + int vbits = m->get_visbits () & ~(VAL_DELTA | VAL_RATIO); + if ((mode & CMP_RATIO) != 0) + vbits |= VAL_RATIO; + else if ((mode & CMP_DELTA) != 0) + vbits |= VAL_DELTA; + m->set_raw_visbits (vbits); + } + } + } + } + break; + default: + mlist = get_metric_list (mtype); + break; + } + return mlist; +} + +Hist_data * +DbeView::get_data (MetricList *mlist, Histable *selObj, int type, int subtype) +{ + Hist_data *data; + switch (type) + { + case DSP_FUNCTION: + delete func_data; + mlist = new MetricList (mlist); + func_data = get_hist_data (mlist, Histable::FUNCTION, subtype, Hist_data::ALL); + return func_data; + case DSP_LINE: + delete line_data; + mlist = new MetricList (mlist); + line_data = get_hist_data (mlist, Histable::LINE, subtype, Hist_data::ALL); + return line_data; + case DSP_PC: + delete pc_data; + mlist = new MetricList (mlist); + pc_data = get_hist_data (mlist, Histable::INSTR, subtype, Hist_data::ALL); + return pc_data; + case DSP_DATAOBJ: + delete dobj_data; + dobj_data = get_hist_data (mlist, Histable::DOBJECT, subtype, + Hist_data::ALL); + break; + case DSP_MEMOBJ: + return get_hist_data (mlist, Histable::MEMOBJ, subtype, Hist_data::ALL); + case DSP_INDXOBJ: + data = get_hist_data (mlist, Histable::INDEXOBJ, subtype, Hist_data::ALL); + indx_data->store (subtype, data); + return data; + case DSP_DLAYOUT: + delete dlay_data; + marks->reset (); + data = get_hist_data (mlist, Histable::DOBJECT, subtype, + Hist_data::LAYOUT); + // .. provides metric data for layout + dlay_data = get_data_space ()->get_layout_data (data, marks, + get_thresh_dis ()); + return dlay_data; + case DSP_CALLER: + delete callers; + callers = get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::CALLERS, selObj); + return callers; + case DSP_CALLEE: + delete callees; + callees = get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::CALLEES, selObj); + return callees; + case DSP_SELF: + // Center Function item + delete fitem_data; + fitem_data = get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::SELF, selObj); + return fitem_data; + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + case DSP_SOURCE: + case DSP_DISASM: + { + // Source or disassembly + if (selObj == NULL) + { + error_msg = status_str (DBEVIEW_NO_SEL_OBJ); + return NULL; + } + Function *func = (Function *) selObj->convertto (Histable::FUNCTION); + if (func == NULL) + { + error_msg = dbe_strdup (GTXT ("Not a real function; no source or disassembly available.")); + return NULL; + } + if (func->flags & FUNC_FLAG_SIMULATED) + { + error_msg = dbe_strdup (GTXT ("Not a real function; no source or disassembly available.")); + return NULL; + } + if (func->get_name () == NULL) + { + error_msg = dbe_strdup (GTXT ("Source location not recorded in experiment")); + return NULL; + } + Module *module = func->module; + if (module == NULL || module->get_name () == NULL) + { + error_msg = dbe_strdup (GTXT ("Object name not recorded in experiment")); + return NULL; + } + marks->reset (); + SourceFile *srcContext = (SourceFile *) selObj->convertto (Histable::SOURCEFILE); + sel_binctx = func; + + if (func_data == NULL) + func_data = get_hist_data (mlist, Histable::FUNCTION, subtype, Hist_data::ALL); + + // for source and disassembly the name needs to be invisible, + // but that's handled in the module code + if (type == DSP_SOURCE || type == DSP_SOURCE_V2) + { + marks2dsrc->reset (); + marks2dsrc_inc->reset (); + delete src_data; + data = src_data = module->get_data (this, mlist, Histable::LINE, + func_data->get_totals ()->value, srcContext, func, + marks, get_thresh_src (), get_src_compcom (), + get_src_visible (), get_hex_visible (), + false, false, marks2dsrc, marks2dsrc_inc); + } + else + { /* type == DSP_DISASM */ + marks2ddis->reset (); + marks2ddis_inc->reset (); + delete dis_data; + data = dis_data = module->get_data (this, mlist, Histable::INSTR, + func_data->get_totals ()->value, srcContext, func, + marks, get_thresh_dis (), get_dis_compcom (), + get_src_visible (), get_hex_visible (), + get_func_scope (), false, marks2ddis, + marks2ddis_inc); + } + return data; + } + default: + abort (); + } + return NULL; +} + +Histable * +DbeView::get_compare_obj (Histable *obj) +{ + char *nm; + switch (obj->get_type ()) + { + case Histable::LINE: + nm = obj->get_name (); + if (nm == NULL) + break; + if (dbeSession->comp_dbelines == NULL) + dbeSession->comp_dbelines = new HashMap<char*, DbeLine*>; + return dbeSession->comp_dbelines->get (nm, (DbeLine*) obj); + case Histable::SOURCEFILE: + nm = obj->get_name (); + if (nm == NULL) + break; + nm = get_basename (nm); + if (dbeSession->comp_sources == NULL) + dbeSession->comp_sources = new HashMap<char*, SourceFile*>; + return dbeSession->comp_sources->get (nm, (SourceFile*) obj); + default: + return obj->get_compare_obj (); + } + return obj; +} + +// +// get_hist_data() creates a new Hist_data object; +// it's caller's responsibility to delete it. +Hist_data * +DbeView::get_hist_data (MetricList *mlist_orig, Histable::Type type, + int subtype, Hist_data::Mode mode, Histable *obj, + Histable *context, Vector<Histable*> *sel_objs, + PathTree::PtreeComputeOption flag) +{ + Vector<Histable*> *objs = NULL; + if (obj != NULL) + { + objs = new Vector<Histable*>(); + objs->append (obj); + } + Hist_data *res = get_hist_data (mlist_orig, type, subtype, mode, objs, context, sel_objs, flag); + delete objs; + return res; +} + +Hist_data * +DbeView::get_hist_data (MetricList *mlist_orig, Histable::Type type, + int subtype, Hist_data::Mode mode, + Vector<Histable*> *objs, + Histable *context, Vector<Histable*> *sel_objs, + PathTree::PtreeComputeOption flag) +{ + MetricList *mlist = new MetricList (mlist_orig); + /* + * mlist differs from mlist_orig in two ways: + * - extra metrics have been added as needed to compute derived metrics + * - extra metrics have been added as needed to compute time for HWC (time converted) metrics + * (We don't drop those extra metrics but we don't display they to user.) + * - visibility bits have been added for compare mode (e.g., VAL_DELTA or VAL_RATIO) + * (We want to preserve those visbits.) + */ + // loop over mlist to add missing dependencies for derived metrics + for (long i = 0, sz = mlist->get_items ()->size (); i < sz; i++) + { + Metric *m = mlist->get_items ()->fetch (i); + char *expr_spec = m->get_expr_spec (); + if (expr_spec && (strcmp (expr_spec, NTXT ("EXPGRID==1")) != 0)) + { + int ind = mlist->get_listorder (m->get_cmd (), m->get_subtype (), NTXT ("EXPGRID==1")); + if (ind < 0) + { + BaseMetric *bm1 = dbeSession->find_metric (m->get_type (), m->get_cmd (), NTXT ("EXPGRID==1")); + Metric *m1 = new Metric (bm1, m->get_subtype ()); + m1->set_dmetrics_visbits (VAL_VALUE); + mlist->append (m1); + } + } + } + + for (long i = 0, sz = mlist->get_items ()->size (); i < sz; i++) + { + Metric *m = mlist->get_items ()->fetch (i); + if (m->get_type () == BaseMetric::DERIVED) + { + Definition *def = m->get_definition (); + Vector<BaseMetric*> *dependencies = def->get_dependencies (); + long *map = def->get_map (); + for (long i1 = 0, sz1 = dependencies ? dependencies->size () : 0; i1 < sz1; i1++) + { + BaseMetric *bm = dependencies->fetch (i1); + int ind = mlist->get_listorder (bm->get_cmd (), m->get_subtype (), m->get_expr_spec ()); + if (ind < 0) + { + BaseMetric *bm1 = dbeSession->find_metric (bm->get_type (), bm->get_cmd (), m->get_expr_spec ()); + assert (bm1 != NULL); + Metric *m1 = new Metric (bm1, m->get_subtype ()); + m1->set_dmetrics_visbits (VAL_VALUE); + ind = mlist->size (); + mlist->append (m1); + } + map[i1] = ind; + } + } + else if (m->get_type () == BaseMetric::HWCNTR) + { + if (m->is_tvisible () && m->get_dependent_bm ()) + { + int ii = mlist->get_listorder (m->get_dependent_bm ()->get_cmd (), + m->get_subtype (), m->get_expr_spec ()); + if (ii < 0) + { + BaseMetric *bm1 = dbeSession->find_metric (m->get_type (), + m->get_dependent_bm ()->get_cmd (), + m->get_expr_spec ()); + assert (bm1 != NULL); + Metric *m1 = new Metric (bm1, m->get_subtype ()); + m1->set_dmetrics_visbits ((m->get_visbits () + & ~VAL_VALUE) | VAL_TIMEVAL); + mlist->append (m1); + } + } + } + } + + // compute Hist_data + Hist_data *data; + switch (type) + { + case Histable::INSTR: + case Histable::LINE: + data = ptree->compute_metrics (mlist, type, mode, objs, context, sel_objs); + break; + case Histable::FUNCTION: + case Histable::MODULE: + case Histable::LOADOBJECT: + data = ptree->compute_metrics (mlist, type, mode, objs, NULL, + sel_objs, flag); + break; + case Histable::DOBJECT: + data = dspace->compute_metrics (mlist, type, mode, + objs ? objs->fetch (0) : NULL); + break; + case Histable::MEMOBJ: + case Histable::INDEXOBJ: + data = indxspaces->get (subtype)->compute_metrics (mlist, type, mode, + objs, NULL); + break; + case Histable::IOACTFILE: + if (objs == NULL) + { + data = iofile_data = iospace->compute_metrics (mlist, type, mode, + NULL); + break; + } + else + { + data = iospace->compute_metrics (mlist, type, mode, objs->fetch (0)); + break; + } + case Histable::IOACTVFD: + if (objs == NULL) + data = iovfd_data = iospace->compute_metrics (mlist, type, mode, NULL); + else + data = iospace->compute_metrics (mlist, type, mode, objs->fetch (0)); + break; + case Histable::IOCALLSTACK: + if (objs == NULL) + data = iocs_data = iospace->compute_metrics (mlist, type, mode, NULL); + else + data = iospace->compute_metrics (mlist, type, mode, objs->fetch (0)); + break; + case Histable::HEAPCALLSTACK: + if (objs == NULL) + data = heapcs_data = heapspace->compute_metrics (mlist, type, mode, NULL); + else + data = heapspace->compute_metrics (mlist, type, mode, objs->fetch (0)); + break; + default: + data = NULL; + break; + } + for (long i = mlist_orig->get_items ()->size (), + sz = mlist->get_items ()->size (); i < sz; i++) + { + Metric *m = mlist->get_items ()->get (i); + m->set_dmetrics_visbits (VAL_HIDE_ALL | m->get_visbits ()); + } + if (data) + data->nmetrics = mlist_orig->size (); + return data; +} + +char * +DbeView::get_mobj_name (int subtype) +{ + MemorySpace *ms = getMemorySpace (subtype); + if (ms == NULL) + ms = addMemorySpace (subtype); + return ms->getMemObjTypeName (); +} + +MemorySpace * +DbeView::getMemorySpace (int subtype) +{ + for (long i = 0, sz = VecSize (memspaces); i < sz; i++) + { + MemorySpace *ms = memspaces->get (i); + if (subtype == ms->getMemObjType ()) + return ms; + } + return NULL; +} + +MemorySpace * +DbeView::addMemorySpace (int subtype) +{ + MemorySpace *ms = new MemorySpace (this, subtype); + memspaces->append (ms); + return ms; +} + +Hist_data * +DbeView::get_indxobj_data (int subtype) +{ + if (subtype < 0 || subtype >= indx_data->size ()) + return NULL; + return indx_data->fetch (subtype); +} + +void +DbeView::set_indxobj_sel (int subtype, int sel_ind) +{ + Hist_data *data = get_indxobj_data (subtype); + if (data == NULL) + return; + if (sel_ind >= 0 && sel_ind < data->size ()) + { + Histable *obj = data->fetch (sel_ind)->obj; + sel_idxobj->store (subtype, obj); + } +} + +Histable * +DbeView::get_indxobj_sel (int subtype) +{ + return sel_idxobj->fetch (subtype); +} + +void +DbeView::addIndexSpace (int subtype) +{ + PathTree *is = new PathTree (this, subtype); + indxspaces->store (subtype, is); + indx_data->store (subtype, NULL); + sel_idxobj->store (subtype, NULL); + settings->indxobj_define (subtype, false); +} + +Histable * +DbeView::get_sel_obj_io (uint64_t id, Histable::Type type) +{ + if (iospace == NULL) + return NULL; + Histable *obj = NULL; + Hist_data *data = NULL; + switch (type) + { + case Histable::IOACTFILE: + data = iofile_data; + break; + case Histable::IOACTVFD: + data = iovfd_data; + break; + case Histable::IOCALLSTACK: + data = iocs_data; + break; + default: + break; + } + if (data == NULL) + return NULL; + + Vector<Hist_data::HistItem*> *hi_data = data->get_hist_items (); + int size = hi_data->size (); + for (int i = 0; i < size; i++) + { + Hist_data::HistItem *hi = hi_data->fetch (i); + if (hi->obj != NULL && (uint64_t) hi->obj->id == id) + { + obj = hi->obj; + break; + } + } + return obj; +} + +Histable * +DbeView::get_sel_obj_heap (uint64_t id) +{ + if (heapspace == NULL || heapcs_data == NULL) + return NULL; + Histable *obj = NULL; + Hist_data *data = heapcs_data; + Vector<Hist_data::HistItem*> *hi_data = data->get_hist_items (); + int size = hi_data->size (); + for (int i = 0; i < size; i++) + { + Hist_data::HistItem *hi = hi_data->fetch (i); + if ((hi->obj != NULL) && ((uint64_t) hi->obj->id) == id) + { + obj = hi->obj; + break; + } + } + return obj; +} + +CStack_data * +DbeView::get_cstack_data (MetricList *mlist) +{ + return ptree->get_cstack_data (mlist); +} + +Stats_data * +DbeView::get_stats_data (int index) +{ + DataView *packets = get_filtered_events (index, DATA_SAMPLE); + if (packets == NULL) + return NULL; + return new Stats_data (packets); +} + +Ovw_data * +DbeView::get_ovw_data (int index) +{ + DataView *packets = get_filtered_events (index, DATA_SAMPLE); + Experiment* exp = dbeSession->get_exp (index); + hrtime_t starttime = 0; + if (exp != NULL) + starttime = exp->getStartTime (); + if (packets == NULL) + return NULL; + return new Ovw_data (packets, starttime); +} + +char * +DbeView::set_filter (const char *filter_spec) +{ + if (dbe_strcmp (filter_spec, cur_filter_str) == 0) // Nothing was changed + return NULL; + + // if string is NULL, delete the filter + if (filter_spec == NULL) + { + if (cur_filter_str) + { + free (cur_filter_str); + cur_filter_str = NULL; + } + if (cur_filter_expr) + { + delete cur_filter_expr; + cur_filter_expr = NULL; + } + noParFilter = false; + purge_events (); + reset_data (false); + return NULL; + } + + // process the filter + Expression *expr = dbeSession->ql_parse (filter_spec); + if (expr == NULL) + return dbe_sprintf (GTXT ("Invalid filter specification `%s'\n"), filter_spec); + + if (dbe_strcmp (filter_spec, "1") == 0) + noParFilter = false; + else if (sel_obj != NULL) + if (sel_obj->get_type () == Histable::LINE) + if (expr->verifyObjectInExpr (sel_obj)) + noParFilter = true; + + // valid new filter + if (cur_filter_str != NULL) + { + free (prev_filter_str); + prev_filter_str = dbe_strdup (cur_filter_str); + } + free (cur_filter_str); + cur_filter_str = dbe_strdup (filter_spec); + delete cur_filter_expr; + cur_filter_expr = expr; + purge_events (); + reset_data (false); + return NULL; +} + +FilterExp * +DbeView::get_FilterExp (Experiment *exp) +{ + if (cur_filter_expr == NULL) + return NULL; + Expression::Context *ctx = new Expression::Context (this, exp); + return new FilterExp (cur_filter_expr, ctx, noParFilter); +} + +char * +DbeView::get_filter () +{ + return dbe_strdup (cur_filter_str); +} + +FilterSet * +DbeView::get_filter_set (int n) +{ + fflush (stderr); + if (n >= filters->size ()) + return NULL; + return ( filters->fetch (n)); +} + +Vector<FilterNumeric*> * +DbeView::get_all_filters (int nexp) +{ + FilterSet *fs = get_filter_set (nexp); + return fs ? fs->get_all_filters () : NULL; +} + +FilterNumeric * +DbeView::get_FilterNumeric (int nexp, int idx) +{ + FilterSet *fs = get_filter_set (nexp); + return fs ? fs->get_filter (idx) : NULL; +} + +void +DbeView::backtrack_filter() +{ + if (prev_filter_str != NULL) + set_filter(prev_filter_str); + else set_filter("1"); // reset + +} + +void +DbeView::update_advanced_filter () +{ + char *s = get_advanced_filter (); + if (dbe_strcmp (s, cur_filter_str)) + { + phaseIdx++; + char *err_msg = set_filter (s); + if (err_msg) + { +#ifdef DEBUG + fprintf (stderr, NTXT ("ERROR: Advanced Filter: '%s'\n"), err_msg); +#endif + } + } + free (s); +} + +bool +DbeView::set_pattern (int n, Vector<char *> *pattern_str, bool *error) +{ + Vector<FilterNumeric*> *filts = get_all_filters (n); + + bool ret = false; + *error = false; + int imax = pattern_str->size (); + if (imax > filts->size ()) + imax = filts->size (); + for (int i = 0; i < imax; i++) + { + FilterNumeric *f = filts->fetch (i); + char *s = pattern_str->fetch (i); + if (s == NULL) + continue; + if (f->set_pattern (s, error)) + ret = true; + } + + if (ret || cur_filter_expr) + { + update_advanced_filter (); + filter_active = true; + } + return ret; +} + +static void +append_experiments (StringBuilder *sb, int first, int last) +{ + if (first == -1) + return; + if (sb->length () != 0) + sb->append (NTXT (" || ")); + sb->append ('('); + if (first == last) + { + sb->append (NTXT ("EXPID==")); + sb->append (first); + } + else + { + sb->append (NTXT ("EXPID>=")); + sb->append (first); + sb->append (NTXT (" && EXPID<=")); + sb->append (last); + } + sb->append (')'); +} + +char * +DbeView::get_advanced_filter () +{ + StringBuilder sb; + bool wasFalse = false; + int first = -1, last = -1; + for (int n = 0, nexps = dbeSession->nexps (); n < nexps; n++) + { + FilterSet *fs = get_filter_set (n); + char *s = fs->get_advanced_filter (); + if (s) + { + if (streq (s, NTXT ("1"))) + { + last = n + 1; + if (first == -1) + first = last; + continue; + } + append_experiments (&sb, first, last); + first = -1; + if (streq (s, NTXT ("0"))) + { + wasFalse = true; + continue; + } + if (sb.length () != 0) + sb.append (NTXT (" || ")); + sb.append (NTXT ("(EXPID==")); + sb.append (n + 1); + sb.append (NTXT (" && (")); + sb.append (s); + free (s); + sb.append (NTXT ("))")); + } + else + { + last = n + 1; + if (first == -1) + first = last; + } + } + if (first != 1) + { + append_experiments (&sb, first, last); + first = -1; + } + if (sb.length () == 0) + sb.append (wasFalse ? '0' : '1'); + else + append_experiments (&sb, first, last); + return sb.toString (); +} + +bool +DbeView::set_pattern (int m, char *pattern) +{ + bool error = false; + + // Store original setting in case of error + int nexps = dbeSession->nexps (); + int orig_phaseIdx = phaseIdx; + bool *orig_enable = new bool[nexps]; + char **orig_pattern = new char*[nexps]; + for (int i = 0; i < nexps; i++) + { + orig_pattern[i] = get_FilterNumeric (i, m)->get_pattern (); + orig_enable[i] = get_exp_enable (i); + set_exp_enable (i, false); + } + + // Copy the pattern so that we could safely modify it + char *buf = dbe_strdup (pattern); + FilterNumeric *fexp = NULL; + char *pb, *pe; + pb = pe = buf; + for (bool done = false; !done; pe++) + { + if (*pe == ':') + { + // experiment filter; + *pe = '\0'; + fexp = new FilterNumeric (NULL, NULL, NULL); + fexp->set_range (1, nexps, nexps); + fexp->set_pattern (pb, &error); + if (error) + break; + pb = pe + 1; + } + else if (*pe == '+' || *pe == '\0') + { + // entity filter + if (*pe == '\0') + done = true; + else + *pe = '\0'; + for (int i = 0; i < nexps; i++) + { + if (!fexp || fexp->is_selected (i + 1)) + { + FilterNumeric *f = get_FilterNumeric (i, m); + f->set_pattern (pb, &error); + if (error) + break; + set_exp_enable (i, true); + } + } + if (error) + break; + delete fexp; + fexp = NULL; + pb = pe + 1; + } + } + + if (error) + { + for (int i = 0; i < nexps; i++) + { + bool err; + set_exp_enable (i, orig_enable[i]); + FilterNumeric *f = get_FilterNumeric (i, m); + f->set_pattern (orig_pattern[i], &err); + free (orig_pattern[i]); + } + phaseIdx = orig_phaseIdx; + } + else + { + update_advanced_filter (); + filter_active = true; + } + delete[] orig_enable; + delete[] orig_pattern; + delete fexp; + free (buf); + return !error; +} + +void +DbeView::set_view_mode (VMode newmode) +{ + if (newmode != settings->get_view_mode ()) + { + + // For OpenMP, the expert mode path-tree is already present with the user mode + // No need to increase the phaseIdx to trigger recomputation of path-tree + // if we toggle between user and expert modes + if (!(dbeSession->is_omp_available () + && ((newmode == VMODE_EXPERT + && settings->get_view_mode () == VMODE_USER) + || (newmode == VMODE_USER + && settings->get_view_mode () == VMODE_EXPERT)))) + phaseIdx++; // For all other cases + setNewViewMode (); + settings->set_view_mode (newmode); + } +} + +Cmd_status +DbeView::set_view_mode (char *str, bool fromRC) +{ + VMode old = settings->get_view_mode (); + Cmd_status ret = settings->set_view_mode (str, fromRC); + if (old != settings->get_view_mode ()) + phaseIdx++; + return ret; +} + +Cmd_status +DbeView::set_en_desc (char *str, bool fromRC) +{ + // Tell the session + Settings *s = dbeSession->get_settings (); + s->set_en_desc (str, fromRC); + + // and tell our settings + return settings->set_en_desc (str, fromRC); +} + +// Get processor stats messages +char * +DbeView::get_processor_msg (int type) +{ + if (ptree == NULL) // if no PathTree, no messages + return NULL; + + StringBuilder sb; + Emsg *m = (type == PSTAT_MSG) ? ptree->fetch_stats () : ptree->fetch_warnings (); + for (; m != NULL; m = m->next) + { + char* newmsg = m->get_msg (); + sb.append (newmsg); + sb.append ("\n"); + } + + if (type == PSTAT_MSG) + ptree->delete_stats (); + else + ptree->delete_warnings (); + return (sb.length () > 0) ? sb.toString () : NULL; +} + +void +DbeView::dump_nodes (FILE *outfile) +{ + FILE *f = (outfile == NULL ? stderr : outfile); + ptree->print (f); +} + +// Dump the clock profile events +void +DbeView::dump_profile (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + char * stateNames [/*LMS_NUM_STATES*/] = LMS_STATE_STRINGS; + + // Process clock profile date + DataView *packets = get_filtered_events (idx, DATA_CLOCK); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, + GTXT ("\nTotal Clock Profiling Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + for (long i = 0; i < packets->getSize (); i++) + { + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + + // get the properties from the packet + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + int mstate = (int) packets->getIntValue (PROP_MSTATE, i); + int nticks = (int) packets->getIntValue (PROP_NTICK, i); + + char *sname; + char buf[1024]; + if (mstate >= 0 && mstate < LMS_NUM_STATES) + sname = stateNames[mstate]; + else + { + snprintf (buf, sizeof (buf), NTXT ("Unexpected mstate = %d"), mstate); + sname = buf; + } + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"), + i, expr_ts, ts / NANOSEC, ts % NANOSEC, + expr_ts / NANOSEC, expr_ts % NANOSEC, + thrid, cpuid, stack_size); + fprintf (out_file, + GTXT (" mstate = %d (%s), nticks = %d\n"), + mstate, sname, nticks); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, + GTXT ("\nNo Clock Profiling Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the sync trace events +void +DbeView::dump_sync (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + + // Process heap trace date + DataView *packets = get_filtered_events (idx, DATA_SYNCH); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, + GTXT ("\nTotal Synctrace Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + + for (long i = 0; i < packets->getSize (); i++) + { + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + + // get the properties from the packet + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + uint64_t syncobj = (uint64_t) packets->getLongValue (PROP_SOBJ, i); + hrtime_t syncrtime = (uint64_t) packets->getLongValue (PROP_SRQST, i); + hrtime_t syncdelay = expr_ts - syncrtime; + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"), + i, expr_ts, ts / NANOSEC, ts % NANOSEC, + expr_ts / NANOSEC, expr_ts % NANOSEC, thrid, + cpuid, stack_size); + fprintf (stderr, + GTXT (" synchronization object @ 0x%016llx; synchronization delay %3lld.%09lld\n"), + (unsigned long long) syncobj, (long long) (syncdelay / NANOSEC), (long long) (syncdelay % NANOSEC)); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, GTXT ("\nNo Synctrace Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the IO trace events +void +DbeView::dump_iotrace (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + + // Process IO trace date + DataView *packets = get_filtered_events (idx, DATA_IOTRACE); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, + GTXT ("\nTotal IO trace Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + for (long i = 0; i < packets->getSize (); i++) + { + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + + // get the properties from the packet + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + IOTrace_type iotrtype = (IOTrace_type) packets->getIntValue (PROP_IOTYPE, i); + uint32_t iofd = (uint32_t) packets->getIntValue (PROP_IOFD, i); + uint64_t ionbyte = (uint64_t) packets->getIntValue (PROP_IONBYTE, i); + hrtime_t iorqst = (hrtime_t) packets->getLongValue (PROP_IORQST, i); + uint32_t ioofd = (uint32_t) packets->getIntValue (PROP_IOOFD, i); + FileSystem_type iofstype = (FileSystem_type) packets->getIntValue (PROP_CPUID, i); + int64_t iovfd = (int64_t) packets->getIntValue (PROP_IOVFD, i); + + char *fName = NULL; + StringBuilder *sb = (StringBuilder*) packets->getObjValue (PROP_IOFNAME, i); + if (sb != NULL && sb->length () > 0) + fName = sb->toString (); + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + const char *iotrname; + switch (iotrtype) + { + case READ_TRACE: + iotrname = "ReadTrace"; + break; + case WRITE_TRACE: + iotrname = "WriteTrace"; + break; + case OPEN_TRACE: + iotrname = "OpenTrace"; + break; + case CLOSE_TRACE: + iotrname = "CloseTrace"; + break; + case OTHERIO_TRACE: + iotrname = "OtherIOTrace"; + break; + case READ_TRACE_ERROR: + iotrname = "ReadTraceError"; + break; + case WRITE_TRACE_ERROR: + iotrname = "WriteTraceError"; + break; + case OPEN_TRACE_ERROR: + iotrname = "OpenTraceError"; + break; + case CLOSE_TRACE_ERROR: + iotrname = "CloseTraceError"; + break; + case OTHERIO_TRACE_ERROR: + iotrname = "OtherIOTraceError"; + break; + default: + iotrname = "UnknownIOTraceType"; + break; + } + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"), + i, expr_ts, ts / NANOSEC, ts % NANOSEC, + expr_ts / NANOSEC, expr_ts % NANOSEC, + thrid, cpuid, stack_size); + fprintf (out_file, + GTXT (" %s: fd = %d, ofd = %d, vfd = %lld, fstype = %d, rqst = %3lld.%09lld\n"), + iotrname, (int) iofd, (int) ioofd, (long long) iovfd, + (int) iofstype, (long long) (iorqst / NANOSEC), + (long long) (iorqst % NANOSEC)); + fprintf (out_file, GTXT (" filename = `%s', nbytes = %d\n"), + STR (fName), (int) ionbyte); + free (fName); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, GTXT ("\nNo IO trace Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the HWC Profiling events +void +DbeView::dump_hwc (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + + // Dump HWC profiling data + DataView *packets = get_filtered_events (idx, DATA_HWC); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, + GTXT ("\nTotal HW Counter Profiling Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + for (long i = 0; i < packets->getSize (); i++) + { + const char * hwc_name; + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + uint32_t tag = (uint32_t) packets->getIntValue (PROP_HWCTAG, i); + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + + // This will work even with a different counter in every packet. + if (tag < 0 || tag >= MAX_HWCOUNT + || !exp->coll_params.hw_aux_name[tag]) + // if the packet has an invalid tag, use <invalid> as its name + hwc_name = "<invalid>"; + else + hwc_name = exp->coll_params.hw_aux_name[tag]; + int64_t mval = packets->getLongValue (PROP_HWCINT, i); + const char *err = HWCVAL_HAS_ERR (mval) ? " $$" : ""; + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n count = %10lld (0x%016llx), tag = %d (%s)%s\n"), + (long) i, (long long) expr_ts, + (long long) (ts / NANOSEC), (long long) (ts % NANOSEC), + (long long) (expr_ts / NANOSEC), (long long) (expr_ts % NANOSEC), + (int) thrid, (int) cpuid, (int) stack_size, + (long long) (HWCVAL_CLR_ERR (mval)), (long long) mval, + (int) tag, hwc_name, err); + + // dump extended HWC packets values + uint64_t va = (uint64_t) packets->getLongValue (PROP_VADDR, i); + uint64_t pa = (uint64_t) packets->getLongValue (PROP_PADDR, i); + fprintf (out_file, GTXT (" va = 0x%016llx, pa = 0x%016llx\n"), + (unsigned long long) va, (unsigned long long) pa); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, + GTXT ("\nNo HWC Profiling Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the Heap events +void +DbeView::dump_heap (FILE *out_file) +{ + char *heapstrings[] = HEAPTYPE_STATE_USTRINGS; + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + + // Process heap trace date + DataView *packets = get_filtered_events (idx, DATA_HEAP); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, GTXT ("\nTotal Heaptrace Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + for (long i = 0; i < packets->getSize (); i++) + { + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + + // get the properties from the packet + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + uint32_t heaptype = (uint32_t) packets->getIntValue (PROP_HTYPE, i); + uint64_t heapsize = (uint64_t) packets->getULongValue (PROP_HSIZE, i); + uint64_t heapvaddr = (uint64_t) packets->getULongValue (PROP_HVADDR, i); + uint64_t heapovaddr = (uint64_t) packets->getULongValue (PROP_HOVADDR, i); + if (heaptype == MUNMAP_TRACE) + { + heapsize = (uint64_t) packets->getULongValue (PROP_HOVADDR, i); + heapovaddr = 0; + } + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"), + i, expr_ts, ts / NANOSEC, ts % NANOSEC, + expr_ts / NANOSEC, expr_ts % NANOSEC, + thrid, cpuid, stack_size); + char *typestr = heapstrings[heaptype]; + fprintf (out_file, + GTXT (" type = %d (%s), size = %llu (0x%llx), VADDR = 0x%016llx, OVADDR = 0x%016llx\n"), + (int) heaptype, typestr, (long long unsigned int) heapsize, + (long long unsigned int) heapsize, + (long long unsigned int) heapvaddr, + (long long unsigned int) heapovaddr); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, GTXT ("\nNo Heaptrace Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the Java garbage collector events +void +DbeView::dump_gc_events (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + if (!exp->has_java) + fprintf (out_file, + GTXT ("# No GC events in experiment %d, %s (PID %d, %s)\n"), + idx, exp->get_expt_name (), exp->getPID (), exp->utargname); + else + { + Vector<GCEvent*> *gce = exp->get_gcevents (); + GCEvent *this_event; + int index; + fprintf (out_file, + GTXT ("# %li events in experiment %d: %s (PID %d, %s)\n"), + gce->size (), idx, + exp->get_expt_name (), exp->getPID (), exp->utargname); + fprintf (out_file, + GTXT ("# exp:idx GC_start, GC_end, GC_duration\n")); + Vec_loop (GCEvent*, gce, index, this_event) + { + hrtime_t start = this_event->start - exp->getStartTime (); + hrtime_t end = this_event->end - exp->getStartTime (); + hrtime_t delta = this_event->end - this_event->start; + fprintf (out_file, + "%5d:%d, %3lld.%09lld, %3lld.%09lld, %3lld.%09lld\n", + idx, index, + (long long) (start / NANOSEC), (long long) (start % NANOSEC), + (long long) (end / NANOSEC), (long long) (end % NANOSEC), + (long long) (delta / NANOSEC), (long long) (delta % NANOSEC)); + } + } + } +} + +void +DbeView::purge_events (int n) +{ + phaseIdx++; + int lst; + if (n == -1) + lst = filters->size (); + else + lst = n > filters->size () ? filters->size () : n + 1; + for (int i = n == -1 ? 0 : n; i < lst; i++) + { + Vector<DataView*> *expDataViewList = dataViews->fetch (i); + if (expDataViewList) + { + // clear out all the data_ids, but don't change the vector size + for (int data_id = 0; data_id < expDataViewList->size (); ++data_id) + { + delete expDataViewList->fetch (data_id); + expDataViewList->store (data_id, NULL); + } + } + } + filter_active = false; +} + + +// LIBRARY_VISIBILITY +void +DbeView::resetAndConstructShowHideStacks () +{ + for (int n = 0, nexps = dbeSession->nexps (); n < nexps; n++) + { + Experiment *exp = dbeSession->get_exp (n); + if (exp != NULL) + resetAndConstructShowHideStack (exp); + } +} + +// LIBRARY_VISIBILITY +void +DbeView::resetAndConstructShowHideStack (Experiment *exp) +{ + exp->resetShowHideStack (); + /* Vector<DataDescriptor*> *dDscrs = */ exp->getDataDescriptors (); + + DataDescriptor *dd; + // Construct show hide stack only for objects which have call stacks + // list below similar to path tree. What about HEAP_SZ? (DBFIXME) + dd = exp->get_raw_events (DATA_CLOCK); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_SYNCH); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_IOTRACE); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_HWC); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_HEAP); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_RACE); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_DLCK); + if (dd != NULL) + constructShowHideStack (dd, exp); +} + +// LIBRARY_VISIBILITY +void +DbeView::constructShowHideStack (DataDescriptor *dDscr, Experiment *exp) +{ + if (dDscr == NULL) + return; + int stack_prop = PROP_NONE; + VMode view_mode = get_view_mode (); + if (view_mode == VMODE_MACHINE) + stack_prop = PROP_MSTACK; + else if (view_mode == VMODE_EXPERT) + stack_prop = PROP_XSTACK; + else if (view_mode == VMODE_USER) + stack_prop = PROP_USTACK; + + for (long j = 0, sz = dDscr->getSize (); j < sz; j++) + { + void *stackId = dDscr->getObjValue (stack_prop, j); + Vector<Histable*> *stack = (Vector<Histable*>*)CallStack::getStackPCs (stackId); + int stack_size = stack->size (); + bool hide_on = false; + LoadObject *hide_lo = NULL; + Histable *last_addr = NULL; + Histable *api_addr = NULL; + DbeInstr *h_instr; + + Vector<Histable*> *hidepcs = new Vector<Histable*>; + for (int i = stack_size - 1; i >= 0; i--) + { + bool leaf = (i == 0); + Histable *cur_addr = stack->fetch (i); + Function *func = (Function*) cur_addr->convertto (Histable::FUNCTION); + if (func != NULL) + { + Module *mod = func->module; + LoadObject *lo = mod->loadobject; + int segx = lo->seg_idx; + if ((get_lo_expand (segx) == LIBEX_API) && (i != (stack_size - 1))) + { + leaf = true; + api_addr = cur_addr; + } + else if (get_lo_expand (segx) == LIBEX_HIDE) + { + if (hide_on) + { + if (lo != hide_lo) + { + // Changed hidden loadobject + if (last_addr != NULL) + { + h_instr = hide_lo->get_hide_instr ((DbeInstr*) last_addr); + hidepcs->append (h_instr); + last_addr = cur_addr; + } + hide_lo = lo; + } + } + else + { + // Start hide + hide_on = true; + last_addr = cur_addr; + hide_lo = lo; + } + if (!leaf) + continue; + } + else + { + hide_on = false; + if (last_addr != NULL) + { + h_instr = hide_lo->get_hide_instr ((DbeInstr*) last_addr); + hidepcs->append (h_instr); + last_addr = NULL; + } + } + } + if (last_addr != NULL && leaf) cur_addr = last_addr; + if (hide_on) + { + h_instr = hide_lo->get_hide_instr ((DbeInstr*) cur_addr); + hidepcs->append (h_instr); + if (api_addr != NULL) + hidepcs->append (api_addr); + } + else + hidepcs->append (cur_addr); + if (leaf) + break; + } + for (int i = 0, k = hidepcs->size () - 1; i < k; ++i, --k) + hidepcs->swap (i, k); + + CallStack *cstkSH = exp->callTreeShowHide (); + CallStackNode *hstack = (CallStackNode *) cstkSH->add_stack (hidepcs); + dDscr->setObjValue (PROP_HSTACK, j, hstack); + CallStack::setHideStack (stackId, hstack); + delete hidepcs; + delete stack; + } +} + +DataView * +DbeView::get_filtered_events (int idx, int data_id) +{ + if (idx < 0 || idx >= dataViews->size ()) + return NULL; + Vector<DataView*> *expDataViewList = dataViews->fetch (idx); + if (!expDataViewList) + return NULL; // Weird + + DataView *dview = expDataViewList->fetch (data_id); + Experiment *exp = dbeSession->get_exp (idx); + if (dview) + { + // if show-hide is on force a reconstruction of hide stacks + // LIBRARY_VISIBILITY + if (!showAll && (showHideChanged || newViewMode)) + { + DataDescriptor *dDscr = exp->get_raw_events (data_id); + constructShowHideStack (dDscr, exp); + } + return dview; + } + + int orig_data_id = data_id; + data_id = exp->base_data_id (data_id); + if (orig_data_id != data_id) + // orig_data_id is a derived DataView. Get the master DataView: + dview = expDataViewList->fetch (data_id); + if (dview == NULL) + { + Expression *saved = cur_filter_expr; + if (!adjust_filter (exp)) + return NULL; + + DataDescriptor *dDscr = exp->get_raw_events (data_id); + if (!showAll && (showHideChanged || newViewMode)) + constructShowHideStack (dDscr, exp); + + Emsg *m = exp->fetch_warnings (); + if (m != NULL) + this->warning_msg = m->get_msg (); + + if (dDscr != NULL) + { + FilterExp *filter = get_FilterExp (exp); + dview = dDscr->createView (); + dview->setFilter (filter); + if (dview->getSize () < dDscr->getSize ()) + filter_active = true; + } + expDataViewList->store (data_id, dview); + + if (saved) + { + delete cur_filter_expr; + cur_filter_expr = saved; + } + } + if (orig_data_id != data_id) + { + // create the derived DataView: + dview = exp->create_derived_data_view (orig_data_id, dview); + expDataViewList->store (orig_data_id, dview); + } + return dview; +} + +DataView * +DbeView::get_filtered_events (int idx, int data_id, + const int sortprops[], int sortprop_count) +{ + DataView *packets = get_filtered_events (idx, data_id); + if (packets) + packets->sort (sortprops, sortprop_count); + return packets; +} + +bool +DbeView::adjust_filter (Experiment *exp) +{ + if (cur_filter_expr) + { + Expression::Context ctx (this, exp); + resetFilterHideMode (); + Expression *fltr = cur_filter_expr->pEval (&ctx); + if (fltr->complete ()) + { // Filter is a constant + if (fltr->eval (NULL) == 0) + return false; + delete fltr; + fltr = NULL; + } + cur_filter_expr = fltr; + } + return true; +} + +// Moved from Cacheable.cc: +char * +DbeView::status_str (DbeView_status status) +{ + switch (status) + { + case DBEVIEW_SUCCESS: + return NULL; + case DBEVIEW_NO_DATA: + return dbe_strdup (GTXT ("Data not available for this filter selection")); + case DBEVIEW_IO_ERROR: + return dbe_strdup (GTXT ("Unable to open file")); + case DBEVIEW_BAD_DATA: + return dbe_strdup (GTXT ("Data corrupted")); + case DBEVIEW_BAD_SYMBOL_DATA: + return dbe_strdup (GTXT ("Functions/Modules information corrupted")); + case DBEVIEW_NO_SEL_OBJ: + return dbe_strdup (GTXT ("No selected object, bring up Functions Tab")); + } + return NULL; +} + +Histable * +DbeView::set_sel_obj (Histable *obj) +{ + if (obj) + { + switch (obj->get_type ()) + { + case Histable::INSTR: + lastSelInstr = (DbeInstr *) obj; + lastSelFunc = lastSelInstr->func; + this->sel_binctx = lastSelFunc; + break; + case Histable::FUNCTION: + if (lastSelInstr && lastSelInstr->func != obj) + lastSelInstr = NULL; + lastSelFunc = (Function *) obj; + break; + case Histable::LINE: + { + DbeLine *dbeLine = (DbeLine *) obj; + if (dbeLine->func) + { + // remember previous DbeInstr and DbeFunc + lastSelFunc = dbeLine->func; + if (lastSelInstr && lastSelInstr->func != lastSelFunc) + lastSelInstr = NULL; + this->sel_binctx = lastSelFunc; + } + else + this->sel_binctx = dbeLine->convertto (Histable::FUNCTION); + break; + } + case Histable::MODULE: + case Histable::LOADOBJECT: + case Histable::EADDR: + case Histable::MEMOBJ: + case Histable::INDEXOBJ: + case Histable::PAGE: + case Histable::DOBJECT: + case Histable::SOURCEFILE: + case Histable::IOACTFILE: + case Histable::IOACTVFD: + case Histable::IOCALLSTACK: + case Histable::HEAPCALLSTACK: + case Histable::EXPERIMENT: + case Histable::OTHER: + break; + } + } + sel_obj = obj; + Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d obj %s\n"), + __LINE__, obj ? obj->dump () : "NULL"); + Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d sel_obj %s\n"), + __LINE__, sel_obj ? sel_obj->dump () : "NULL"); + Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d lastSelFunc %s\n"), + __LINE__, lastSelFunc ? lastSelFunc->dump () : "NULL"); + Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d lastSelInstr %s\n"), + __LINE__, lastSelInstr ? lastSelInstr->dump () : "NULL"); + return sel_obj; +} + +DbeInstr * +DbeView::convert_line_to_instr (DbeLine *dbeLine) +{ + Dprintf (DEBUG_DBE, "### convert_line_to_instr DbeView::%d dbeLine=%s\n", __LINE__, dbeLine->dump ()); + Function *func = convert_line_to_func (dbeLine); + if (func) + { + Dprintf (DEBUG_DBE, "### convert_line_to_instr DbeView::%d func=%s\n", __LINE__, func->dump ()); + DbeInstr *dbeInstr = func->mapLineToPc (dbeLine); + Dprintf (DEBUG_DBE && dbeInstr, "### convert_line_to_instr DbeView::%d dbeInstr=%s\n", __LINE__, dbeInstr->dump ()); + return dbeInstr; + } + Dprintf (DEBUG_DBE && lastSelInstr, "### convert_line_to_instr DbeView::%d lastSelInstr=%s\n", __LINE__, lastSelInstr->dump ()); + return lastSelInstr; +} + +DbeInstr * +DbeView::convert_func_to_instr (Function *func) +{ + return (lastSelInstr && lastSelInstr->func == func) ? + lastSelInstr : (DbeInstr *) func->convertto (Histable::INSTR); +} + +Function * +DbeView::convert_line_to_func (DbeLine *dbeLine) +{ + Function *func = dbeLine->func; + if (func) + return func; + if (lastSelFunc != NULL) + // Can be mapped to the same function ? + for (DbeLine *dl = dbeLine->dbeline_base; dl; dl = dl->dbeline_func_next) + if (dl->func == lastSelFunc) + return lastSelFunc; + + PathTree *pathTree = NULL; + Function *firstFunc = NULL; + for (DbeLine *dl = dbeLine->dbeline_base; dl; dl = dl->dbeline_func_next) + { + // Find a first function with non-zero metrics + if (dl->func) + { + if (pathTree == NULL) + pathTree = get_path_tree (); + if (pathTree->get_func_nodeidx (dl->func)) + return dl->func; + if (firstFunc == NULL) + firstFunc = dl->func; + } + } + // Take a first function + return firstFunc; +} + +Histable * +DbeView::get_sel_obj (Histable::Type type) +{ + Histable *lastSelObj = sel_obj; + Dprintf (DEBUG_DBE, NTXT ("### get_sel_obj: DbeView.cc:%d type=%d sel_obj %s\n"), + __LINE__, type, lastSelObj ? lastSelObj->dump () : "NULL"); + if (lastSelObj == NULL) + return NULL; + switch (type) + { + case Histable::INSTR: + if (!showAll) + { + // DBFIXME LIBRARY VISIBILITY + // hack to get to the hide mode object for PCs when filtering + // with a PC in timeline + if (lastSelObj->get_type () == Histable::INSTR) + { + Function *func = (Function*) (lastSelObj->convertto (Histable::FUNCTION)); + LoadObject *lo = func->module->loadobject; + if (get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + } + } + if (lastSelObj->get_type () == Histable::LINE) + return convert_line_to_instr ((DbeLine*) lastSelObj); + else if (lastSelObj->get_type () == Histable::FUNCTION) + return convert_func_to_instr ((Function *) lastSelObj); + return lastSelObj->convertto (type); + case Histable::FUNCTION: + if (lastSelObj->get_type () == Histable::LINE) + { + Function *func = convert_line_to_func ((DbeLine*) lastSelObj); + if (func) + return func; + return NULL; + } + return lastSelObj->convertto (type); + case Histable::LINE: + default: + return lastSelObj->convertto (type); + } +} diff --git a/gprofng/src/DbeView.h b/gprofng/src/DbeView.h new file mode 100644 index 0000000..bb6bb90 --- /dev/null +++ b/gprofng/src/DbeView.h @@ -0,0 +1,842 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * The DbeView class represents a window into the data managed by a DbeSession + * + * A DbeView has a Settings class that determines the user preferences, + * instantiated initially as a copy of the one in the DbeSession + * that created it, or in the DbeView being cloned by the DbeSession + * + * A DbeView has a vector of Experiment pointers, matching the one in the + * DbeSession, and a vector of enable bits governing which of the + * Experiments are currently being used to process the data. + * + * A DbeView has three vectors of Metrics, one for functions, etc., + * a second for callers/callees, and a third for dataspace/memoryspace. + * + * A DbeView has a vector of FilterSet's (q.v.), one for each Experiment, + * used to determine how the data is filtered. + * + * Each DbeView has its own instantiation of the objects representing + * the processed, filtered data. Currently these are a PathTree + * for computing text-based metrics, a DataSpace for computing + * data-based metrics, and a MemorySpace used for computing + * memory-object-based metrics. + */ + +#ifndef _DBEVIEW_H +#define _DBEVIEW_H + +#include <stdio.h> +#include "dbe_structs.h" +#include "vec.h" +#include "enums.h" +#include "util.h" +#include "DerivedMetrics.h" +#include "Histable.h" +#include "Hist_data.h" +#include "Settings.h" +#include "Metric.h" +#include "Table.h" +#include "PathTree.h" + +class Application; +class DataView; +class Experiment; +class Expression; +class FilterSet; +class FilterNumeric; +class FilterExp; +class Function; +class Histable; +class MetricList; +class Module; +class Ovw_data; +class PathTree; +class DataSpace; +class MemorySpace; +class Stats_data; +class LoadObject; +class IOActivity; +class HeapActivity; + +class DbeView +{ +public: + DbeView (Application *app, Settings *_settings, int _vindex); + DbeView (DbeView *dbev, int _vindex); + ~DbeView (); + + // Access functions for settings in the view + Settings * + get_settings () + { + return settings; + }; + + // Get the list of tabs for this view + Vector<DispTab*> * + get_TabList () + { + return settings->get_TabList (); + }; + + // Get the list of memory tabs for this view + Vector<bool> * + get_MemTabState () + { + return settings->get_MemTabState (); + }; + + // Set the list of memory tabs for this view + void + set_MemTabState (Vector<bool>*sel) + { + settings->set_MemTabState (sel); + }; + + // Get the list of index tabs for this view + Vector<bool> * + get_IndxTabState () + { + return settings->get_IndxTabState (); + }; + + // Set the list of memory tabs for this view + void + set_IndxTabState (Vector<bool>*sel) + { + settings->set_IndxTabState (sel); + }; + + // controlling the name format + Cmd_status + set_name_format (char *str) + { + return settings->set_name_format (str); + }; + + void + set_name_format (int fname_format, bool soname) + { + settings->set_name_format (fname_format, soname); + }; + + Histable::NameFormat + get_name_format () + { + return settings->name_format; + } + + // processing modes: view_mode + Cmd_status set_view_mode (char *str, bool fromRC); // from a string + void set_view_mode (VMode mode); // from the analyzer + + VMode + get_view_mode () + { + return settings->get_view_mode (); + }; + + // handling of descendant processes + Cmd_status set_en_desc (char *str, bool rc); // from a string + + bool + check_en_desc (const char * lineage_name = NULL, const char *targname = NULL) + { + return settings->check_en_desc (lineage_name, targname); + }; + + // Controlling the print line-limit + char * + set_limit (char *str, bool rc) // from a string + { + settings->set_limit (str, rc); + return NULL; + }; + + char * + set_limit (int _limit) + { + settings->limit = _limit; + return NULL; + }; + + int + get_limit () + { + return settings->limit; + }; + + // Controlling the print format mode + char * + set_printmode (char *str) + { + return settings->set_printmode (str); + }; + + enum PrintMode + get_printmode () + { + return settings->print_mode; + }; + + char + get_printdelimiter () + { + return settings->print_delim; + }; + + char * + get_printmode_str () + { + return dbe_strdup (settings->str_printmode); + }; + + // processing compiler commentary visibility bits, and other source annotation + // controls + Cmd_status + proc_compcom (const char *cmd, bool isSrc, bool rc) + { + return settings->proc_compcom (cmd, isSrc, rc); + }; + + char * + get_str_scompcom () + { + return settings->str_scompcom; + }; + + char * + get_str_dcompcom () + { + return settings->str_dcompcom; + }; + + void + set_src_compcom (int v) + { + settings->src_compcom = v; + }; + + int + get_src_compcom () + { + return settings->src_compcom; + }; + + void + set_dis_compcom (int v) + { + settings->dis_compcom = v; + }; + + int + get_dis_compcom () + { + return settings->dis_compcom; + }; + + void + set_cmpline_visible (bool vis) + { + settings->set_cmpline_visible (vis); + } + + int + get_cmpline_visible () + { + return settings->cmpline_visible; + } + + void + set_funcline_visible (bool vis) + { + settings->set_funcline_visible (vis); + } + + int + get_funcline_visible () + { + return settings->funcline_visible; + } + + // controls for disassembly presentation + void + set_src_visible (int vis) + { + settings->set_src_visible (vis); + } + + int + get_src_visible () + { + return settings->src_visible; + } + + void + set_srcmetric_visible (bool vis) + { + settings->set_srcmetric_visible (vis); + } + + bool + get_srcmetric_visible () + { + return settings->srcmetric_visible; + } + + void + set_hex_visible (bool vis) + { + settings->set_hex_visible (vis); + } + + bool + get_hex_visible () + { + return settings->hex_visible; + } + + // processing and accessing the threshold settings + Cmd_status + proc_thresh (char *cmd, bool isSrc, bool rc) + { + return settings->proc_thresh (cmd, isSrc, rc); + }; + + void + set_thresh_src (int v) + { + settings->threshold_src = v; + }; + + int + get_thresh_src () + { + return settings->threshold_src; + }; + + void + set_thresh_dis (int v) + { + settings->threshold_dis = v; + }; + + int + get_thresh_dis () + { + return settings->threshold_dis; + }; + + // controls for the Timeline mode, stack presentation + Cmd_status + proc_tlmode (char *cmd, bool rc) + { + return settings->proc_tlmode (cmd, rc); + }; + + void + set_tlmode (int _tlmode) + { + settings->tlmode = _tlmode; + }; + + int + get_tlmode () + { + return settings->tlmode; + }; + + void + set_stack_align (int _stack_align) + { + settings->stack_align = _stack_align; + }; + + int + get_stack_align () + { + return settings->stack_align; + }; + + void + set_stack_depth (int _stack_depth) + { + settings->stack_depth = _stack_depth; + }; + + int + get_stack_depth () + { + return settings->stack_depth; + }; + + // Controls for which data is shown in Timeline + Cmd_status + proc_tldata (char *cmd, bool rc) + { + return settings->proc_tldata (cmd, rc); + }; + + void + set_tldata (const char* tldata_cmd) + { + settings->set_tldata (tldata_cmd); + }; + + char* + get_tldata () + { + return settings->get_tldata (); + }; + + // settings controlling the expand/collapse of functions within each LoadObject + enum LibExpand get_lo_expand (int idx); + + // set_lo_expand -- returns true if any change + bool set_lo_expand (int idx, enum LibExpand how); + + // set_libexpand -- returns true if any change + bool set_libexpand (char *liblist, enum LibExpand flag); + void update_lo_expands (); + bool set_libdefaults (); + void reset (); + void reset_data (bool all); + + char * + get_error_msg () + { + return error_msg; + }; + + void + clear_error_msg () + { + error_msg = NULL; + }; + + char * + get_warning_msg () + { + return warning_msg; + }; + + void + clear_warning_msg () + { + warning_msg = NULL; + }; + char *get_processor_msg (int type); + + // methods controlling the metric list + BaseMetric *register_metric_expr (BaseMetric::Type type, char *aux, char *expr_spec); + Vector<BaseMetric*> *get_all_reg_metrics (); + void reset_metric_list (MetricList *mlist, int cmp_mode); + + // Get the various metric master lists + MetricList *get_metric_ref (MetricType mtype); + + // Get the various current metric lists + MetricList *get_metric_list (int dsptype, int subtype); + MetricList *get_metric_list (MetricType mtype); + MetricList *get_metric_list (MetricType mtype, bool compare, int gr_num); + MetricList *get_compare_mlist (MetricList *met_list, int grInd); + + // Set the metric list, from a string specification + char *setMetrics (char *metricspec, bool fromRcFile); + + // Set the sort metric, from its name + char *setSort (char *sortname, MetricType mtype, bool fromRcFile); + + // Set the sort metric, from its visible index (Analyzer) + void setSort (int visindex, MetricType mtype, bool reverse); + + // Resort any cached data, after changing sort + void resortData (MetricType mtype); + + // Get the sort metric + char *getSort (MetricType mtype); + char *getSortCmd (MetricType mtype); + + // reset the metrics + void reset_metrics (); + bool comparingExperiments (); + + int + get_compare_mode () + { + return settings->compare_mode; + }; + + void + reset_compare_mode (int mode) + { + settings->compare_mode = mode; + }; + + void set_compare_mode (int mode); // modifies the global MET_* arrays + void add_compare_metrics (MetricList *mlist); + void remove_compare_metrics (MetricList *mlist); + Histable *get_compare_obj (Histable *obj); + + // method for printing the instruction-frequency report + void ifreq (FILE *); + + // methods controlling the experiment list + void add_experiment (int index, bool enabled); + void add_subexperiment (int index, bool enabled); + void add_experiment_epilogue (); + void drop_experiment (int index); + bool get_exp_enable (int n); + void set_exp_enable (int n, bool e); + + // method for new-style filtering + char *set_filter (const char *filter_spec); + char *get_filter (void); + char *get_advanced_filter (); + void backtrack_filter (); + void update_advanced_filter (); + FilterExp *get_FilterExp (Experiment *exp); + + Expression * + get_filter_expr () + { + return cur_filter_expr; + }; + + // methods controlling old-style filtering + Vector<FilterNumeric*> *get_all_filters (int nexp); + FilterNumeric *get_FilterNumeric (int nexp, int idx); + bool set_pattern (int n, Vector<char *> *pattern_str, bool *error); + bool set_pattern (int m, char *pattern); + + // Data processing objects + PathTree * + get_path_tree () + { + return ptree; + }; + + DataSpace * + get_data_space () + { + return dspace; + }; + + IOActivity * + get_io_space () + { + return iospace; + }; + + HeapActivity * + get_heap_space () + { + return heapspace; + }; + Hist_data *get_data (MetricList *mlist, Histable *selObj, int type, int subtype); + int get_sel_ind (Histable *selObj, int type, int subtype); + + // Return histogram data for the specified arguments. + Hist_data *get_hist_data (MetricList *mlist, Histable::Type type, + int subtype, // used for memory objects only + Hist_data::Mode mode, + Vector<Histable*> *objs = NULL, + Histable *context = NULL, + Vector<Histable*> *sel_objs = NULL, + PathTree::PtreeComputeOption flag = PathTree::COMPUTEOPT_NONE + ); + Hist_data *get_hist_data (MetricList *mlist, Histable::Type type, + int subtype, // used for memory objects only + Hist_data::Mode mode, Histable *obj, + Histable *context = NULL, + Vector<Histable*> *sel_objs = NULL, + PathTree::PtreeComputeOption flag = PathTree::COMPUTEOPT_NONE + ); + CStack_data *get_cstack_data (MetricList *); + Stats_data *get_stats_data (int index); + Ovw_data *get_ovw_data (int index); + + char *names_src[3]; // names for anno-src + char *names_dis[3]; // names for anno-dis + + // Get filtered packets. Ordering is NOT guaranteed to be + // stable between calls; DataView indexes are not persistent - + // use underlying DataDescriptor ids if you don't consume data right away. + // Parameters: idx==exp_id, data_id==kind==ProfData_type + DataView *get_filtered_events (int idx, int data_id); + DataView *get_filtered_events (int idx, int data_id, + const int sortprops[], int sortprop_count); + + // SORT is not used for PathTree. + // PathTree reads data once and discards. It doesn't + // depend on the indices so sort can be performed w/o recomputing pathtree. + // Timeline is the primary consumer of sort(), however Races also need to sort. + // + // YM: I can't remember the context for the following note, but + // In case I need it when we refactor more TBR stuff, here it is: + // base metrics like USER_CPU are known,(but we should/should not?) + // explicitly set DATA_CLOCK as a property/attribute? + bool adjust_filter (Experiment *exp); + + // Generated report data + Hist_data *func_data; // function list data + Hist_data *line_data; // hot line list data + Hist_data *pc_data; // hot PC list data + Hist_data *src_data; // annotated source data + Hist_data *dis_data; // annotated disasm data + Hist_data *fitem_data; // func item for callers/callees + Hist_data *callers; // callers data + Hist_data *callees; // callees data + Hist_data *dobj_data; // data object list data + Hist_data *dlay_data; // data layout data + Hist_data *iofile_data; // io data aggregated by file name + Hist_data *iovfd_data; // io data aggregated by virtual fd + Hist_data *iocs_data; // io data aggregated by call stack + Hist_data *heapcs_data; // heap data aggregated by call stack + Vector<Hist_data*> *indx_data; // index object data + Vector<int> *lobjectsNoJava; // List of indices into LoadObjects excluding java classes + + // memory object data -- create MemorySpace, if needed + MemorySpace *getMemorySpace (int subtype); + char *get_mobj_name (int subtype); + void addIndexSpace (int type); + Hist_data *get_indxobj_data (int subtype); + void set_indxobj_sel (int subtype, int sel_ind); + Histable *get_indxobj_sel (int subtype); + void set_obj_sel_io (int type, long sel_ind); + Histable *set_sel_obj (Histable *obj); + Histable *get_sel_obj (Histable::Type type); + Histable *get_sel_obj_io (uint64_t id, Histable::Type type); + Histable *get_sel_obj_heap (uint64_t id); + Histable *sel_obj; // current selected obj + Histable *sel_dobj; // current selected data obj + Histable *sel_binctx; // current binary context + Vector<Histable*> *sel_idxobj; // selected index objects + char *error_msg; // error message + char *warning_msg; // warning message + Vector<int> *marks; // flagged as important for anno src/dis + Vector<int_pair_t> *marks2dsrc; + Vector<int_pair_t> *marks2dsrc_inc; + Vector<int_pair_t> *marks2ddis; + Vector<int_pair_t> *marks2ddis_inc; + + void dump_nodes (FILE *); // dump out the pathtree nodes + void dump_profile (FILE *); // dump out the clock profiling events + void dump_sync (FILE *); // dump out the synctrace events + void dump_iotrace (FILE *); // dump out the IO trace events + void dump_hwc (FILE *); // dump out the HWC Profiling events + void dump_heap (FILE *); // dump out the heap trace events + void dump_gc_events (FILE *); // dump out the Java garbage collector events + + int vindex; // index of this view -- set by Analyzer + bool func_scope; + + bool + get_func_scope () + { + return func_scope; + }; + + void + set_func_scope (bool scope_only) + { + func_scope = scope_only; + }; + + // value set T if filtering is active, i.e., some packets were dropped + bool filter_active; + + bool + get_filter_active () + { + return filter_active; + }; + + DerivedMetrics * + get_derived_metrics () + { + return derived_metrics; + } + + // Internal time (time means change) + int + getPhaseIdx () + { + return phaseIdx; + } + + enum DbeView_status + { + DBEVIEW_SUCCESS = 0, + DBEVIEW_NO_DATA, + DBEVIEW_IO_ERROR, + DBEVIEW_BAD_DATA, + DBEVIEW_BAD_SYMBOL_DATA, + DBEVIEW_NO_SEL_OBJ + }; + static char *status_str (DbeView_status status); + + bool + isOmpDisMode () + { + return ompDisMode; + } + + void + setOmpDisMode () + { + ompDisMode = true; + } + + void + resetOmpDisMode () + { + ompDisMode = false; + } + + bool + isShowHideChanged () + { + return showHideChanged; + } + + void + setShowHideChanged () + { + showHideChanged = true; + } + + void + resetShowHideChanged () + { + showHideChanged = false; + } + + bool + isNewViewMode () + { + return newViewMode; + } + + void + setNewViewMode () + { + newViewMode = true; + } + + void + resetNewViewMode () + { + newViewMode = false; + } + + bool + isFilterHideMode () + { + return filterHideMode; + } + + void + setFilterHideMode () + { + filterHideMode = true; + } + + void + resetFilterHideMode () + { + filterHideMode = false; + } + + bool + isShowAll () + { + return showAll; + } + + void + setShowAll () + { + showAll = true; + } + + void + resetShowAll () + { + showAll = false; + } + void resetAndConstructShowHideStacks (); + +private: + void init (); + Metric *get_compare_metric (Metric *mtr, int groupNum); + + // methods controlling old-style filtering + FilterSet *get_filter_set (int n); + + void purge_events (int n = -1); + + char *cur_filter_str; + char *prev_filter_str; + Expression *cur_filter_expr; + bool noParFilter; + + // MemorySpace's -- added when a request is made; for now, never dropped + Vector<MemorySpace*> *memspaces; + MemorySpace *addMemorySpace (int mtype); + + Vector<FilterSet*> *filters; + Vector<enum LibExpand> *lo_expands; + Vector<BaseMetric*> *reg_metrics; // vector of registered metrics + Vector<MetricList*> *metrics_lists; // metrics list of MET_NORMAL, MET_CALL... + // note: includes compare metrics + Vector<MetricList*> *metrics_ref_lists; + DerivedMetrics *derived_metrics; // vector of derived metrics + + DataSpace *dspace; + PathTree *ptree; + Vector<PathTree *> *indxspaces; + IOActivity *iospace; + HeapActivity *heapspace; + int phaseIdx; + bool ompDisMode; + bool filterHideMode; + bool showAll; + bool showHideChanged; + bool newViewMode; + + // Filtered events + Vector<Vector<DataView*>*> *dataViews; //outer idx is exp_id, inner is data_id + Settings *settings; + + Application *app; + Function *convert_line_to_func (DbeLine *dbeLine); + DbeInstr *convert_line_to_instr (DbeLine *dbeLine); + DbeInstr *convert_func_to_instr (Function *func); + DbeInstr *lastSelInstr; + Function *lastSelFunc; + void constructShowHideStack (DataDescriptor* dDscr, Experiment *exp); + void resetAndConstructShowHideStack (Experiment *exp); +}; + +#endif /* _DBEVIEW_H */ diff --git a/gprofng/src/DefaultHandler.h b/gprofng/src/DefaultHandler.h new file mode 100644 index 0000000..4c3d82c --- /dev/null +++ b/gprofng/src/DefaultHandler.h @@ -0,0 +1,114 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * org/xml/sax/helpers/DefaultHandler.java + * Based on JavaTM 2 Platform Standard Ed. 5.0 + */ + +#ifndef _DefaultHandler_h +#define _DefaultHandler_h + +/* + * org/xml/sax/Attributes.java + */ +class Attributes +{ +public: + virtual ~Attributes () { }; + + virtual int getLength () = 0; + virtual const char *getQName (int index) = 0; + virtual const char *getValue (int index) = 0; + virtual int getIndex (const char *qName) = 0; + virtual const char *getValue (const char *qName) = 0; +}; + +/* + * org/xml/sax/SAXException.java + */ +class SAXException +{ +public: + SAXException (); + SAXException (const char *message); + virtual ~SAXException (); + char *getMessage (); + +private: + char *message; +}; + +class SAXParseException : public SAXException +{ +public: + SAXParseException (char *message, int lineNumber, int columnNumber); + + int + getLineNumber () + { + return lineNumber; + } + + int + getColumnNumber () + { + return columnNumber; + } + +private: + int lineNumber; + int columnNumber; +}; + +class DefaultHandler +{ +public: + virtual ~DefaultHandler () { }; + + virtual void startDocument () = 0; + virtual void endDocument () = 0; + virtual void startElement (char *uri, char *localName, char *qName, + Attributes *attributes) = 0; + virtual void endElement (char *uri, char *localName, char *qName) = 0; + virtual void characters (char *ch, int start, int length) = 0; + virtual void ignorableWhitespace (char *ch, int start, int length) = 0; + + virtual void + warning (SAXParseException *e) + { + delete e; + } + + virtual void + error (SAXParseException *e) + { + delete e; + } + + virtual void + fatalError (SAXParseException *e) + { + throw ( e); + } + void dump_startElement (const char *qName, Attributes *attributes); +}; + +#endif /* _DefaultHandler_h */ diff --git a/gprofng/src/DefaultMap.h b/gprofng/src/DefaultMap.h new file mode 100644 index 0000000..4a87fcc --- /dev/null +++ b/gprofng/src/DefaultMap.h @@ -0,0 +1,232 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_DEFAULTMAP_H +#define _DBE_DEFAULTMAP_H + +#include <assert.h> +#include <vec.h> +#include <Map.h> + +template <typename Key_t, typename Value_t> +class DefaultMap : public Map<Key_t, Value_t> +{ +public: + + DefaultMap (); + ~DefaultMap (); + void clear (); + void put (Key_t key, Value_t val); + Value_t get (Key_t key); + Value_t get (Key_t key, typename Map<Key_t, Value_t>::Relation rel); + Value_t remove (Key_t); + Vector<Key_t> *keySet (); + Vector<Value_t> *values (); + +private: + + struct Entry + { + Key_t key; + Value_t val; + }; + + static const int CHUNK_SIZE; + static const int HTABLE_SIZE; + + int entries; + int nchunks; + Entry **chunks; + Vector<Entry*> *index; + Entry **hashTable; +}; + + +template <typename Key_t, typename Value_t> +const int DefaultMap<Key_t, Value_t>::CHUNK_SIZE = 16384; +template <typename Key_t, typename Value_t> +const int DefaultMap<Key_t, Value_t>::HTABLE_SIZE = 1024; + +template <typename Key_t, typename Value_t> +DefaultMap<Key_t, Value_t>::DefaultMap () +{ + entries = 0; + nchunks = 0; + chunks = NULL; + index = new Vector<Entry*>; + hashTable = new Entry*[HTABLE_SIZE]; + for (int i = 0; i < HTABLE_SIZE; i++) + hashTable[i] = NULL; +} + +template <typename Key_t, typename Value_t> +DefaultMap<Key_t, Value_t>::~DefaultMap () +{ + for (int i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; + delete index; + delete[] hashTable; +} + +template <typename Key_t, typename Value_t> +void +DefaultMap<Key_t, Value_t>::clear () +{ + entries = 0; + index->reset (); + for (int i = 0; i < HTABLE_SIZE; i++) + hashTable[i] = NULL; +} + +template <typename Key_t> +inline unsigned +hash (Key_t key) +{ + unsigned h = (unsigned) ((unsigned long) key); + h ^= (h >> 20) ^ (h >> 12); + return (h ^ (h >> 7) ^ (h >> 4)); +} + +template <typename Key_t, typename Value_t> +void +DefaultMap<Key_t, Value_t>::put (Key_t key, Value_t val) +{ + unsigned idx = hash (key) % HTABLE_SIZE; + Entry *entry = hashTable[idx]; + if (entry && entry->key == key) + { + entry->val = val; + return; + } + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + entry = index->fetch (md); + int cmp = entry->key < key ? -1 : entry->key > key ? 1 : 0; + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + entry->val = val; + return; + } + } + if (entries >= nchunks * CHUNK_SIZE) + { + nchunks++; + // Reallocate Entry chunk array + Entry **new_chunks = new Entry*[nchunks]; + for (int i = 0; i < nchunks - 1; i++) + new_chunks[i] = chunks[i]; + delete[] chunks; + chunks = new_chunks; + + // Allocate new chunk for entries. + chunks[nchunks - 1] = new Entry[CHUNK_SIZE]; + } + entry = &chunks[entries / CHUNK_SIZE][entries % CHUNK_SIZE]; + entry->key = key; + entry->val = val; + index->insert (lo, entry); + hashTable[idx] = entry; + entries++; +} + +template <typename Key_t, typename Value_t> +Value_t +DefaultMap<Key_t, Value_t>::get (Key_t key) +{ + unsigned idx = hash (key) % HTABLE_SIZE; + Entry *entry = hashTable[idx]; + if (entry && entry->key == key) + return entry->val; + + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + entry = index->fetch (md); + int cmp = entry->key < key ? -1 : entry->key > key ? 1 : 0; + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + hashTable[idx] = entry; + return entry->val; + } + } + return (Value_t) 0; +} + +template <typename Key_t, typename Value_t> +Value_t +DefaultMap<Key_t, Value_t>::get (Key_t key, + typename Map<Key_t, Value_t>::Relation rel) +{ + if (rel != Map<Key_t, Value_t>::REL_EQ) + return (Value_t) 0; + return get (key); +} + +template <typename Key_t, typename Value_t> +Value_t +DefaultMap<Key_t, Value_t>::remove (Key_t) +{ + // Not implemented + if (1) + assert (0); + return (Value_t) 0; +} + +template <typename Key_t, typename Value_t> +Vector<Value_t> * +DefaultMap<Key_t, Value_t>::values () +{ + Vector<Value_t> *vals = new Vector<Value_t>(entries); + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + vals->append (entry->val); + } + return vals; +} + +template <typename Key_t, typename Value_t> +Vector<Key_t> * +DefaultMap<Key_t, Value_t>::keySet () +{ + Vector<Key_t> *keys = new Vector<Key_t>(entries); + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + keys->append (entry->key); + } + return keys; +} + +#endif diff --git a/gprofng/src/DefaultMap2D.h b/gprofng/src/DefaultMap2D.h new file mode 100644 index 0000000..8045aad --- /dev/null +++ b/gprofng/src/DefaultMap2D.h @@ -0,0 +1,147 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_DEFAULTMAP2D_H +#define _DBE_DEFAULTMAP2D_H + +#include <assert.h> +#include <vec.h> +#include <DefaultMap.h> +#include <IntervalMap.h> +#include <Map2D.h> + +/* + * Default Map2D implementation. + * + * Default Map2D is a cartesian product of two default Maps. + */ + +template <typename Key1_t, typename Key2_t, typename Value_t> +class DefaultMap2D : public Map2D<Key1_t, Key2_t, Value_t> +{ +public: + DefaultMap2D (); + DefaultMap2D (typename Map2D<Key1_t, Key2_t, Value_t>::MapType _type); + ~DefaultMap2D (); + void put (Key1_t key1, Key2_t key2, Value_t val); + Value_t get (Key1_t key1, Key2_t key2); + Value_t get (Key1_t key1, Key2_t key2, + typename Map2D<Key1_t, Key2_t, Value_t>::Relation rel); + Value_t remove (Key1_t, Key2_t); + +private: + typename Map2D<Key1_t, Key2_t, Value_t>::MapType type; + Map<Key1_t, Map<Key2_t, Value_t>*> *map1; + Vector<Map<Key2_t, Value_t>*> *map2list; +}; + +template <typename Key1_t, typename Key2_t, typename Value_t> +DefaultMap2D<Key1_t, Key2_t, Value_t>::DefaultMap2D () +{ + type = Map2D<Key1_t, Key2_t, Value_t>::Default; + map1 = new DefaultMap<Key1_t, Map<Key2_t, Value_t>*>; + map2list = new Vector<Map<Key2_t, Value_t>*>; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +DefaultMap2D<Key1_t, Key2_t, Value_t>::DefaultMap2D ( + typename Map2D<Key1_t, Key2_t, Value_t>::MapType _type) +{ + type = _type; + map1 = new DefaultMap<Key1_t, Map<Key2_t, Value_t>*>; + map2list = new Vector<Map<Key2_t, Value_t>*>; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +DefaultMap2D<Key1_t, Key2_t, Value_t>::~DefaultMap2D () +{ + map2list->destroy (); + delete map2list; + delete map1; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +void +DefaultMap2D<Key1_t, Key2_t, Value_t>::put (Key1_t key1, Key2_t key2, Value_t val) +{ + Map<Key2_t, Value_t> *map2 = map1->get (key1); + if (map2 == NULL) + { + if (type == Map2D<Key1_t, Key2_t, Value_t>::Interval) + map2 = new IntervalMap<Key2_t, Value_t>; + else + map2 = new DefaultMap<Key2_t, Value_t>; + map2list->append (map2); + map1->put (key1, map2); + } + map2->put (key2, val); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +DefaultMap2D<Key1_t, Key2_t, Value_t>::get (Key1_t key1, Key2_t key2) +{ + Map<Key2_t, Value_t> *map2 = map1->get (key1); + if (map2 == NULL) + return (Value_t) 0; + return map2->get (key2); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +DefaultMap2D<Key1_t, Key2_t, Value_t>::get (Key1_t key1, Key2_t key2, + typename Map2D<Key1_t, Key2_t, Value_t>::Relation rel) +{ + Map<Key2_t, Value_t> *map2 = map1->get (key1); + if (map2 == NULL) + return (Value_t) 0; + typename Map<Key2_t, Value_t>::Relation rel2; + switch (rel) + { + case Map2D<Key1_t, Key2_t, Value_t>::REL_EQLT: + rel2 = map2->REL_LT; + break; + case Map2D<Key1_t, Key2_t, Value_t>::REL_EQLE: + rel2 = map2->REL_LE; + break; + case Map2D<Key1_t, Key2_t, Value_t>::REL_EQGE: + rel2 = map2->REL_GE; + break; + case Map2D<Key1_t, Key2_t, Value_t>::REL_EQGT: + rel2 = map2->REL_GT; + break; + default: + rel2 = map2->REL_EQ; + break; + } + return map2->get (key2, rel2); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +DefaultMap2D<Key1_t, Key2_t, Value_t>::remove (Key1_t, Key2_t) +{ + // Not implemented + if (1) + assert (0); + return (Value_t) 0; +} + +#endif diff --git a/gprofng/src/DerivedMetrics.cc b/gprofng/src/DerivedMetrics.cc new file mode 100644 index 0000000..9f504a5 --- /dev/null +++ b/gprofng/src/DerivedMetrics.cc @@ -0,0 +1,293 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <strings.h> +#include "DerivedMetrics.h" +#include "util.h" + +enum opType +{ + opNULL, + opPrimitive, + opDivide +}; + +class definition +{ +public: + definition(); + ~definition(); + char *name; + char *def; + opType op; + definition *arg1; + definition *arg2; + int index; +}; + +definition::definition () +{ + name = def = NULL; + arg1 = arg2 = NULL; +} + +definition::~definition () +{ + free (name); + free (def); +} + +DerivedMetrics::DerivedMetrics () +{ + items = new Vector<definition*>; +} + +DerivedMetrics::~DerivedMetrics () +{ + Destroy (items); +} + +definition * +DerivedMetrics::add_definition (char *_name, char *_username, char *_def) +{ + definition *p; + + // if the name doesn't matter, maybe there is a duplicate we can use + if (_name == NULL) + { + int i; + Vec_loop (definition*, items, i, p) + { + if (strcmp (p->def, _def) == 0) + return p; + } + } + + p = new definition; + p->name = dbe_strdup (_name); + p->def = dbe_strdup (_def); + + // parse the definition + if (strchr (_def, '/') == NULL) + { + // it's a primitive metric + p->op = opPrimitive; + p->arg1 = p->arg2 = NULL; + + } + else + { + // it's some operation on arguments + p->op = opDivide; + char *op_ptr = strchr (p->def, '/'); + *op_ptr = 0; + p->arg1 = add_definition (NULL, NULL, p->def); + *op_ptr = '/'; + p->arg2 = add_definition (NULL, NULL, op_ptr + 1); + } + p->index = items->size (); + items->append (p); + return p; +} + +int * +DerivedMetrics::construct_map (Vector<Metric*> *mitems, BaseMetric::SubType st, char *expr_spec) +{ + if (items == NULL) + return NULL; + int ndm = items->size (); + if (ndm == 0) + return NULL; + int nmetrics = mitems->size (); + + // allocate arrays for the mapping between derived metrics and requested values + int *map = (int *) malloc (ndm * sizeof (int)); + + // map derived metrics to requested metrics // EUGENE explain this more clearly + // 0 means not mapped + // >0 means primitive metric maps to map-1 + // <0 means derived metric maps to 1-map + int ndm_requested = 0; + for (int idm = 0; idm < ndm; idm++) + { + definition *defdm = items->fetch (idm); + map[idm] = 0; + + // figure out what name to use for this derived metric + char *dname; + if (defdm->op == opPrimitive) + dname = defdm->def; + else + { + dname = defdm->name; + if (dname == NULL) break; + } + + // look for this name among metrics + int im; + for (im = 0; im < nmetrics; im++) + { + Metric *m = mitems->fetch (im); + if (strcmp (dname, m->get_cmd ()) == 0 && m->get_subtype () == st) + // apparent match, but let's check comparison mode + if (dbe_strcmp (expr_spec, m->get_expr_spec ()) == 0) + break; + } + + // encode the mapping + if (im >= nmetrics) + map[idm] = 0; // does not map to requested metrics + else if (defdm->op == opPrimitive) + map[idm] = +1 + im; // encode as a positive index + else + { + map[idm] = -1 - im; // encode as a negative index + ndm_requested++; + } + } + if (ndm_requested == 0) + { + free (map); + map = NULL; + } + return map; +} + +void +DerivedMetrics::fill_dependencies (definition *def, int *vec) +{ + switch (def->op) + { + case opPrimitive: + vec[def->index] = 1; + break; + case opDivide: + fill_dependencies (def->arg1, vec); + fill_dependencies (def->arg2, vec); + break; + default: + break; + } +} + +Vector<definition*> * +DerivedMetrics::get_dependencies (definition *def) +{ + int n = items->size (); + + // zero out a vector representing definitions + int *vec = (int *) malloc (n * sizeof (int)); + for (int i = 0; i < n; i++) + vec[i] = 0; + fill_dependencies (def, vec); + + // construct the dependency vector + Vector<definition*> *dependencies = new Vector<definition*>; + for (int i = 0; i < n; i++) + if (vec[i] == 1) + dependencies->append (items->fetch (i)); + free (vec); + return dependencies; +} + +void +DerivedMetrics::dump (FILE *dis_file, int verbosity) +{ + int i; + definition *item; + + // deal with the possibility that names might be NULL + const char *UNNAMED = "(unnamed)"; +#define NAME(x) ( (x) ? (x) : UNNAMED) + + Vec_loop (definition*, items, i, item) + { + // at low verbosity, skip over some items + if (verbosity == 0) + { + if (item->name == NULL) + continue; + if (strcmp (item->name, item->def) && item->op == opPrimitive) + continue; + } + + // dump the definition + switch (item->op) + { + case opPrimitive: + fprintf (dis_file, "%s [%s] is a primitive metric\n", NAME (item->name), + item->def); + break; + case opDivide: + fprintf (dis_file, "%s [%s] = %s [%s] / %s [%s]\n", NAME (item->name), + item->def, NAME (item->arg1->name), item->arg1->def, + NAME (item->arg2->name), item->arg2->def); + break; + default: + fprintf (dis_file, "%s [%s] has an unrecognized op %d\n", + NAME (item->name), item->def, item->op); + break; + } + } +} + +double +DerivedMetrics::eval_one_item (definition *def, int *map, double *values) +{ + switch (def->op) + { + case opNULL: + fprintf (stderr, GTXT ("cannot eval NULL expression\n")); + return 0.; + case opPrimitive: + { + int ival = map[def->index]; + if (ival <= 0) return 0.; + ival--; + return values[ival]; + } + case opDivide: + { + double x1 = eval_one_item (def->arg1, map, values); + double x2 = eval_one_item (def->arg2, map, values); + if (x2 == 0) return 0.; + return (x1 / x2); + } + default: + fprintf (stderr, GTXT ("unknown expression\n")); + return 0.; + } +} + +int +DerivedMetrics::eval (int *map, double *values) +{ + for (int i = 0, n = items->size (); i < n; i++) + { + if (map[i] < 0) + { + int ival = -1 - map[i]; + values[ival] = eval_one_item (items->fetch (i), map, values); + } + } + return 0; +} + diff --git a/gprofng/src/DerivedMetrics.h b/gprofng/src/DerivedMetrics.h new file mode 100644 index 0000000..b457e5e --- /dev/null +++ b/gprofng/src/DerivedMetrics.h @@ -0,0 +1,54 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DERIVEDMETRICS_H +#define _DERIVEDMETRICS_H + +#include <stdio.h> +#include "BaseMetric.h" +#include "Metric.h" + +class definition; + +class DerivedMetrics +{ +public: + DerivedMetrics (); + ~DerivedMetrics (); + definition *add_definition (char *_name, char *_username, char *_def); + int *construct_map (Vector<Metric*> *mitems, BaseMetric::SubType st, + char *expr_spec); + void dump (FILE *dis_file, int verbosity); + double eval_one_item (definition *def, int *map, double *values); + int eval (int *map, double *values); + void fill_dependencies (definition *def, int *vec); + Vector<definition*> *get_dependencies (definition *def); + + Vector<definition*> * + get_items () + { + return items; + } + +private: + Vector<definition*> *items; +}; + +#endif diff --git a/gprofng/src/Disasm.cc b/gprofng/src/Disasm.cc new file mode 100644 index 0000000..0fec9c3 --- /dev/null +++ b/gprofng/src/Disasm.cc @@ -0,0 +1,403 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <string.h> +#include <sys/param.h> + +#include "disassemble.h" +#include "dis-asm.h" +#include "demangle.h" +#include "dbe_types.h" +#include "DbeSession.h" +#include "Elf.h" +#include "Disasm.h" +#include "Stabs.h" +#include "i18n.h" +#include "util.h" +#include "StringBuilder.h" + +struct DisContext +{ + bool is_Intel; + Stabs *stabs; + uint64_t pc; // first_pc <= pc < last_pc + uint64_t first_pc; + uint64_t last_pc; + uint64_t f_offset; // file offset for first_pc + int codeptr[4]; // longest instruction length may not be > 16 + Data_window *elf; +}; + +static const int MAX_DISASM_STR = 2048; +static const int MAX_INSTR_SIZE = 8; + +Disasm::Disasm (char *fname) +{ + dwin = NULL; + dis_str = NULL; + need_swap_endian = false; + my_stabs = Stabs::NewStabs (fname, fname); + if (my_stabs == NULL) + return; + stabs = my_stabs; + platform = stabs->get_platform (); + disasm_open (); +} + +Disasm::Disasm (Platform_t _platform, Stabs *_stabs) +{ + dwin = NULL; + dis_str = NULL; + need_swap_endian = false; + stabs = _stabs; + platform = _platform; + my_stabs = NULL; + disasm_open (); +} + +static int +fprintf_func (void *arg, const char *fmt, ...) +{ + char buf[512]; + va_list vp; + va_start (vp, fmt); + int cnt = vsnprintf (buf, sizeof (buf), fmt, vp); + va_end (vp); + + Disasm *dis = (Disasm *) arg; + dis->dis_str->append (buf); + return cnt; +} + +/* Get LENGTH bytes from info's buffer, at target address memaddr. + Transfer them to myaddr. */ +static int +read_memory_func (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, + disassemble_info *info) +{ + unsigned int opb = info->octets_per_byte; + size_t end_addr_offset = length / opb; + size_t max_addr_offset = info->buffer_length / opb; + size_t octets = (memaddr - info->buffer_vma) * opb; + if (memaddr < info->buffer_vma + || memaddr - info->buffer_vma > max_addr_offset + || memaddr - info->buffer_vma + end_addr_offset > max_addr_offset + || (info->stop_vma && (memaddr >= info->stop_vma + || memaddr + end_addr_offset > info->stop_vma))) + return -1; + memcpy (myaddr, info->buffer + octets, length); + return 0; +} + +static void +print_address_func (bfd_vma addr, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "0x%llx", (unsigned long long) addr); +} + +static asymbol * +symbol_at_address_func (bfd_vma addr ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) +{ + return NULL; +} + +static bfd_boolean +symbol_is_valid (asymbol * sym ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) +{ + return TRUE; +} + +static void +memory_error_func (int status, bfd_vma addr, disassemble_info *info) +{ + info->fprintf_func (info->stream, "Address 0x%llx is out of bounds.\n", + (unsigned long long) addr); +} + +void +Disasm::disasm_open () +{ + hex_visible = 1; + snprintf (addr_fmt, sizeof (addr_fmt), NTXT ("%s"), NTXT ("%8llx: ")); + if (dis_str == NULL) + dis_str = new StringBuilder; + + switch (platform) + { + case Aarch64: + case Intel: + case Amd64: + need_swap_endian = (DbeSession::platform == Sparc); + break; + case Sparcv8plus: + case Sparcv9: + case Sparc: + default: + need_swap_endian = (DbeSession::platform != Sparc); + break; + } + + memset (&dis_info, 0, sizeof (dis_info)); + dis_info.flavour = bfd_target_unknown_flavour; + dis_info.endian = BFD_ENDIAN_UNKNOWN; + dis_info.endian_code = dis_info.endian; + dis_info.octets_per_byte = 1; + dis_info.disassembler_needs_relocs = FALSE; + dis_info.fprintf_func = fprintf_func; + dis_info.stream = this; + dis_info.disassembler_options = NULL; + dis_info.read_memory_func = read_memory_func; + dis_info.memory_error_func = memory_error_func; + dis_info.print_address_func = print_address_func; + dis_info.symbol_at_address_func = symbol_at_address_func; + dis_info.symbol_is_valid = symbol_is_valid; + dis_info.display_endian = BFD_ENDIAN_UNKNOWN; + dis_info.symtab = NULL; + dis_info.symtab_size = 0; + dis_info.buffer_vma = 0; + switch (platform) + { + case Aarch64: + dis_info.arch = bfd_arch_aarch64; + dis_info.mach = bfd_mach_aarch64; + break; + case Intel: + case Amd64: + dis_info.arch = bfd_arch_i386; + dis_info.mach = bfd_mach_x86_64; + break; + case Sparcv8plus: + case Sparcv9: + case Sparc: + default: + dis_info.arch = bfd_arch_unknown; + dis_info.endian = BFD_ENDIAN_UNKNOWN; + break; + } + dis_info.display_endian = dis_info.endian = BFD_ENDIAN_BIG; + dis_info.display_endian = dis_info.endian = BFD_ENDIAN_LITTLE; + dis_info.display_endian = dis_info.endian = BFD_ENDIAN_UNKNOWN; + disassemble_init_for_target (&dis_info); +} + +Disasm::~Disasm () +{ + delete my_stabs; + delete dwin; + delete dis_str; +} + +void +Disasm::set_img_name (char *img_fname) +{ + if (stabs == NULL && img_fname && dwin == NULL) + { + dwin = new Data_window (img_fname); + if (dwin->not_opened ()) + { + delete dwin; + dwin = NULL; + return; + } + dwin->need_swap_endian = need_swap_endian; + } +} + +void +Disasm::remove_disasm_hndl (void *hndl) +{ + DisContext *ctx = (DisContext *) hndl; + delete ctx; +} + +#if 0 +int +Disasm::get_instr_size (uint64_t vaddr, void *hndl) +{ + DisContext *ctx = (DisContext *) hndl; + if (ctx == NULL || vaddr < ctx->first_pc || vaddr >= ctx->last_pc) + return -1; + ctx->pc = vaddr; + size_t sz = ctx->is_Intel ? sizeof (ctx->codeptr) : 4; + if (sz > ctx->last_pc - vaddr) + sz = (size_t) (ctx->last_pc - vaddr); + if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc), + sz, ctx->codeptr) == NULL) + return -1; + + char buf[MAX_DISASM_STR]; + *buf = 0; + uint64_t inst_vaddr = vaddr; +#if MEZ_NEED_TO_FIX + size_t instrs_cnt = 0; + disasm_err_code_t status = disasm (handle, &inst_vaddr, ctx->last_pc, 1, + ctx, buf, sizeof (buf), &instrs_cnt); + if (instrs_cnt != 1 || status != disasm_err_ok) + return -1; +#endif + return (int) (inst_vaddr - vaddr); +} +#endif + +void +Disasm::set_addr_end (uint64_t end_address) +{ + char buf[32]; + int len = snprintf (buf, sizeof (buf), "%llx", (long long) end_address); + snprintf (addr_fmt, sizeof (addr_fmt), "%%%dllx: ", len < 8 ? 8 : len); +} + +char * +Disasm::get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, int64_t &inst_size) +{ + inst_size = 0; + if (inst_address >= end_address) + return NULL; + Data_window *dw = stabs ? stabs->openElf (false) : dwin; + if (dw == NULL) + return NULL; + + unsigned char buffer[MAX_DISASM_STR]; + dis_info.buffer = buffer; + dis_info.buffer_length = end_address - inst_address; + if (dis_info.buffer_length > sizeof (buffer)) + dis_info.buffer_length = sizeof (buffer); + dw->get_data (f_offset + (inst_address - start_address), + dis_info.buffer_length, dis_info.buffer); + + dis_str->setLength (0); + bfd abfd; + disassembler_ftype disassemble = disassembler (dis_info.arch, dis_info.endian, + dis_info.mach, &abfd); + if (disassemble == NULL) + { + printf ("ERROR: unsupported disassemble\n"); + return NULL; + } + inst_size = disassemble (0, &dis_info); + if (inst_size <= 0) + { + inst_size = 0; + return NULL; + } + StringBuilder sb; + sb.appendf (addr_fmt, inst_address); // Write address + + // Write hex bytes of instruction + if (hex_visible) + { + char bytes[64]; + *bytes = '\0'; + for (int i = 0; i < inst_size; i++) + { + unsigned int hex_value = buffer[i] & 0xff; + snprintf (bytes + 3 * i, sizeof (bytes) - 3 * i, "%02x ", hex_value); + } + const char *fmt = "%s "; + if (platform == Intel) + fmt = "%-21s "; // 21 = 3 * 7 - maximum instruction length on Intel + sb.appendf (fmt, bytes); + } + sb.append (dis_str); +#if MEZ_NEED_TO_FIX + // Write instruction + if (ctx.is_Intel) // longest instruction length for Intel is 7 + sb.appendf (NTXT ("%-7s %s"), parts_array[1], parts_array[2]); + else // longest instruction length for SPARC is 11 + sb.appendf (NTXT ("%-11s %s"), parts_array[1], parts_array[2]); + if (strcmp (parts_array[1], NTXT ("call")) == 0) + { + if (strncmp (parts_array[2], NTXT ("0x"), 2) == 0) + sb.append (GTXT ("\t! (Unable to determine target symbol)")); + } +#endif + return sb.toString (); +} + +#if MEZ_NEED_TO_FIX +void * +Disasm::get_inst_ptr (disasm_handle_t, uint64_t vaddr, void *pass_through) +{ + // Actually it fetches only one instruction at a time for sparc, + // and one byte at a time for intel. + DisContext *ctx = (DisContext*) pass_through; + size_t sz = ctx->is_Intel ? 1 : 4; + if (vaddr + sz > ctx->last_pc) + { + ctx->codeptr[0] = -1; + return ctx->codeptr; + } + if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc), sz, ctx->codeptr) == NULL) + { + ctx->codeptr[0] = -1; + return ctx->codeptr; + } + if (ctx->elf->need_swap_endian && !ctx->is_Intel) + ctx->codeptr[0] = ctx->elf->decode (ctx->codeptr[0]); + return ctx->codeptr; +} + +// get a symbol name for an address +disasm_err_code_t +Disasm::get_sym_name (disasm_handle_t, // an open disassembler handle + uint64_t target_address, // the target virtual address + uint64_t inst_address, // virtual address of instruction + // being disassembled + int use_relocation, // flag to use relocation information + char *buffer, // places the symbol here + size_t buffer_size, // limit on symbol length + int *, // sys/elf_{SPARC.386}.h + uint64_t *offset, // from the symbol to the address + void *pass_through) // disassembler context +{ + char buf[MAXPATHLEN]; + if (!use_relocation) + return disasm_err_symbol; + + DisContext *ctxp = (DisContext*) pass_through; + char *name = NULL; + if (ctxp->stabs) + { + uint64_t addr = ctxp->f_offset + (inst_address - ctxp->first_pc); + name = ctxp->stabs->sym_name (target_address, addr, use_relocation); + } + if (name == NULL) + return disasm_err_symbol; + + char *s = NULL; + if (*name == '_') + s = cplus_demangle (name, DMGL_PARAMS); + if (s) + { + snprintf (buffer, buffer_size, NTXT ("%s"), s); + free (s); + } + else + snprintf (buffer, buffer_size, NTXT ("%s"), name); + + *offset = 0; + return disasm_err_ok; +} +#endif diff --git a/gprofng/src/Disasm.h b/gprofng/src/Disasm.h new file mode 100644 index 0000000..c1530cc --- /dev/null +++ b/gprofng/src/Disasm.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DISASM_H +#define _DISASM_H + +#include "disassemble.h" + +class Data_window; +class Stabs; +class StringBuilder; +enum Platform_t; + +class Disasm +{ +public: + Disasm (char *fname); + Disasm (Platform_t _platform, Stabs *_stabs); + ~Disasm (); + void remove_disasm_hndl (void *hndl); + void *get_disasm_hndl (uint64_t vaddr, uint64_t f_offset, size_t size); + int get_instr_size (uint64_t vaddr, void *hndl); + void set_addr_end (uint64_t end_address); + + void + set_hex_visible (int set) + { + hex_visible = set; + } + + char *get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, int64_t &inst_size); + void set_img_name (char *fname); // Only for dynfunc + + StringBuilder *dis_str; + +private: + void disasm_open (); + + disassemble_info dis_info; + Data_window *dwin; + Stabs *stabs, *my_stabs; + Platform_t platform; + char addr_fmt[32]; + int hex_visible; + bool need_swap_endian; +}; + +#endif /* _DISASM_H */ diff --git a/gprofng/src/Dwarf.cc b/gprofng/src/Dwarf.cc new file mode 100644 index 0000000..eb8bd9e --- /dev/null +++ b/gprofng/src/Dwarf.cc @@ -0,0 +1,1041 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "util.h" +#include "DbeSession.h" +#include "Elf.h" +#include "Stabs.h" +#include "Dwarf.h" +#include "DataObject.h" +#include "Function.h" +#include "LoadObject.h" +#include "Module.h" +#include "DefaultMap.h" + +static int +datatypeCmp (const void *a, const void *b) +{ + uint32_t o1 = ((datatype_t *) a)->datatype_id; + uint32_t o2 = ((datatype_t *) b)->datatype_id; + return o1 == o2 ? 0 : (o1 < o2 ? -1 : 1); +} + +static int +targetOffsetCmp (const void *a, const void *b) +{ + uint32_t o1 = ((target_info_t *) a)->offset; + uint32_t o2 = ((target_info_t *) b)->offset; + return o1 == o2 ? 0 : (o1 < o2 ? -1 : 1); +} + + +////////////////////////////////////////////////////////// +// class Dwr_type +class Dwr_type +{ +public: + + Dwr_type (int64_t _cu_die_offset, int _tag) + { + cu_die_offset = _cu_die_offset; + tag = _tag; + name = NULL; + dobj_name = NULL; + dtype = NULL; + extent = 0; + parent = 0; + child = 0; + next = 0; + ref_type = 0; + size = 0; + elems = 0; + offset = -1; + bit_size = 0; + }; + + char *name, *dobj_name; + int64_t cu_die_offset, ref_type, extent, parent, child, next; + int64_t size, elems, offset; + int tag, bit_size; + + DataObject *get_dobj (Dwarf_cnt *ctx); + char *get_dobjname (Dwarf_cnt *ctx); + char *dump (); + +private: + datatype_t *dtype; + datatype_t *get_datatype (Dwarf_cnt *ctx); + void get_dobj_for_members (Dwarf_cnt *ctx); + void set_dobjname (char *spec, char *nm); +}; + + +////////////////////////////////////////////////////////// +// class Dwarf_cnt +Dwarf_cnt::Dwarf_cnt () +{ + cu_offset = 0; + parent = 0; + module = NULL; + name = NULL; + func = NULL; + fortranMAIN = NULL; + dwr_types = NULL; + inlinedSubr = NULL; + level = 0; +} + +Dwr_type * +Dwarf_cnt::get_dwr_type (int64_t cu_die_offset) +{ + Dwr_type *t = dwr_types->get (cu_die_offset); + if (t == NULL) + { + Dprintf (DUMP_DWARFLIB, "DWARF_ERROR: %s:%d wrong cu_die_offset=%lld in Dwarf_cnt::get_dwr_type\n", + get_basename (__FILE__), (int) __LINE__, + (long long) cu_die_offset); + t = put_dwr_type (cu_die_offset, 0); // DOBJ_UNSPECIFIED + } + return t; +} + +Dwr_type * +Dwarf_cnt::put_dwr_type (int64_t cu_die_offset, int tag) +{ + Dwr_type *t = new Dwr_type (cu_die_offset, tag); + dwr_types->put (cu_die_offset, t); + return t; +} + +Dwr_type * +Dwarf_cnt::put_dwr_type (Dwr_Tag *dwrTag) +{ + Dwr_type *t = new Dwr_type (dwrTag->die, dwrTag->tag); + dwr_types->put (dwrTag->die, t); + return t; +} + +////////////////////////////////////////////////////////// +// class Dwr_type +char * +Dwr_type::dump () +{ + char *s = dbe_sprintf ("%lld %-15s name='%s' parent=%lld next=%lld child=%lld dtype=%llx", + (long long) cu_die_offset, DwrCU::tag2str (tag), + STR (name), (long long) parent, (long long) next, + (long long) child, (long long) dtype); + return s; +} + +void +Dwr_type::set_dobjname (char *spec, char *nm) +{ + if (spec) + { + if (nm) + dobj_name = dbe_sprintf ("%s%s", spec, nm); + else + dobj_name = dbe_sprintf ("%s<ANON=%lld>", spec, + (long long) cu_die_offset); + } + else + { + if (nm) + dobj_name = dbe_sprintf ("%s", nm); + else + dobj_name = dbe_sprintf ("<ANON=%lld>", (long long) cu_die_offset); + } +} + +char * +Dwr_type::get_dobjname (Dwarf_cnt *ctx) +{ + if (dobj_name) + return dobj_name; + switch (tag) + { + case DW_TAG_base_type: + set_dobjname (NULL, name); + for (int i = 0, len = (int) strlen (dobj_name); i < len; i++) + { + if (dobj_name[i] == ' ') + dobj_name[i] = '_'; + } + break; + case DW_TAG_constant: + case DW_TAG_formal_parameter: + case DW_TAG_variable: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + set_dobjname (NULL, t->get_dobjname (ctx)); + break; + } + case DW_TAG_unspecified_type: + set_dobjname (NTXT ("unspecified:"), name); + break; + case DW_TAG_enumeration_type: + set_dobjname (NTXT ("enumeration:"), name); + break; + case DW_TAG_typedef: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + dobj_name = dbe_sprintf ("%s=%s", name, t->get_dobjname (ctx)); + break; + } + case DW_TAG_const_type: + set_dobjname (NTXT ("const+"), name); + break; + case DW_TAG_volatile_type: + set_dobjname (NTXT ("volatile+"), name); + break; + case DW_TAG_pointer_type: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + set_dobjname (NTXT ("pointer+"), t->get_dobjname (ctx)); + break; + } + case DW_TAG_reference_type: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + set_dobjname (NTXT ("reference+"), t->get_dobjname (ctx)); + break; + } + case DW_TAG_array_type: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + if (elems > 0) + dobj_name = dbe_sprintf ("array[%lld]:%s", + (long long) elems, t->get_dobjname (ctx)); + else + dobj_name = dbe_sprintf ("array[]:%s", t->get_dobjname (ctx)); + break; + } + case DW_TAG_structure_type: + set_dobjname (NTXT ("structure:"), name); + break; + case DW_TAG_union_type: + set_dobjname (NTXT ("union:"), name); + break; + case DW_TAG_class_type: + set_dobjname (NTXT ("class:"), name); + break; + case DW_TAG_member: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + if (bit_size > 0) + dobj_name = dbe_sprintf (NTXT ("%s:%lld"), t->get_dobjname (ctx), + (long long) bit_size); + else + dobj_name = dbe_sprintf (NTXT ("%s"), t->get_dobjname (ctx)); + break; + } + default: + Dprintf (DUMP_DWARFLIB, NTXT ("DWARF_ERROR: %s:%d No case for %s cu_die_offset=%lld\n"), + get_basename (__FILE__), (int) __LINE__, + DwrCU::tag2str (tag), (long long) cu_die_offset); + set_dobjname (NTXT ("Undefined:"), NULL); + break; + } + return dobj_name; +} + +datatype_t* +Dwr_type::get_datatype (Dwarf_cnt *ctx) +{ + if (dtype) + return dtype; + dtype = new datatype_t; + dtype->datatype_id = (unsigned) cu_die_offset; + dtype->memop_refs = 0; + dtype->event_data = 0; + dtype->dobj = NULL; + ctx->module->datatypes->incorporate (dtype, datatypeCmp); + return dtype; +} + +DataObject * +Dwr_type::get_dobj (Dwarf_cnt *ctx) +{ + if (dtype == NULL) + dtype = get_datatype (ctx); + dtype->memop_refs++; + DataObject *dobj = dtype->dobj; + if (dobj) + return dobj; + + if (tag == 0) + dobj = dbeSession->find_dobj_by_name (PTXT (DOBJ_UNSPECIFIED)); + else + { + dobj = dbeSession->createDataObject (); + dobj->size = size; + dobj->offset = offset; + dobj->scope = ctx->func ? (Histable*) ctx->func : (Histable*) ctx->module; + } + dtype->dobj = dobj; + if (parent) + { + Dwr_type *t = ctx->get_dwr_type (parent); + dobj->parent = t->get_dobj (ctx); + } + + if (ref_type) + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + t->get_dobj (ctx); + if (size == 0) + { + size = t->size; + dobj->size = size; + } + } + + switch (tag) + { + case 0: + break; + case DW_TAG_array_type: + case DW_TAG_base_type: + case DW_TAG_unspecified_type: + case DW_TAG_enumeration_type: + case DW_TAG_typedef: + case DW_TAG_const_type: + case DW_TAG_volatile_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + dobj->set_dobjname (get_dobjname (ctx), NULL); + break; + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + dobj->set_dobjname (get_dobjname (ctx), NULL); + dobj->master = dbeSession->find_dobj_by_name (dobj_name); + get_dobj_for_members (ctx); + break; + case DW_TAG_constant: + case DW_TAG_formal_parameter: + case DW_TAG_member: + case DW_TAG_variable: + if (dobj->parent == NULL) + dobj->parent = dbeSession->get_Scalars_DataObject (); + dobj->set_dobjname (get_dobjname (ctx), name); + break; + default: + Dprintf (DUMP_DWARFLIB, NTXT ("DWARF_ERROR: %s:%d No case for %s cu_die_offset=%lld\n"), + get_basename (__FILE__), (int) __LINE__, + DwrCU::tag2str (tag), (long long) cu_die_offset); + break; + } + return dobj; +} + +void +Dwr_type::get_dobj_for_members (Dwarf_cnt *ctx) +{ + for (int64_t i = child; i != 0;) + { + Dwr_type *t = ctx->get_dwr_type (i); + t->get_dobj (ctx); + i = t->next; + } +} + +////////////////////////////////////////////////////////// +// class Dwarf +Dwarf::Dwarf (Stabs *_stabs) +{ + stabs = _stabs; + status = Stabs::DBGD_ERR_NONE; + dwrCUs = 0; + debug_infoSec = NULL; + debug_abbrevSec = NULL; + debug_strSec = NULL; + debug_lineSec = NULL; + debug_rangesSec = NULL; + elf = stabs->openElf (true); + if (elf == NULL) + { + status = Stabs::DBGD_ERR_BAD_ELF_FORMAT; + return; + } + debug_infoSec = dwrGetSec (NTXT (".debug_info")); + if (debug_infoSec) + { + debug_infoSec->reloc = ElfReloc::get_elf_reloc (elf, NTXT (".rela.debug_info"), NULL); + debug_infoSec->reloc = ElfReloc::get_elf_reloc (elf, NTXT (".rel.debug_info"), debug_infoSec->reloc); + if (debug_infoSec->reloc) + debug_infoSec->reloc->dump (); + } + debug_abbrevSec = dwrGetSec (NTXT (".debug_abbrev")); + debug_strSec = dwrGetSec (NTXT (".debug_str")); + debug_lineSec = dwrGetSec (NTXT (".debug_line")); + debug_rangesSec = dwrGetSec (NTXT (".debug_ranges")); + + if ((debug_infoSec == NULL) || (debug_abbrevSec == NULL) || (debug_lineSec == NULL)) + { + status = Stabs::DBGD_ERR_NO_DWARF; + return; + } +} + +Dwarf::~Dwarf () +{ + delete debug_infoSec; + delete debug_abbrevSec; + delete debug_strSec; + delete debug_lineSec; + delete debug_rangesSec; + Destroy (dwrCUs); +} + +DwrSec * +Dwarf::dwrGetSec (const char *sec_name) +{ + int secN = elf->elf_get_sec_num (sec_name); + if (secN > 0) + { + Elf_Data *elfData = elf->elf_getdata (secN); + if (elfData) + return new DwrSec ((unsigned char *) elfData->d_buf, elfData->d_size, + elf->need_swap_endian, + elf->elf_getclass () == ELFCLASS32); + } + return NULL; +} + +uint64_t +DwrCU::get_low_pc () +{ + uint64_t pc = Dwarf_addr (DW_AT_low_pc); + if (pc) + return pc; + return pc; +} + +char * +DwrCU::get_linkage_name () +{ + char *nm = Dwarf_string (DW_AT_linkage_name); + if (nm != NULL) + return nm; + nm = Dwarf_string (DW_AT_SUN_link_name); + if (nm != NULL) + return nm; + return Dwarf_string (DW_AT_MIPS_linkage_name); +} + +void +DwrCU::parseChild (Dwarf_cnt *ctx) +{ + if (!dwrTag.hasChild) + return; + uint64_t old_size = debug_infoSec->size; + uint64_t next_die_offset = 0; + Dwarf_Die next_die; + if (read_ref_attr (DW_AT_sibling, &next_die) == DW_DLV_OK) + { + next_die_offset = next_die + cu_offset; + if (next_die_offset <= debug_infoSec->offset) + { + Dprintf (DEBUG_ERR_MSG, NTXT ("DwrCU::parseChild: next_die(0x%llx) <= debug_infoSec->offset(%llx)\n"), + (long long) next_die, (long long) debug_infoSec->offset); + next_die_offset = 0; + } + else if (debug_infoSec->size > next_die_offset) + debug_infoSec->size = next_die_offset; + } + dwrTag.level++; + ctx->level++; + for (;;) + { + if (set_die (0) != DW_DLV_OK) + break; + Function *func; + char *old_name; + int hasChild = dwrTag.hasChild; + switch (dwrTag.tag) + { + case DW_TAG_imported_declaration: + if (Stabs::is_fortran (ctx->module->lang_code)) + { + char *link_name = Dwarf_string (DW_AT_name); + ctx->fortranMAIN = NULL; + parseChild (ctx); + hasChild = 0; + if (ctx->fortranMAIN) + { + ctx->fortranMAIN->set_match_name (link_name); + ctx->fortranMAIN = NULL; + } + } + break; + case DW_TAG_subprogram: + if (dwrTag.get_attr (DW_AT_abstract_origin)) + break; + if (dwrTag.get_attr (DW_AT_declaration)) + { + // Only declaration + if (Stabs::is_fortran (ctx->module->lang_code)) + { + char *link_name = Dwarf_string (DW_AT_name); + if (link_name && streq (link_name, NTXT ("MAIN"))) + ctx->fortranMAIN = Stabs::find_func (NTXT ("MAIN"), ctx->module->functions, true, true); + } + if (get_linkage_name () == NULL) + break; + } + func = append_Function (ctx); + if (func) + { + if (Stabs::is_fortran (ctx->module->lang_code) && + streq (func->get_match_name (), NTXT ("MAIN"))) + ctx->fortranMAIN = func; + old_name = ctx->name; + Function *old_func = ctx->func; + ctx->name = func->get_match_name (); + ctx->func = func; + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + ctx->func = old_func; + } + break; + case DW_TAG_module: + old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_SUN_link_name); + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_class_type: + old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_name); + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_structure_type: + old_name = ctx->name; + ctx->name = NULL; + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_namespace: + old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_name); + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_lexical_block: + old_name = ctx->name; + ctx->name = NULL; + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_SUN_memop_info: + isMemop = true; + break; + case DW_TAG_inlined_subroutine: + if (ctx->module) + { + parse_inlined_subroutine (ctx); + hasChild = 0; + } + break; + default: // No more special cases + break; + } + if (hasChild) + parseChild (ctx); + } + ctx->level--; + dwrTag.level--; + if (next_die_offset != 0) + debug_infoSec->offset = next_die_offset; + debug_infoSec->size = old_size; +} + +bool +Dwarf::archive_Dwarf (LoadObject *lo) +{ + if (debug_infoSec == NULL) + return false; + if (dwrCUs) + return true; + dwrCUs = new Vector<DwrCU *>; + + debug_infoSec->offset = 0; + while (debug_infoSec->offset < debug_infoSec->sizeSec) + { + DwrCU *dwrCU = new DwrCU (this); + dwrCUs->append (dwrCU); + debug_infoSec->size = debug_infoSec->sizeSec; + debug_infoSec->offset = dwrCU->next_cu_offset; + + if (dwrCU->set_die (dwrCU->cu_header_offset) != DW_DLV_OK) + { + Dprintf (1, "DwrCU::archive_Dwarf: CU=%lld (offset=0x%llx); set_die(0x%llx) failed\n", + (long long) dwrCUs->size (), (long long) dwrCU->cu_offset, + (long long) dwrCU->cu_header_offset); + continue; + } + + Module *mod = dwrCU->parse_cu_header (lo); + if (mod) + { + mod->hdrOffset = dwrCUs->size (); + DwrLineRegs *lineReg = dwrCU->get_dwrLineReg (); + dwrCU->srcFiles = new Vector<SourceFile *> (VecSize (lineReg->file_names)); + for (long i = 0, sz = VecSize (lineReg->file_names); i < sz; i++) + { + char *fname = lineReg->getPath (i + 1); + SourceFile *sf = mod->findSource (fname, true); + dwrCU->srcFiles->append (sf); + } + + Dwarf_cnt ctx; + ctx.module = mod; + dwrCU->parseChild (&ctx); + if (dwrCU->dwrInlinedSubrs && DUMP_DWARFLIB) + { + char msg[128]; + char *lo_name = mod->loadobject ? mod->loadobject->get_name () + : NULL; + snprintf (msg, sizeof (msg), NTXT ("\ndwrCUs[%lld]: %s:%s\n"), + (long long) dwrCUs->size (), + STR (lo_name), STR (mod->get_name ())); + dwrCU->dwrInlinedSubrs->dump (msg); + } + } + } + return true; +} + +void +Dwarf::srcline_Dwarf (Module *module) +{ + if (module == NULL || module->hdrOffset == 0) + return; + DwrCU *dwrCU = dwrCUs->get (module->hdrOffset - 1); + dwrCU->map_dwarf_lines (module); +} + + +// parse hwcprof info for given module in loadobject + +void +Dwarf::read_hwcprof_info (Module *module) +{ + if (module->datatypes || (module->hdrOffset == 0)) + return; + DwrCU *dwrCU = dwrCUs->get (module->hdrOffset - 1); + if (!dwrCU->isMemop) + return; + module->datatypes = new Vector<datatype_t*>; + if (dwrCU->set_die (dwrCU->cu_header_offset) != DW_DLV_OK) + { + Dprintf (1, "Dwarf::read_hwcprof_info: CU=%lld (offset=0x%llx); set_die(0x%llx) failed\n", + (long long) module->hdrOffset, (long long) dwrCU->cu_offset, + (long long) dwrCU->cu_header_offset); + return; + } + Dwarf_cnt ctx; + ctx.module = module; + ctx.cu_offset = dwrCU->cu_offset; // CU header offset; + ctx.dwr_types = new DefaultMap<int64_t, Dwr_type*>; + ctx.put_dwr_type (0, 0); // for DOBJ_UNSPECIFIED + dwrCU->read_hwcprof_info (&ctx); + + Vector<inst_info_t*> *infoList = module->infoList; + Dprintf (DUMP_DWARFLIB, + "\n\n ### Dwarf::read_hwcprof_info: module: '%s' infoList->size()=%lld\n", + STR (module->get_name ()), + (long long) (infoList ? infoList->size () : -1)); + for (int i = 0, sz = infoList ? infoList->size () : -1; i < sz; i++) + { + inst_info_t *ip = infoList->fetch (i); + memop_info_t *mp = ip->memop; + Dwr_type *t = ctx.get_dwr_type (mp->datatype_id); + t->get_dobj (&ctx); + } + +#ifdef DEBUG + Dprintf (DUMP_DWARFLIB, + "\n\n ### Dwarf::read_hwcprof_info: '%s' infoList->size()=%lld\n", + STR (module->get_name ()), + (long long) (infoList ? infoList->size () : 1)); + for (int i = 0, sz = infoList ? infoList->size () : -1; i < sz; i++) + { + inst_info_t *ip = infoList->fetch (i); + memop_info_t *mp = ip->memop; + Dprintf (DUMP_DWARFLIB, + " %d id=%lld offset=%lld signature=%lld datatype_id=%lld \n", + i, (long long) mp->id, (long long) mp->offset, + (long long) mp->signature, (long long) mp->datatype_id); + } + + Vector<int64_t> *keys = ctx.dwr_types->keySet (); + Dprintf (DUMP_DWARFLIB, + "\n\n ### Dwarf::read_hwcprof_info: '%s' keys->size()=%lld\n", + STR (module->get_name ()), (long long) (keys ? keys->size () : -1)); + for (int i = 0, sz = keys->size (); i < sz; i++) + { + int64_t ind = keys->fetch (i); + Dwr_type *t = ctx.get_dwr_type (ind); + Dprintf (DUMP_DWARFLIB, NTXT (" %d %lld %s\n"), i, + (long long) ind, t->dump ()); + } +#endif +} + +void +DwrCU::read_hwcprof_info (Dwarf_cnt *ctx) +{ + if (!dwrTag.hasChild) + return; + uint64_t old_size = debug_infoSec->size; + uint64_t next_die_offset = 0; + Dwarf_Die next_die; + if (read_ref_attr (DW_AT_sibling, &next_die) == DW_DLV_OK) + { + next_die_offset = next_die + cu_offset; + if (next_die_offset <= debug_infoSec->offset) + next_die_offset = 0; + else if (debug_infoSec->size > next_die_offset) + debug_infoSec->size = next_die_offset; + } + dwrTag.level++; + ctx->level++; + for (;;) + { + if (set_die (0) != DW_DLV_OK) + break; + Dprintf (DUMP_DWARFLIB, NTXT ("%s:%d <%lld:%lld> cu_die=%lld %-15s\n"), + get_basename (__FILE__), (int) __LINE__, (long long) ctx->level, + (long long) dwrTag.die, (long long) dwrTag.offset, + DwrCU::tag2str (dwrTag.tag)); + switch (dwrTag.tag) + { + case DW_TAG_SUN_memop_info: + { + if (ctx->func == NULL) + break; + Dwarf_Unsigned mid = Dwarf_data (DW_AT_SUN_profile_id); + Dwarf_Unsigned off = Dwarf_data (DW_AT_SUN_func_offset); + Dwarf_Unsigned sig = Dwarf_data (DW_AT_SUN_memop_signature); + Dwarf_Off ref = Dwarf_ref (DW_AT_SUN_memop_type_ref); + + // define memop entry + memop_info_t *memop = new memop_info_t; + memop->id = (unsigned) mid; + memop->signature = (unsigned) sig; + memop->datatype_id = ref ? (unsigned) ref : 0; + memop->offset = (unsigned) (ctx->func->img_offset + off); + + // define instop entry + inst_info_t *instop = new inst_info_t; + instop->type = CPF_INSTR_TYPE_PREFETCH; // XXXX UNKNOWN + instop->offset = memop->offset; + instop->memop = memop; + if (ctx->module->infoList == NULL) + ctx->module->infoList = new Vector<inst_info_t*>; + ctx->module->infoList->append (instop); + break; + } + case DW_TAG_SUN_codeflags: + { + if (ctx->func == NULL) + break; + Dwarf_Unsigned kind = Dwarf_data (DW_AT_SUN_cf_kind); + if (kind == DW_ATCF_SUN_branch_target) + { + DwrSec *secp = Dwarf_block (DW_AT_SUN_func_offsets); + if (secp) + { + int foffset = 0; + for (int i = 0; secp->offset < secp->size; i++) + { + int val = (int) secp->GetSLEB128 (); + if (i == 0 || val != 0) + { + foffset += val; + target_info_t *t = new target_info_t; + t->offset = (unsigned) (ctx->func->img_offset + foffset); + ctx->module->bTargets.incorporate (t, targetOffsetCmp); + } + } + delete(secp); + } + } + break; + } + case DW_TAG_subprogram: + { + Function *old_func = ctx->func; + if (dwrTag.get_attr (DW_AT_abstract_origin) + || dwrTag.get_attr (DW_AT_declaration)) + ctx->func = NULL; + else + ctx->func = append_Function (ctx); + read_hwcprof_info (ctx); + ctx->func = old_func; + break; + } + case DW_TAG_base_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->size = Dwarf_data (DW_AT_byte_size); + break; + } + case DW_TAG_unspecified_type: + ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + break; + case DW_TAG_enumeration_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->size = Dwarf_data (DW_AT_byte_size); + break; + } + case DW_TAG_constant: + case DW_TAG_formal_parameter: + case DW_TAG_variable: + case DW_TAG_typedef: + case DW_TAG_const_type: + case DW_TAG_volatile_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->ref_type = Dwarf_ref (DW_AT_type); + break; + } + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->ref_type = Dwarf_ref (DW_AT_type); + t->size = (dwarf->stabs->get_class () == W64) ? 8 : 4; + break; + } + case DW_TAG_array_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->ref_type = Dwarf_ref (DW_AT_type); + t->size = Dwarf_data (DW_AT_byte_size); + ctx->size = -1; + read_hwcprof_info (ctx); + t->elems = ctx->size; + break; + } + case DW_TAG_subrange_type: + { + int64_t ref_type = Dwarf_ref (DW_AT_type); + int64_t hi = Dwarf_data (DW_AT_upper_bound); + int64_t lo = Dwarf_data (DW_AT_lower_bound); + int64_t ss = Dwarf_data (DW_AT_stride_size); + ctx->size = 1 + hi - lo; + if (ss != 0) + ctx->size /= ss; + Dprintf (DUMP_DWARFLIB, + "Got subrange [%lld:%lld:%lld] indexed <%lld>: size=%lld\n", + (long long) lo, (long long) hi, (long long) ss, + (long long) ref_type, (long long) ctx->size); + break; + } + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->size = Dwarf_data (DW_AT_byte_size); + t->extent = Dwarf_ref (DW_AT_sibling); + int64_t old_parent = ctx->parent; + ctx->parent = t->cu_die_offset; + + char *old_name = ctx->name; + ctx->name = (dwrTag.tag == DW_TAG_class_type) ? Dwarf_string (DW_AT_name) : NULL; + read_hwcprof_info (ctx); + ctx->name = old_name; + ctx->parent = old_parent; + // Reverse children + for (int64_t i = t->child, last = 0; i != 0;) + { + Dwr_type *t1 = ctx->get_dwr_type (i); + t->child = i; + i = t1->next; + t1->next = last; + last = t->child; + } + break; + } + case DW_TAG_member: + { + if (ctx->parent == 0) + { + Dprintf (DUMP_DWARFLIB, NTXT ("DWARF_ERROR: %s:%d %s cu_die_offset=%lld\n"), + get_basename (__FILE__), (int) __LINE__, + DwrCU::tag2str (dwrTag.tag), (long long) dwrTag.die); + break; + } + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->ref_type = Dwarf_ref (DW_AT_type); + t->offset = Dwarf_location (DW_AT_data_member_location); + Dwr_type *parent = ctx->get_dwr_type (ctx->parent); + t->parent = ctx->parent; + t->next = parent->child; // a reverse order of members + parent->child = t->cu_die_offset; + t->bit_size = (uint32_t) Dwarf_data (DW_AT_bit_size); + break; + } + case DW_TAG_module: + { + char *old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_SUN_link_name); + read_hwcprof_info (ctx); + ctx->name = old_name; + break; + } + case DW_TAG_namespace: + { + char *old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_name); + read_hwcprof_info (ctx); + ctx->name = old_name; + break; + } + case DW_TAG_lexical_block: + { + char *old_name = ctx->name; + ctx->name = NULL; + read_hwcprof_info (ctx); + ctx->name = old_name; + break; + } + default: // No more special cases + read_hwcprof_info (ctx); + break; + } + } + ctx->level--; + dwrTag.level--; + if (next_die_offset != 0) + debug_infoSec->offset = next_die_offset; + debug_infoSec->size = old_size; +} + +// Append function to module +Function * +DwrCU::append_Function (Dwarf_cnt *ctx) +{ + char *outerName = ctx->name, *name, tmpname[2048]; + Function *func; + char *fname = Dwarf_string (DW_AT_name); + if (fname && outerName && !strchr (fname, '.')) + { + size_t outerlen = strlen (outerName); + if (outerlen > 0 && outerName[outerlen - 1] == '_') + { + outerlen--; + snprintf (tmpname, sizeof (tmpname), NTXT ("%s"), outerName); + snprintf (tmpname + outerlen, sizeof (tmpname) - outerlen, NTXT (".%s_"), fname); + } + else + snprintf (tmpname, sizeof (tmpname), NTXT ("%s.%s"), outerName, fname); + name = tmpname; + Dprintf (DUMP_DWARFLIB, NTXT ("Generated innerfunc name %s\n"), name); + } + else + name = fname; + + char *link_name = get_linkage_name (); + if (link_name == NULL) + link_name = name; + + uint64_t pc = get_low_pc (); + func = dwarf->stabs->append_Function (module, link_name, pc); + if (func != NULL) + { + int lineno = (int) Dwarf_data (DW_AT_decl_line); + func->set_match_name (name); + if (lineno > 0) + { + func->setLineFirst (lineno); + if (dwrLineReg == NULL) + dwrLineReg = new DwrLineRegs (new DwrSec (dwarf->debug_lineSec, + stmt_list_offset), comp_dir); + int fileno = ((int) Dwarf_data (DW_AT_decl_file)) - 1; + SourceFile *sf = ((fileno >= 0) && (fileno < VecSize (srcFiles))) ? srcFiles->get (fileno) + : module->getMainSrc (); + func->setDefSrc (sf); + func->pushSrcFile (func->def_source, 0); + func->popSrcFile (); + } + } + return func; +} + +// Get language code +Sp_lang_code +DwrCU::Dwarf_lang () +{ + char *str = Dwarf_string (DW_AT_producer); + if (str && strncmp (str, NTXT ("GNU"), 3) == 0) + isGNU = true; + int64_t lang = Dwarf_data (DW_AT_language); + switch (lang) + { + case DW_LANG_C89: + case DW_LANG_C: + return Sp_lang_c; // Sp_lang_ansic? + case DW_LANG_C99: + return Sp_lang_c99; + case DW_LANG_C_plus_plus: + return isGNU ? Sp_lang_gcc : Sp_lang_cplusplus; + case DW_LANG_Fortran90: + return Sp_lang_fortran90; + case DW_LANG_Fortran77: + return Sp_lang_fortran; + case DW_LANG_Java: + return Sp_lang_java; + case DW_LANG_Mips_Assembler: + case DW_LANG_SUN_Assembler: + return Sp_lang_asm; + case DW_LANG_Pascal83: + return Sp_lang_pascal; + default: + case DW_LANG_Ada83: + case DW_LANG_Cobol74: + case DW_LANG_Cobol85: + case DW_LANG_Modula2: + case DW_LANG_Ada95: + case DW_LANG_Fortran95: + case DW_LANG_lo_user: + return Sp_lang_unknown; + } +} diff --git a/gprofng/src/Dwarf.h b/gprofng/src/Dwarf.h new file mode 100644 index 0000000..12ffbc0 --- /dev/null +++ b/gprofng/src/Dwarf.h @@ -0,0 +1,87 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _Dwarf_h_ +#define _Dwarf_h_ 1 + +#include "dwarf2.h" + +#include "Stabs.h" +#include "dbe_structs.h" +#include "DwarfLib.h" + +enum +{ + /* ICC extensions */ + DW_AT_icc_flags = 0x3b01, + DW_TAG_icc_compile_unit = 0x7000, + + /* Sun extensions */ + DW_ATCF_SUN_branch_target = 0x46, + DW_AT_SUN_command_line = 0x2205, + DW_AT_SUN_func_offsets = 0x2211, + DW_AT_SUN_cf_kind = 0x2212, + DW_AT_SUN_func_offset = 0x2216, + DW_AT_SUN_memop_type_ref = 0x2217, + DW_AT_SUN_profile_id = 0x2218, + DW_AT_SUN_memop_signature = 0x2219, + DW_AT_SUN_obj_dir = 0x2220, + DW_AT_SUN_obj_file = 0x2221, + DW_AT_SUN_original_name = 0x2222, + DW_AT_SUN_link_name = 0x2226, + + DW_TAG_SUN_codeflags = 0x4206, + DW_TAG_SUN_memop_info = 0x4207, + DW_TAG_SUN_dtor_info = 0x420a, + DW_TAG_SUN_dtor = 0x420b, + + DW_LANG_SUN_Assembler = 0x9001 +}; + + +class LoadObject; +class Module; +class DwrCU; +class DwrSec; + +class Dwarf +{ +public: + Dwarf (Stabs *_stabs); + ~Dwarf (); + bool archive_Dwarf (LoadObject *lo); + void srcline_Dwarf (Module *module); + void read_hwcprof_info (Module *module); + + Stabs::Stab_status status; + Vector<DwrCU *> *dwrCUs; + DwrSec *debug_infoSec; + DwrSec *debug_abbrevSec; + DwrSec *debug_strSec; + DwrSec *debug_lineSec; + DwrSec *debug_rangesSec; + Elf *elf; + Stabs *stabs; + +private: + DwrSec *dwrGetSec (const char *sec_name); +}; + +#endif /* _Dwarf_h_ */ diff --git a/gprofng/src/DwarfLib.cc b/gprofng/src/DwarfLib.cc new file mode 100644 index 0000000..c8af0ab --- /dev/null +++ b/gprofng/src/DwarfLib.cc @@ -0,0 +1,2203 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> + +#include "util.h" +#include "Dwarf.h" +#include "DwarfLib.h" +#include "Elf.h" +#include "Function.h" +#include "Module.h" +#include "StringBuilder.h" +#include "DbeArray.h" +#include "DbeSession.h" + +#define CASE_S(x) case x: s = (char *) #x; break + +static char * +gelf_st_type2str (int type) +{ + static char buf[128]; + char *s; + switch (type) + { + CASE_S (STT_NOTYPE); + CASE_S (STT_OBJECT); + CASE_S (STT_FUNC); + CASE_S (STT_SECTION); + CASE_S (STT_FILE); + CASE_S (STT_COMMON); + CASE_S (STT_TLS); + // CASE_S(STT_NUM); + CASE_S (STT_LOPROC); + CASE_S (STT_HIPROC); + default: s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, type); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +static char * +special_opcode2str (int opcode) +{ + static char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("SpecialOpcode: %3d"), opcode); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +static char * +extended_opcode2str (int opcode) +{ + static char buf[128]; + char *s; + switch (opcode) + { + CASE_S (DW_LNE_end_sequence); + CASE_S (DW_LNE_set_address); + CASE_S (DW_LNE_define_file); + default: + snprintf (buf, sizeof (buf), NTXT ("??? (%d)"), opcode); + buf[sizeof (buf) - 1] = 0; + s = buf; + break; + } + return s; +} + +static char * +standard_opcode2str (int opcode) +{ + static char buf[128]; + char *s; + switch (opcode) + { + CASE_S (DW_LNS_copy); + CASE_S (DW_LNS_advance_pc); + CASE_S (DW_LNS_advance_line); + CASE_S (DW_LNS_set_file); + CASE_S (DW_LNS_set_column); + CASE_S (DW_LNS_negate_stmt); + CASE_S (DW_LNS_set_basic_block); + CASE_S (DW_LNS_const_add_pc); + CASE_S (DW_LNS_fixed_advance_pc); + default: + snprintf (buf, sizeof (buf), NTXT ("??? (%d)"), opcode); + buf[sizeof (buf) - 1] = 0; + s = buf; + break; + } + return s; +} + +template<> void Vector<DwrInlinedSubr *> +::dump (const char *msg) +{ + Dprintf (1, NTXT ("%s Vector<DwrInlinedSubr *> [%lld]\n"), + msg ? msg : NTXT (""), (long long) size ()); + for (long i = 0, sz = size (); i < sz; i++) + { + DwrInlinedSubr *p = get (i); + Dprintf (1, NTXT ("%ld: "), (long) i); + p->dump (); + } +} + +template<> void Vector<DwrLine *> +::dump (const char *msg) +{ + Dprintf (1, "%s Vector<DwrLine *> [%lld]:\n address [file line column]\n", + msg ? msg : NTXT (""), (long long) size ()); + for (long i = 0, sz = size (); i < sz; i++) + { + DwrLine *lnp = get (i); + Dprintf (1, NTXT (" %2lld 0x%08llx [ %2lld, %lld, %lld ] \n"), + (long long) i, (long long) lnp->address, (long long) lnp->file, + (long long) lnp->line, (long long) lnp->column); + } + Dprintf (1, NTXT ("\n\n")); +} + +////////////////////////////////////////////////////////// +// class ElfReloc + +ElfReloc::ElfReloc (Elf *_elf) +{ + elf = _elf; + reloc = NULL; + cur_reloc_ind = 0; +} + +ElfReloc::~ElfReloc () +{ + if (reloc) + { + reloc->destroy (); + delete reloc; + } +} + +void +ElfReloc::dump_rela_debug_sec (int sec) +{ + if (!DUMP_RELA_SEC) + return; + Elf_Internal_Shdr *shdr = elf->get_shdr (sec); + if (shdr == NULL) + return; + + Elf_Data *data = elf->elf_getdata (sec); + if (data == NULL) + return; + + uint64_t ScnSize = data->d_size; + uint64_t EntSize = shdr->sh_entsize; + if (ScnSize == 0 || EntSize == 0) + return; + + Elf_Internal_Shdr *shdr_sym = elf->get_shdr (shdr->sh_link); + if (shdr_sym == NULL) + return; + Elf_Data *data_sym = elf->elf_getdata (shdr->sh_link); + Elf_Data *data_str = elf->elf_getdata (shdr_sym->sh_link); + char *Strtab = data_str ? (char*) data_str->d_buf : NULL; + Elf_Internal_Rela rela; + int n, cnt = (int) (ScnSize / EntSize); + + char *sec_name = elf->get_sec_name (sec); + if (sec_name == NULL) // It can not be, but let's check + return; + Dprintf (DUMP_RELA_SEC, + "======= DwarfLib::dump_rela_debug_sec Section:%2d '%s'\n", + sec, sec_name); + Dprintf (DUMP_RELA_SEC, + " N |addend| offset | r_info | stt_type |\n"); + for (n = 0; n < cnt; n++) + { + if (strncmp (sec_name, NTXT (".rela."), 6) == 0) + elf->elf_getrela (data, n, &rela); + else + { + elf->elf_getrel (data, n, &rela); + rela.r_addend = 0; + } + int ndx = (int) GELF_R_SYM (rela.r_info); + Elf_Internal_Shdr *secHdr; + Elf_Internal_Sym sym; + elf->elf_getsym (data_sym, ndx, &sym); + Dprintf (DUMP_RELA_SEC, NTXT ("%3d:%5d |%11lld |0x%016llx | %-15s|"), + n, (int) rela.r_addend, + (long long) rela.r_offset, (long long) rela.r_info, + gelf_st_type2str ((int) GELF_ST_TYPE (sym.st_info))); + switch (GELF_ST_TYPE (sym.st_info)) + { + case STT_FUNC: + case STT_OBJECT: + case STT_NOTYPE: + secHdr = elf->get_shdr (sym.st_shndx); + if (secHdr) + Dprintf (DUMP_RELA_SEC, NTXT (" img_offset=0x%llx"), + (long long) (sym.st_value + secHdr->sh_offset)); + if (Strtab && sym.st_name) + Dprintf (DUMP_RELA_SEC, NTXT (" %s"), Strtab + sym.st_name); + break; + case STT_SECTION: + secHdr = elf->get_shdr (sym.st_shndx); + if (secHdr) + { + Dprintf (DUMP_RELA_SEC, NTXT (" value=0x%016llx (%lld)"), + (long long) (secHdr->sh_offset + rela.r_addend), + (long long) (secHdr->sh_offset + rela.r_addend)); + } + break; + default: + break; + } + Dprintf (DUMP_RELA_SEC, NTXT ("\n")); + } + Dprintf (DUMP_RELA_SEC, NTXT ("\n")); +} + +void +ElfReloc::dump () +{ + if (!DUMP_ELF_RELOC || (reloc == NULL) || (reloc->size () == 0)) + return; + Dprintf (DUMP_ELF_RELOC, NTXT ("======= ElfReloc::dump\n")); + Dprintf (DUMP_ELF_RELOC, NTXT (" N | offset | value | STT_TYPE\n")); + for (int i = 0; i < reloc->size (); i++) + { + Sreloc *srlc = reloc->fetch (i); + Dprintf (DUMP_ELF_RELOC, NTXT ("%3d:%11lld |%11lld | %s\n"), + i, (long long) srlc->offset, (long long) srlc->value, + gelf_st_type2str (srlc->stt_type)); + } + Dprintf (DUMP_ELF_RELOC, NTXT ("\n")); +} + +static int +DwrRelocOffsetCmp (const void *a, const void *b) +{ + ElfReloc::Sreloc *item1 = *((ElfReloc::Sreloc **) a); + ElfReloc::Sreloc *item2 = *((ElfReloc::Sreloc **) b); + return item1->offset < item2->offset ? -1 : + item1->offset == item2->offset ? 0 : 1; +} + +ElfReloc * +ElfReloc::get_elf_reloc (Elf *elfp, char *sec_name, ElfReloc *rlc) +{ + int et = elfp->elf_getehdr ()->e_type; + if (et == ET_EXEC || et == ET_DYN) + return rlc; + int sec = elfp->elf_get_sec_num (sec_name); + if (sec == 0) + return rlc; + Elf_Internal_Shdr *shdr = elfp->get_shdr (sec); + if (shdr == NULL || shdr->sh_entsize == 0) + return rlc; + + Elf_Data *data = elfp->elf_getdata (sec); + if (data == NULL || data->d_size == 0) + return rlc; + + int cnt = (int) (data->d_size / shdr->sh_entsize); + Elf_Internal_Shdr *shdr_sym = elfp->get_shdr (shdr->sh_link); + if (shdr_sym == NULL) + return rlc; + Elf_Data *data_sym = elfp->elf_getdata (shdr->sh_link); + Vector<Sreloc *> *vp = NULL; + + for (int n = 0; n < cnt; n++) + { + Elf_Internal_Shdr *secHdr; + Sreloc *srlc; + Elf_Internal_Rela rela; + if (strncmp (sec_name, NTXT (".rela."), 6) == 0) + elfp->elf_getrela (data, n, &rela); + else + { + elfp->elf_getrel (data, n, &rela); + rela.r_addend = 0; + } + int ndx = (int) GELF_R_SYM (rela.r_info); + Elf_Internal_Sym sym; + elfp->elf_getsym (data_sym, ndx, &sym); + + srlc = new Sreloc; + srlc->offset = rela.r_offset; + srlc->value = 0; + srlc->stt_type = (int) GELF_ST_TYPE (sym.st_info); + switch (GELF_ST_TYPE (sym.st_info)) + { + case STT_FUNC: + secHdr = elfp->get_shdr (sym.st_shndx); + if (secHdr) + srlc->value = secHdr->sh_offset + sym.st_value; + break; + case STT_OBJECT: + case STT_NOTYPE: + secHdr = elfp->get_shdr (shdr->sh_info); + if (secHdr) + { + srlc->offset = rela.r_info; + srlc->value = secHdr->sh_offset + rela.r_addend; + } + break; + case STT_SECTION: + secHdr = elfp->get_shdr (sym.st_shndx); + if (secHdr) + srlc->value = rela.r_addend; + break; + default: + srlc->value = 0; + break; + } + if (rlc == NULL) + { + rlc = new ElfReloc (elfp); + vp = rlc->reloc; + } + if (vp == NULL) + { + vp = new Vector<Sreloc*>; + rlc->reloc = vp; + } + vp->append (srlc); + } + if (vp) + vp->sort (DwrRelocOffsetCmp); + if (rlc) + { + rlc->dump_rela_debug_sec (sec); + rlc->dump (); + } + return rlc; +} + +long long +ElfReloc::get_reloc_addr (long long offset) +{ + Sreloc *srlc; + int i = cur_reloc_ind - 1; + if (i >= 0 && i < reloc->size ()) + { + srlc = reloc->fetch (i); + if (srlc->offset > offset) // need to reset + cur_reloc_ind = 0; + } + for (; cur_reloc_ind < reloc->size (); cur_reloc_ind++) + { + srlc = reloc->fetch (cur_reloc_ind); + if (srlc->offset == offset) + return srlc->value; + if (srlc->offset > offset) + return 0; + } + return 0; +} + +DwrLocation * +DwrCU::dwr_get_location (DwrSec *secp, DwrLocation *lp) +{ + lp->offset = secp->offset; + lp->lc_number = 0; + lp->lc_number2 = 0; + lp->op = secp->Get_8 (); + switch (lp->op) + { + // registers + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + break; + case DW_OP_regx: + lp->lc_number = secp->GetULEB128 (); + break; + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + lp->lc_number = secp->GetSLEB128 (); + break; + case DW_OP_fbreg: + lp->lc_number = secp->GetSLEB128 (); + break; + case DW_OP_bregx: + lp->lc_number = secp->GetULEB128 (); + lp->lc_number2 = secp->GetSLEB128 (); + break; + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + lp->lc_number = lp->op - DW_OP_lit0; + break; + case DW_OP_addr: + lp->lc_number = secp->GetADDR (); + break; + case DW_OP_const1u: + lp->lc_number = secp->Get_8 (); + break; + case DW_OP_const1s: + { + signed char x; + x = secp->Get_8 (); + lp->lc_number = x; + } + break; + case DW_OP_const2u: + lp->lc_number = secp->Get_16 (); + break; + case DW_OP_const2s: + { + signed short x; + x = secp->Get_16 (); + lp->lc_number = x; + } + break; + case DW_OP_const4u: + lp->lc_number = secp->Get_32 (); + break; + case DW_OP_const4s: + { + signed int x; + x = secp->Get_32 (); + lp->lc_number = x; + } + break; + case DW_OP_const8u: + lp->lc_number = secp->Get_64 (); + break; + case DW_OP_const8s: + { + signed long long x; + x = secp->Get_64 (); + lp->lc_number = x; + } + break; + case DW_OP_plus_uconst: + case DW_OP_constu: + lp->lc_number = secp->GetULEB128 (); + break; + case DW_OP_consts: + lp->lc_number = secp->GetSLEB128 (); + break; + + // Stack operations + case DW_OP_pick: + case DW_OP_deref_size: + case DW_OP_xderef_size: + lp->lc_number = secp->Get_8 (); + break; + case DW_OP_dup: + case DW_OP_drop: + case DW_OP_over: + case DW_OP_swap: + case DW_OP_rot: + case DW_OP_deref: + case DW_OP_xderef: + // Arithmetic and Logical Operations + case DW_OP_abs: + case DW_OP_and: + case DW_OP_div: + case DW_OP_minus: + case DW_OP_mod: + case DW_OP_mul: + case DW_OP_neg: + case DW_OP_not: + case DW_OP_or: + case DW_OP_plus: + case DW_OP_shl: + case DW_OP_shr: + case DW_OP_shra: + case DW_OP_xor: + case DW_OP_le: + case DW_OP_ge: + case DW_OP_eq: + case DW_OP_lt: + case DW_OP_gt: + case DW_OP_ne: + case DW_OP_nop: + break; + case DW_OP_skip: + case DW_OP_bra: + lp->lc_number = secp->Get_16 (); + break; + case DW_OP_piece: + lp->lc_number = secp->GetULEB128 (); + break; + case DW_OP_push_object_address: /* DWARF3 */ + break; + case DW_OP_call2: /* DWARF3 */ + lp->lc_number = secp->Get_16 (); + break; + case DW_OP_call4: /* DWARF3 */ + lp->lc_number = secp->Get_32 (); + break; + case DW_OP_call_ref: /* DWARF3 */ + lp->lc_number = secp->GetADDR (); + break; + default: + return (NULL); + } + return lp; +} + +char * +DwrCU::tag2str (int tag) +{ + static char buf[128]; + char *s; + + switch (tag) + { + CASE_S (DW_TAG_array_type); + CASE_S (DW_TAG_class_type); + CASE_S (DW_TAG_entry_point); + CASE_S (DW_TAG_enumeration_type); + CASE_S (DW_TAG_formal_parameter); + CASE_S (DW_TAG_imported_declaration); + CASE_S (DW_TAG_label); + CASE_S (DW_TAG_lexical_block); + CASE_S (DW_TAG_member); + CASE_S (DW_TAG_pointer_type); + CASE_S (DW_TAG_reference_type); + CASE_S (DW_TAG_compile_unit); + CASE_S (DW_TAG_string_type); + CASE_S (DW_TAG_structure_type); + CASE_S (DW_TAG_subroutine_type); + CASE_S (DW_TAG_typedef); + CASE_S (DW_TAG_union_type); + CASE_S (DW_TAG_unspecified_parameters); + CASE_S (DW_TAG_variant); + CASE_S (DW_TAG_common_block); + CASE_S (DW_TAG_common_inclusion); + CASE_S (DW_TAG_inheritance); + CASE_S (DW_TAG_inlined_subroutine); + CASE_S (DW_TAG_module); + CASE_S (DW_TAG_ptr_to_member_type); + CASE_S (DW_TAG_set_type); + CASE_S (DW_TAG_subrange_type); + CASE_S (DW_TAG_with_stmt); + CASE_S (DW_TAG_access_declaration); + CASE_S (DW_TAG_base_type); + CASE_S (DW_TAG_catch_block); + CASE_S (DW_TAG_const_type); + CASE_S (DW_TAG_constant); + CASE_S (DW_TAG_enumerator); + CASE_S (DW_TAG_file_type); + CASE_S (DW_TAG_friend); + CASE_S (DW_TAG_namelist); + CASE_S (DW_TAG_namelist_item); + CASE_S (DW_TAG_packed_type); + CASE_S (DW_TAG_subprogram); + CASE_S (DW_TAG_template_type_param); + CASE_S (DW_TAG_template_value_param); + CASE_S (DW_TAG_thrown_type); + CASE_S (DW_TAG_try_block); + CASE_S (DW_TAG_variant_part); + CASE_S (DW_TAG_variable); + CASE_S (DW_TAG_volatile_type); + CASE_S (DW_TAG_dwarf_procedure); + CASE_S (DW_TAG_restrict_type); + CASE_S (DW_TAG_interface_type); + CASE_S (DW_TAG_namespace); + CASE_S (DW_TAG_imported_module); + CASE_S (DW_TAG_unspecified_type); + CASE_S (DW_TAG_partial_unit); + CASE_S (DW_TAG_imported_unit); + CASE_S (DW_TAG_lo_user); + CASE_S (DW_TAG_MIPS_loop); + CASE_S (DW_TAG_format_label); + CASE_S (DW_TAG_function_template); + CASE_S (DW_TAG_class_template); + CASE_S (DW_TAG_GNU_BINCL); + CASE_S (DW_TAG_GNU_EINCL); + CASE_S (DW_TAG_GNU_call_site); + CASE_S (DW_TAG_GNU_call_site_parameter); + CASE_S (DW_TAG_SUN_codeflags); + CASE_S (DW_TAG_SUN_memop_info); + CASE_S (DW_TAG_hi_user); + CASE_S (DW_TAG_icc_compile_unit); + default: s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, tag); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +char * +DwrCU::at2str (int tag) +{ + static char buf[128]; + char *s; + switch (tag) + { + CASE_S (DW_AT_sibling); + CASE_S (DW_AT_location); + CASE_S (DW_AT_name); + CASE_S (DW_AT_ordering); + CASE_S (DW_AT_subscr_data); + CASE_S (DW_AT_byte_size); + CASE_S (DW_AT_bit_offset); + CASE_S (DW_AT_bit_size); + CASE_S (DW_AT_element_list); + CASE_S (DW_AT_stmt_list); + CASE_S (DW_AT_low_pc); + CASE_S (DW_AT_high_pc); + CASE_S (DW_AT_language); + CASE_S (DW_AT_member); + CASE_S (DW_AT_discr); + CASE_S (DW_AT_discr_value); + CASE_S (DW_AT_visibility); + CASE_S (DW_AT_import); + CASE_S (DW_AT_string_length); + CASE_S (DW_AT_common_reference); + CASE_S (DW_AT_comp_dir); + CASE_S (DW_AT_const_value); + CASE_S (DW_AT_containing_type); + CASE_S (DW_AT_default_value); + CASE_S (DW_AT_inline); + CASE_S (DW_AT_is_optional); + CASE_S (DW_AT_lower_bound); + CASE_S (DW_AT_producer); + CASE_S (DW_AT_prototyped); + CASE_S (DW_AT_return_addr); + CASE_S (DW_AT_start_scope); + CASE_S (DW_AT_stride_size); + CASE_S (DW_AT_upper_bound); + CASE_S (DW_AT_abstract_origin); + CASE_S (DW_AT_accessibility); + CASE_S (DW_AT_address_class); + CASE_S (DW_AT_artificial); + CASE_S (DW_AT_base_types); + CASE_S (DW_AT_calling_convention); + CASE_S (DW_AT_count); + CASE_S (DW_AT_data_member_location); + CASE_S (DW_AT_decl_column); + CASE_S (DW_AT_decl_file); + CASE_S (DW_AT_decl_line); + CASE_S (DW_AT_declaration); + CASE_S (DW_AT_discr_list); + CASE_S (DW_AT_encoding); + CASE_S (DW_AT_external); + CASE_S (DW_AT_frame_base); + CASE_S (DW_AT_friend); + CASE_S (DW_AT_identifier_case); + CASE_S (DW_AT_macro_info); + CASE_S (DW_AT_namelist_item); + CASE_S (DW_AT_priority); + CASE_S (DW_AT_segment); + CASE_S (DW_AT_specification); + CASE_S (DW_AT_static_link); + CASE_S (DW_AT_type); + CASE_S (DW_AT_use_location); + CASE_S (DW_AT_variable_parameter); + CASE_S (DW_AT_virtuality); + CASE_S (DW_AT_vtable_elem_location); + CASE_S (DW_AT_allocated); + CASE_S (DW_AT_associated); + CASE_S (DW_AT_data_location); + CASE_S (DW_AT_byte_stride); + CASE_S (DW_AT_entry_pc); + CASE_S (DW_AT_use_UTF8); + CASE_S (DW_AT_extension); + CASE_S (DW_AT_ranges); + CASE_S (DW_AT_trampoline); + CASE_S (DW_AT_call_column); + CASE_S (DW_AT_call_file); + CASE_S (DW_AT_call_line); + CASE_S (DW_AT_description); + CASE_S (DW_AT_binary_scale); + CASE_S (DW_AT_decimal_scale); + CASE_S (DW_AT_small); + CASE_S (DW_AT_decimal_sign); + CASE_S (DW_AT_digit_count); + CASE_S (DW_AT_picture_string); + CASE_S (DW_AT_mutable); + CASE_S (DW_AT_threads_scaled); + CASE_S (DW_AT_explicit); + CASE_S (DW_AT_object_pointer); + CASE_S (DW_AT_endianity); + CASE_S (DW_AT_elemental); + CASE_S (DW_AT_pure); + CASE_S (DW_AT_recursive); + CASE_S (DW_AT_signature); + CASE_S (DW_AT_main_subprogram); + CASE_S (DW_AT_data_bit_offset); + CASE_S (DW_AT_const_expr); + CASE_S (DW_AT_enum_class); + CASE_S (DW_AT_linkage_name); + CASE_S (DW_AT_lo_user); + CASE_S (DW_AT_MIPS_fde); + CASE_S (DW_AT_MIPS_loop_begin); + CASE_S (DW_AT_MIPS_tail_loop_begin); + CASE_S (DW_AT_MIPS_epilog_begin); + CASE_S (DW_AT_MIPS_loop_unroll_factor); + CASE_S (DW_AT_MIPS_software_pipeline_depth); + CASE_S (DW_AT_MIPS_linkage_name); + CASE_S (DW_AT_MIPS_stride); + CASE_S (DW_AT_MIPS_abstract_name); + CASE_S (DW_AT_MIPS_clone_origin); + CASE_S (DW_AT_MIPS_has_inlines); + CASE_S (DW_AT_sf_names); + CASE_S (DW_AT_src_info); + CASE_S (DW_AT_mac_info); + CASE_S (DW_AT_src_coords); + CASE_S (DW_AT_body_begin); + CASE_S (DW_AT_body_end); + CASE_S (DW_AT_GNU_vector); + CASE_S (DW_AT_GNU_guarded_by); + CASE_S (DW_AT_GNU_pt_guarded_by); + CASE_S (DW_AT_GNU_guarded); + CASE_S (DW_AT_GNU_pt_guarded); + CASE_S (DW_AT_GNU_locks_excluded); + CASE_S (DW_AT_GNU_exclusive_locks_required); + CASE_S (DW_AT_GNU_shared_locks_required); + CASE_S (DW_AT_GNU_odr_signature); + CASE_S (DW_AT_GNU_template_name); + CASE_S (DW_AT_GNU_call_site_value); + CASE_S (DW_AT_GNU_call_site_data_value); + CASE_S (DW_AT_GNU_call_site_target); + CASE_S (DW_AT_GNU_call_site_target_clobbered); + CASE_S (DW_AT_GNU_tail_call); + CASE_S (DW_AT_GNU_all_tail_call_sites); + CASE_S (DW_AT_GNU_all_call_sites); + CASE_S (DW_AT_GNU_all_source_call_sites); + CASE_S (DW_AT_SUN_command_line); + CASE_S (DW_AT_SUN_func_offsets); + CASE_S (DW_AT_SUN_cf_kind); + CASE_S (DW_AT_SUN_func_offset); + CASE_S (DW_AT_SUN_memop_type_ref); + CASE_S (DW_AT_SUN_profile_id); + CASE_S (DW_AT_SUN_memop_signature); + CASE_S (DW_AT_SUN_obj_dir); + CASE_S (DW_AT_SUN_obj_file); + CASE_S (DW_AT_SUN_original_name); + CASE_S (DW_AT_SUN_link_name); + CASE_S (DW_AT_hi_user); + CASE_S (DW_AT_icc_flags); + default: s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, tag); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +char * +DwrCU::form2str (int tag) +{ + static char buf[128]; + char *s; + switch (tag) + { + CASE_S (DW_FORM_addr); + CASE_S (DW_FORM_block2); + CASE_S (DW_FORM_block4); + CASE_S (DW_FORM_data2); + CASE_S (DW_FORM_data4); + CASE_S (DW_FORM_data8); + CASE_S (DW_FORM_string); + CASE_S (DW_FORM_block); + CASE_S (DW_FORM_block1); + CASE_S (DW_FORM_data1); + CASE_S (DW_FORM_flag); + CASE_S (DW_FORM_sdata); + CASE_S (DW_FORM_strp); + CASE_S (DW_FORM_udata); + CASE_S (DW_FORM_ref_addr); + CASE_S (DW_FORM_ref1); + CASE_S (DW_FORM_ref2); + CASE_S (DW_FORM_ref4); + CASE_S (DW_FORM_ref8); + CASE_S (DW_FORM_ref_udata); + CASE_S (DW_FORM_indirect); + CASE_S (DW_FORM_sec_offset); + CASE_S (DW_FORM_exprloc); + CASE_S (DW_FORM_flag_present); + CASE_S (DW_FORM_ref_sig8); + default: s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, tag); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +void +Dwr_Tag::dump () +{ + Dprintf (DUMP_DWARFLIB, + "\n<%2d>:<0x%08llx> %-30s <abbrev %lld> offset=0x%llx %s\n", + (int) level, (long long) die, DwrCU::tag2str (tag), (long long) num, + (long long) offset, + hasChild ? NTXT ("DW_children_yes") : NTXT ("DW_children_no")); + for (int i1 = firstAttribute; i1 < lastAttribute; i1++) + { + Dwr_Attr *atrp = abbrevAtForm->get (i1); + Dprintf (DUMP_DWARFLIB, " %-30s ", DwrCU::at2str (atrp->at_name)); + switch (atrp->at_form) + { + case DW_FORM_strp: + case DW_FORM_string: + Dprintf (DUMP_DWARFLIB, " \"%s\" len=%ld", + atrp->u.str ? atrp->u.str : NTXT ("<NULL>"), + (long) atrp->len); + break; + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + Dprintf (DUMP_DWARFLIB, " len=%3ld %p", (long) atrp->len, + atrp->u.str); + break; + case DW_FORM_addr: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_addr: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + case DW_FORM_indirect: + case DW_FORM_sec_offset: + case DW_FORM_exprloc: + case DW_FORM_ref_sig8: + case DW_FORM_flag_present: + Dprintf (DUMP_DWARFLIB, " 0x%llx (%lld)", (long long) atrp->u.val, + (long long) atrp->u.val); + break; + default: + DEBUG_CODE + { + Dprintf (1, "Attribute form 0x%llx (%lld) is not implemented\n", + (long long) atrp->at_form, (long long) atrp->at_form); + assert (false); + } + } + Dprintf (DUMP_DWARFLIB, NTXT ("\n")); + } +} + + +////////////////////////////////////////////////////////// +// class DwrSec + +DwrSec::DwrSec (unsigned char *_data, uint64_t _size, bool _need_swap_endian, bool _addr32) +{ + isCopy = false; + data = _data; + sizeSec = _size; + size = (data ? _size : 0); + offset = 0; + fmt64 = false; + reloc = NULL; + need_swap_endian = _need_swap_endian; + addr32 = _addr32; +} + +DwrSec::DwrSec (DwrSec *secp, uint64_t _offset) +{ + isCopy = true; + data = secp->data; + sizeSec = secp->sizeSec; + size = secp->size; + offset = _offset; + fmt64 = secp->fmt64; + reloc = secp->reloc; + need_swap_endian = secp->need_swap_endian; + addr32 = secp->addr32; +} + +DwrSec::~DwrSec () +{ + if (!isCopy) + delete reloc; +} + +bool +DwrSec::bounds_violation (uint64_t sz) +{ + if (offset + sz > size) + { + Dprintf (DEBUG_ERR_MSG, "DwrSec::bounds_violation: offset=%lld + sz=%lld > size=%lld\n", + (long long) offset, (long long) sz, (long long) size); + return true; + } + return false; +} + +uint64_t +DwrSec::ReadLength () +{ + fmt64 = false; + uint64_t val = Get_32 (); + if (((uint32_t) val) == 0xffffffff) + { + fmt64 = true; + val = Get_64 (); + } + size = (val + offset < sizeSec) ? val + offset : sizeSec; + return size; +} + +unsigned char +DwrSec::Get_8 () +{ + unsigned char n = 0; + if (bounds_violation (sizeof (char))) + return n; + n = data[offset]; + offset += sizeof (char); + return n; +} + +unsigned short +DwrSec::Get_16 () +{ + unsigned short n = 0; + if (bounds_violation (sizeof (short))) + return n; + memcpy ((char *) &n, data + offset, sizeof (short)); + offset += sizeof (short); + if (need_swap_endian) + SWAP_ENDIAN (n); + return n; +} + +uint32_t +DwrSec::Get_32 () +{ + uint32_t n = 0; + if (bounds_violation (sizeof (uint32_t))) + return n; + memcpy ((char *) &n, data + offset, sizeof (uint32_t)); + offset += sizeof (uint32_t); + if (need_swap_endian) + SWAP_ENDIAN (n); + return n; +} + +uint64_t +DwrSec::Get_64 () +{ + uint64_t n = 0; + if (bounds_violation (sizeof (uint64_t))) + return n; + memcpy ((char *) &n, data + offset, sizeof (uint64_t)); + offset += sizeof (uint64_t); + if (need_swap_endian) + SWAP_ENDIAN (n); + return n; +} + +char * +DwrSec::GetData (uint64_t len) +{ + char *s = ((char *) data) + offset; + if (bounds_violation (len)) + s = NULL; + offset += len; + return s; +} + +char * +DwrSec::GetString (uint64_t *lenp) +{ + if (offset < size) + { + uint64_t len = 0; + for (char *s = ((char *) data) + offset; offset + len < size; len++) + { + if (s[len] == 0) + { // '\0' is inside section + offset += len + 1; + if (len == 0) + return NULL; + if (lenp) + *lenp = len + 1; + return s; + } + } + offset += len; + return NULL; // The section is not '\0' terminated + } + return NULL; +} + +uint64_t +DwrSec::GetLong () +{ + if (fmt64) + return Get_64 (); + return Get_32 (); +} + +uint64_t +DwrSec::GetADDR_32 () +{ + uint64_t res = reloc ? reloc->get_reloc_addr (offset) : 0; + res += Get_32 (); + return res; +} + +uint64_t +DwrSec::GetADDR_64 () +{ + uint64_t res = reloc ? reloc->get_reloc_addr (offset) : 0; + res += Get_64 (); + return res; +} + +uint64_t +DwrSec::GetADDR () +{ + if (addr32) + return GetADDR_32 (); + return GetADDR_64 (); +} + +uint64_t +DwrSec::GetRef () +{ + if (fmt64) + return GetADDR_64 (); + return GetADDR_32 (); +} + +ULEB128 +DwrSec::GetULEB128 () +{ + ULEB128 res = 0; + for (int shift = 0;; shift += 7) + { + ULEB128 val = Get_8 (); + res |= (val & 0x7f) << shift; + if ((val & 0x80) == 0) + break; + } + return res; +} + +SLEB128 +DwrSec::GetSLEB128 () +{ + ULEB128 res = 0, val = 0; + size_t shift; + for (shift = 0;;) + { + val = Get_8 (); + res |= (val & 0x7f) << shift; + shift += 7; + if ((val & 0x80) == 0) + break; + } + if ((val & 0x40) && (shift < 8 * sizeof (res))) + res |= -(((ULEB128) 1) << shift); + return (SLEB128) res; +} + +static void +fillBuf (unsigned char *s, int len, int col, unsigned char *buf) +{ + const char *nameX = "0123456789abcdef"; + int i, n, posCh = 2 * col + col / 4 + 5; + + if (len >= col) + len = col; + for (i = n = 0; i < len; i++, n += 2) + { + if ((i % 4) == 0 && i > 0) + { + buf[n] = ' '; + n++; + } + buf[n] = nameX[s[i] >> 4]; + buf[n + 1] = nameX[s[i] & 0xf]; + buf[posCh + i] = isprint (s[i]) ? s[i] : ' '; + } + buf[posCh + i] = 0; + for (i = n; i < posCh; i++) + buf[i] = ' '; +} + +static void +dumpArr (unsigned char *s, int len, int col, int num) +{ + unsigned char buf[128]; + if (col <= 0) + return; + for (int i = 0; i < len; i += col, num += col) + { + fillBuf (s + i, len - i, col, buf); + Dprintf (DUMP_DWARFLIB, "%5d: %s\n", num, buf); + } +} + +void +DwrSec::dump (char *msg) +{ + if (sizeSec > 0) + { + Dprintf (DUMP_DWARFLIB, NTXT ("======= DwrSec::dump\n")); + if (msg) + Dprintf (DUMP_DWARFLIB, NTXT ("%s:\n"), msg); + dumpArr (data, (int) sizeSec, 32, 0); + Dprintf (DUMP_DWARFLIB, NTXT ("\n")); + } +} + +////////////////////////////////////////////////////////// +// class DwrFileNames + +DwrFileName::DwrFileName (char *_fname) +{ + path = NULL; + fname = _fname; + dir_index = 0; + timestamp = 0; + file_size = 0; + isUsed = false; +} + +DwrFileName::~DwrFileName () +{ + if (path != fname) + free (path); +} + + +////////////////////////////////////////////////////////// +// class DwrLine +DwrLine::DwrLine () +{ + address = 0; + file = 0; + line = 0; + column = 0; +} + +DwrLine::~DwrLine () { } + + +////////////////////////////////////////////////////////// +// class DwrLineRegs +static int +LineRegsCmp (const void *a, const void *b) +{ + DwrLine *item1 = *((DwrLine **) a); + DwrLine *item2 = *((DwrLine **) b); + return item1->address == item2->address ? 0 : + item1->address > item2->address ? 1 : -1; +} + +DwrLineRegs::DwrLineRegs (DwrSec *secp, char *dirName) +{ + // `dwarfdump -vv -l` shows a line section (.debug_line) + debug_lineSec = secp; + uint64_t stmt_offset = debug_lineSec->offset; + uint64_t next_cu_offset = debug_lineSec->ReadLength (); + uint64_t header_offset = debug_lineSec->offset; + debug_lineSec->size = next_cu_offset; + version = debug_lineSec->Get_16 (); + header_length = debug_lineSec->GetLong (); + opcode_start = debug_lineSec->offset + header_length; + minimum_instruction_length = debug_lineSec->Get_8 (); + op_index_register = 0; + if (version == 4) + maximum_operations_per_instruction = debug_lineSec->Get_8 (); + else + maximum_operations_per_instruction = 1; + default_is_stmt = debug_lineSec->Get_8 (); + is_stmt = (default_is_stmt != 0); + line_base = debug_lineSec->Get_8 (); + line_range = debug_lineSec->Get_8 (); + opcode_base = debug_lineSec->Get_8 (); + standard_opcode_length = (Dwarf_Small*) debug_lineSec->GetData (opcode_base - 1); + + if (DUMP_DWR_LINE_REGS) + { + Dprintf (DUMP_DWR_LINE_REGS, + "\n.debug_line version=%d stmt_offset=0x%llx" + "header_offset=0x%llx size=%lld dirname='%s'\n" + " header_length=0x%llx opcode_start=0x%llx" + "minimum_instruction_length=%d default_is_stmt=%d\n" + " line_base=%d line_range=%d opcode_base=%d\n", + (int) version, (long long) stmt_offset, + (long long) header_offset, + (long long) (next_cu_offset - header_offset), STR (dirName), + (long long) header_length, (long long) opcode_start, + (int) minimum_instruction_length, (int) default_is_stmt, + (int) line_base, (int) line_range, (int) opcode_base); + if (standard_opcode_length == NULL) + Dprintf (DUMP_DWR_LINE_REGS, "ERROR: standard_opcode_length is NULL\n"); + for (int i = 0, sz = standard_opcode_length ? opcode_base - 1 : 0; + i < sz; i++) + Dprintf (DUMP_DWR_LINE_REGS, " opcode[%2d] length %2d\n", i, + (int) standard_opcode_length[i]); + } + + include_directories = new Vector<char *>; + include_directories->append (dirName); + while (true) + { + char *s = debug_lineSec->GetString (NULL); + if (s == NULL) + break; + include_directories->append (s); + } + + file_names = new Vector<DwrFileName *>; + while (true) + { + char *s = debug_lineSec->GetString (NULL); + if (s == NULL) + break; + DwrFileName *fnp = new DwrFileName (s); + fnp->path = NULL; + fnp->fname = s; + fnp->dir_index = debug_lineSec->GetULEB128_32 (); + fnp->timestamp = debug_lineSec->GetULEB128 (); + fnp->file_size = debug_lineSec->GetULEB128 (); + file_names->append (fnp); + } + lines = NULL; + dump (); +} + +DwrLineRegs::~DwrLineRegs () +{ + Destroy (file_names); + Destroy (lines); + delete debug_lineSec; + delete include_directories; +} + +void +DwrLineRegs::dump () +{ + if (!DUMP_DWR_LINE_REGS) + return; + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("\ninclude_directories size=%lld\n"), (long long) VecSize (include_directories)); + for (long i = 0, sz = VecSize (include_directories); i < sz; i++) + { + char *s = include_directories->get (i); + Dprintf (DUMP_DWR_LINE_REGS, NTXT (" %2lld %s\n"), (long long) i, STR (s)); + } + + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("\nfile_names size=%lld\n"), (long long) VecSize (file_names)); + for (long i = 0, sz = VecSize (file_names); i < sz; i++) + { + DwrFileName *fnp = file_names->get (i); + Dprintf (DUMP_DWR_LINE_REGS, NTXT (" %2lld %-40s dir_index=%4lld timestamp=%8lld file_size=%lld\n"), + (long long) i, STR (fnp->fname), + (long long) fnp->dir_index, (long long) fnp->timestamp, (long long) fnp->file_size); + } + if (lines) + lines->dump (fname); + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("\n\n")); +} + +void +DwrLineRegs::DoExtendedOpcode () +{ + uint64_t size = debug_lineSec->GetULEB128 (); + if (size == 0) + { + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), NTXT ("ExtendedOpCode: size=0")); + return; + } + Dwarf_Small opcode = debug_lineSec->Get_8 (); + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), extended_opcode2str (opcode)); + switch (opcode) + { + case DW_LNE_end_sequence: + end_sequence = true; + reset (); + break; + case DW_LNE_set_address: + address = debug_lineSec->GetADDR (); + break; + case DW_LNE_define_file: + // TODO, add file to file list + fname = debug_lineSec->GetString (NULL); + dir_index = debug_lineSec->GetULEB128 (); + timestamp = debug_lineSec->GetULEB128 (); + file_size = debug_lineSec->GetULEB128 (); + break; + default: + debug_lineSec->GetData (size - 1); // skip unknown opcode + break; + } +} + +void +DwrLineRegs::DoStandardOpcode (int opcode) +{ + switch (opcode) + { + case DW_LNS_copy: + basic_block = false; + EmitLine (); + break; + case DW_LNS_advance_pc: + address += debug_lineSec->GetULEB128 () * minimum_instruction_length; + break; + case DW_LNS_advance_line: + line += (int) debug_lineSec->GetSLEB128 (); + break; + case DW_LNS_set_file: + file = debug_lineSec->GetULEB128_32 (); + break; + case DW_LNS_set_column: + column = debug_lineSec->GetULEB128_32 (); + break; + case DW_LNS_negate_stmt: + is_stmt = -is_stmt; + break; + case DW_LNS_set_basic_block: + basic_block = true; + break; + case DW_LNS_const_add_pc: + address += ((255 - opcode_base) / line_range) * minimum_instruction_length; + break; + case DW_LNS_fixed_advance_pc: + address += debug_lineSec->Get_16 (); + break; + default: // skip unknown opcode/operands + debug_lineSec->GetData (standard_opcode_length ? + standard_opcode_length[opcode] : 1); + break; + } +} + +void +DwrLineRegs::DoSpecialOpcode (int opcode) +{ + int max_op_per_instr = maximum_operations_per_instruction == 0 ? 1 + : maximum_operations_per_instruction; + int operation_advance = (opcode / line_range); + address += minimum_instruction_length * ((op_index_register + operation_advance) / max_op_per_instr); + op_index_register = (op_index_register + operation_advance) % max_op_per_instr; + line += line_base + (opcode % line_range); + basic_block = false; + EmitLine (); +} + +void +DwrLineRegs::reset () +{ + dir_index = 0; + timestamp = 0; + file_size = 0; + address = 0; + file = 1; + line = 1; + column = 0; + is_stmt = (default_is_stmt != 0); + basic_block = false; + end_sequence = false; +} + +void +DwrLineRegs::EmitLine () +{ + DwrLine *lnp = new DwrLine; + + lnp->file = file; + lnp->line = line; + lnp->column = column; + lnp->address = address; + lines->append (lnp); + if ((file > 0) && (file < VecSize (file_names))) + { + DwrFileName *fnp = file_names->get (file); + fnp->isUsed = true; + } +} + +Vector<DwrLine *> * +DwrLineRegs::get_lines () +{ + if (lines == NULL) + { + lines = new Vector<DwrLine *>; + debug_lineSec->offset = opcode_start; + reset (); + Dprintf (DUMP_DWR_LINE_REGS, "\n offset code address (file, line, column) stmt blck end_seq \n"); + while (debug_lineSec->offset < debug_lineSec->size) + { + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("0x%08llx "), + (long long) debug_lineSec->offset); + Dwarf_Small opcode = debug_lineSec->Get_8 (); + if (opcode == 0) + DoExtendedOpcode (); + else if (opcode < opcode_base) + { + DoStandardOpcode (opcode); + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), standard_opcode2str (opcode)); + } + else + { + DoSpecialOpcode (opcode - opcode_base); + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), + special_opcode2str (opcode - opcode_base)); + } + Dprintf (DUMP_DWR_LINE_REGS, + " 0x%08llx (%lld, %lld, %lld) %c %c %c\n", + (long long) address, (long long) file, (long long) line, + (long long) column, is_stmt ? 'T' : 'F', + basic_block ? 'T' : 'F', end_sequence ? 'T' : 'F'); + } + lines->sort (LineRegsCmp); + if (DUMP_DWR_LINE_REGS) + lines->dump (fname); + } + return lines; +} + +char * +DwrLineRegs::getPath (int fn) +{ + fn--; + if ((fn >= VecSize (file_names)) || (fn < 0)) + { + Dprintf (DEBUG_ERR_MSG, NTXT ("DwrLineRegs::getPath: fn=0x%lld file_names->size()=%lld\n"), + (long long) fn, (long long) VecSize (file_names)); + return NULL; + } + DwrFileName *fnp = file_names->fetch (fn); + if (fnp->path) + return fnp->path; + + char *dir = fnp->dir_index < include_directories->size () ? + include_directories->fetch (fnp->dir_index) : NULL; + if ((fnp->fname[0] == '/') || (dir == NULL) || (*dir == 0)) + { + fnp->path = fnp->fname; + return fnp->path; + } + + StringBuilder sb; + if (*dir != '/') + { // not absolute + char *s = include_directories->fetch (0); + sb.append (s); + sb.append ('/'); + } + sb.append (dir); + sb.append ('/'); + sb.append (fnp->fname); + fnp->path = canonical_path (sb.toString ()); + return fnp->path; +} + +DwrCU::DwrCU (Dwarf *_dwarf) +{ + dwarf = _dwarf; + cu_offset = dwarf->debug_infoSec->offset; + debug_infoSec = new DwrSec (dwarf->debug_infoSec, cu_offset); + next_cu_offset = debug_infoSec->ReadLength (); + if (next_cu_offset > debug_infoSec->sizeSec) + { + Dprintf (DEBUG_ERR_MSG, + "DwrCU::DwrCU: next_cu_offset(0x%llx) > debug_infoSec->sizeSec(%llx)\n", + (long long) next_cu_offset, (long long) debug_infoSec->sizeSec); + next_cu_offset = debug_infoSec->sizeSec; + } + debug_infoSec->size = next_cu_offset; + version = debug_infoSec->Get_16 (); + debug_abbrev_offset = debug_infoSec->GetLong (); + address_size = debug_infoSec->Get_8 (); + cu_header_offset = debug_infoSec->offset; + comp_dir = NULL; + module = NULL; + abbrevTable = NULL; + dwrInlinedSubrs = NULL; + srcFiles = NULL; + stmt_list_offset = 0; + dwrLineReg = NULL; + isMemop = false; + isGNU = false; + dwrTag.level = 0; + + build_abbrevTable (dwarf->debug_abbrevSec, debug_abbrev_offset); +#ifdef DEBUG + if (DUMP_DWARFLIB) + { + Dprintf (DUMP_DWARFLIB, + "CU_HEADER: header_offset = 0x%08llx %lld" + "next_header_offset=0x%08llx %lld\n" + " abbrev_offset = 0x%08llx %lld\n" + " unit_length = %lld\n" + " version = %d\n" + " address_size = %d\n" + " fmt64 = %s\n" + "debug_info: need_swap_endian=%s fmt64=%s addr32=%s\n", + (long long) cu_offset, (long long) cu_offset, + (long long) next_cu_offset, (long long) next_cu_offset, + (long long) debug_abbrev_offset, (long long) debug_abbrev_offset, + (long long) (next_cu_offset - cu_offset), + (int) version, (int) address_size, + debug_infoSec->fmt64 ? "true" : "false", + debug_infoSec->need_swap_endian ? "true" : "false", + debug_infoSec->fmt64 ? "true" : "false", + debug_infoSec->addr32 ? "true" : "false"); + Dprintf (DUMP_DWARFLIB, "\n.debug_abbrev cnt=%d offset=0x%08llx %lld\n", + (int) VecSize (abbrevTable), (long long) debug_abbrev_offset, + (long long) debug_abbrev_offset); + for (int i = 1, sz = VecSize (abbrevTable); i < sz; i++) + { + DwrAbbrevTable *abbTbl = abbrevTable->get (i); + Dprintf (DUMP_DWARFLIB, NTXT ("%5d: %-30s %-20s offset=0x%08llx\n"), + (int) i, DwrCU::tag2str (abbTbl->tag), + abbTbl->hasChild ? "DW_children_yes" : "DW_children_no", + (long long) abbTbl->offset); + for (int i1 = abbTbl->firstAtForm; i1 < abbTbl->lastAtForm; i1++) + { + Dwr_Attr *atf = abbrevAtForm->get (i1); + Dprintf (DUMP_DWARFLIB, " %-30s %s\n", + DwrCU::at2str (atf->at_name), + DwrCU::form2str (atf->at_form)); + } + } + } +#endif +} + +DwrCU::~DwrCU () +{ + delete debug_infoSec; + delete abbrevTable; + delete abbrevAtForm; + Destroy (dwrInlinedSubrs); + delete srcFiles; + delete dwrLineReg; + free (comp_dir); +} + +void +DwrCU::build_abbrevTable (DwrSec *_debug_abbrevSec, uint64_t _offset) +{ + if (abbrevTable) + return; + DwrSec *debug_abbrevSec = new DwrSec (_debug_abbrevSec, _offset); + abbrevTable = new DbeArray <DwrAbbrevTable>(128); + abbrevAtForm = new DbeArray <Dwr_Attr>(512); + abbrevTable->allocate (1); // skip first + abbrevAtForm->allocate (1); // skip first + for (int i = 1; debug_abbrevSec->offset < debug_abbrevSec->size; i++) + { + DwrAbbrevTable abbTbl; + abbTbl.offset = debug_abbrevSec->offset; + abbTbl.code = debug_abbrevSec->GetULEB128_32 (); + if (abbTbl.code == 0) + break; + else if (i != abbTbl.code) + { + dwarf->elf->append_msg (CMSG_ERROR, GTXT ("%s: the abbreviations table is corrupted (%lld <--> %lld)\n"), + get_basename (dwarf->elf->get_location ()), + (long long) i, (long long) abbTbl.code); + break; + } + abbTbl.tag = debug_abbrevSec->GetULEB128_32 (); + abbTbl.hasChild = (DW_children_yes == debug_abbrevSec->Get_8 ()); + abbTbl.firstAtForm = abbrevAtForm->size (); + while (debug_abbrevSec->offset < debug_abbrevSec->size) + { + Dwr_Attr atf; + atf.at_name = debug_abbrevSec->GetULEB128_32 (); + atf.at_form = debug_abbrevSec->GetULEB128_32 (); + if (atf.at_name == 0 && atf.at_form == 0) + break; + abbrevAtForm->append (atf); + } + abbTbl.lastAtForm = abbrevAtForm->size (); + abbrevTable->append (abbTbl); + } + delete debug_abbrevSec; +} + +int +DwrCU::set_die (Dwarf_Die die) +{ + if (die > 0) + debug_infoSec->offset = die; + if (debug_infoSec->offset < cu_header_offset + || debug_infoSec->offset >= debug_infoSec->size) + return DW_DLV_ERROR; + dwrTag.offset = debug_infoSec->offset; + dwrTag.die = debug_infoSec->offset - cu_offset; + dwrTag.num = debug_infoSec->GetULEB128_32 (); + if (dwrTag.num == 0) + return DW_DLV_NO_ENTRY; + dwrTag.abbrevAtForm = abbrevAtForm; + DwrAbbrevTable *abbTbl = abbrevTable->get (dwrTag.num); + if (abbTbl == NULL) + { // corrupt dwarf + dwarf->elf->append_msg (CMSG_ERROR, GTXT ("%s: the abbreviation code (%lld) does not match for the Dwarf entry (0x%llx)\n"), + get_basename (dwarf->elf->get_location ()), + (long long) dwrTag.num, (long long) dwrTag.offset); + return DW_DLV_ERROR; + } + dwrTag.tag = abbTbl->tag; + dwrTag.hasChild = abbTbl->hasChild; + dwrTag.firstAttribute = abbTbl->firstAtForm; + dwrTag.lastAttribute = abbTbl->lastAtForm; + for (int k = abbTbl->firstAtForm; k < abbTbl->lastAtForm; k++) + { + Dwr_Attr *atf = abbrevAtForm->get (k); + int at_form = atf->at_form; + if (at_form == DW_FORM_indirect) + at_form = debug_infoSec->GetULEB128_32 (); + switch (at_form) + { + case DW_FORM_addr: + atf->u.offset = (address_size == 4) ? debug_infoSec->GetADDR_32 () + : debug_infoSec->GetADDR_64 (); + break; + case DW_FORM_flag: + atf->u.offset = debug_infoSec->Get_8 (); + break; + case DW_FORM_block: + atf->len = debug_infoSec->GetULEB128 (); + atf->u.str = debug_infoSec->GetData (atf->len); + break; + case DW_FORM_block1: + atf->len = debug_infoSec->Get_8 (); + atf->u.str = debug_infoSec->GetData (atf->len); + break; + case DW_FORM_block2: + atf->len = debug_infoSec->Get_16 (); + atf->u.str = debug_infoSec->GetData (atf->len); + break; + case DW_FORM_block4: + atf->len = debug_infoSec->Get_32 (); + atf->u.str = debug_infoSec->GetData (atf->len); + break; + case DW_FORM_ref1: + atf->u.offset = debug_infoSec->Get_8 (); + break; + case DW_FORM_ref2: + atf->u.offset = debug_infoSec->Get_16 (); + break; + case DW_FORM_ref4: + atf->u.offset = debug_infoSec->Get_32 (); + break; + case DW_FORM_ref8: + atf->u.offset = debug_infoSec->Get_64 (); + break; + case DW_FORM_ref_udata: + atf->u.offset = debug_infoSec->GetULEB128 (); + break; + case DW_FORM_data1: + atf->u.offset = debug_infoSec->Get_8 (); + break; + case DW_FORM_data2: + atf->u.offset = debug_infoSec->Get_16 (); + break; + case DW_FORM_data4: + atf->u.offset = debug_infoSec->Get_32 (); + break; + case DW_FORM_data8: + atf->u.offset = debug_infoSec->Get_64 (); + break; + case DW_FORM_string: + atf->u.str = debug_infoSec->GetString (&atf->len); + break; + case DW_FORM_strp: + atf->u.offset = debug_infoSec->GetRef (); + if (dwarf->debug_strSec == NULL) + { + atf->u.str = NULL; + atf->len = 0; + } + else + { + dwarf->debug_strSec->offset = atf->u.offset; + atf->u.str = dwarf->debug_strSec->GetString (&atf->len); + } + break; + case DW_FORM_sdata: + atf->u.val = debug_infoSec->GetSLEB128 (); + break; + case DW_FORM_udata: + atf->u.offset = debug_infoSec->GetULEB128 (); + break; + case DW_FORM_ref_addr: + atf->u.offset = debug_infoSec->GetADDR (); + break; + case DW_FORM_sec_offset: + atf->u.offset = debug_infoSec->GetRef (); + break; + case DW_FORM_exprloc: + atf->u.offset = debug_infoSec->GetULEB128 (); + debug_infoSec->offset += atf->u.offset; + break; + case DW_FORM_flag_present: + atf->u.val = 1; + break; + case DW_FORM_ref_sig8: + atf->u.offset = debug_infoSec->GetADDR_64 (); + break; + default: + DEBUG_CODE + { + Dprintf (1, "Attribute form 0x%llx (%lld) is not implemented\n", + (long long) atf->at_form, (long long) atf->at_form); + assert (0); + } + atf->u.str = NULL; + atf->len = 0; + break; + } + } + dwrTag.dump (); + return DW_DLV_OK; +} + +static char * +composePath (char *dname, char *fname) +{ + char *s; + if (*fname == '/' || dname == NULL) + s = dbe_sprintf (NTXT ("%s"), fname); + else + s = dbe_sprintf (NTXT ("%s/%s"), dname, fname); + return canonical_path (s); +} + +Module * +DwrCU::parse_cu_header (LoadObject *lo) +{ + // Is tag always DW_TAG_compile_unit? + if (dwrTag.tag != DW_TAG_compile_unit) + { + Dprintf (DEBUG_ERR_MSG, + "parse_cu_header: die=0x%llx tag=%lld is not DW_TAG_compile_unit\n", + (long long) cu_offset, (long long) dwrTag.tag); + return NULL; + } + + char *name = Dwarf_string (DW_AT_name); + if (name == NULL) + name = NTXT ("UnnamedUnit"); + stmt_list_offset = Dwarf_data (DW_AT_stmt_list); + comp_dir = dbe_strdup (Dwarf_string (DW_AT_comp_dir)); + char *dir_name = comp_dir ? StrChr (comp_dir, ':') : NULL; + char *orig_name = Dwarf_string (DW_AT_SUN_original_name); + char *path = composePath (dir_name, orig_name ? orig_name : name); + + module = dwarf->stabs->append_Module (lo, path); + free (path); + if (module == NULL) + return NULL; + module->hasDwarf = true; + if (orig_name) + module->linkerStabName = composePath (dir_name, name); + module->lang_code = Dwarf_lang (); + module->comp_flags = dbe_strdup (Dwarf_string (DW_AT_SUN_command_line)); + if (module->comp_flags == NULL) + module->comp_flags = dbe_strdup (Dwarf_string (DW_AT_icc_flags)); + module->comp_dir = dbe_strdup (dir_name); + + char *obj_file = Dwarf_string (DW_AT_SUN_obj_file); + char *obj_dir = Dwarf_string (DW_AT_SUN_obj_dir); + if (obj_dir && obj_file) + { + // object information may not be available + dir_name = StrChr (obj_dir, ':'); + path = composePath (dir_name, obj_file); + if (module->dot_o_file == NULL) + module->dot_o_file = module->createLoadObject (path); + } + else + path = dbe_strdup (dwarf->stabs->path); + module->set_name (path); + return module; +} + +Dwr_Attr * +Dwr_Tag::get_attr (Dwarf_Half attr) +{ + for (long i = firstAttribute; i < lastAttribute; i++) + { + Dwr_Attr *atf = abbrevAtForm->get (i); + if (atf->at_name == attr) + return atf; + } + return NULL; +} + +char * +DwrCU::Dwarf_string (Dwarf_Half attr) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + return dwrAttr ? dwrAttr->u.str : NULL; +} + +uint64_t +DwrCU::get_high_pc (uint64_t low_pc) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (DW_AT_high_pc); + if (dwrAttr) + switch (dwrAttr->at_form) + { + case DW_FORM_addr: + return dwrAttr->u.offset; + default: + return dwrAttr->u.offset + low_pc; + } + return 0; +} + +Dwarf_Addr +DwrCU::Dwarf_addr (Dwarf_Half attr) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + if (dwrAttr) + switch (dwrAttr->at_form) + { + case DW_FORM_addr: + return dwrAttr->u.offset; + } + return 0; +} + +DwrSec* +DwrCU::Dwarf_block (Dwarf_Half attr) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + if (dwrAttr && dwrAttr->u.block) + switch (dwrAttr->at_form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + return new DwrSec (dwrAttr->u.block, dwrAttr->len, + dwarf->elf->need_swap_endian, + dwarf->elf->elf_getclass () == ELFCLASS32); + } + return NULL; +} + +int +DwrCU::read_data_attr (Dwarf_Half attr, int64_t *retVal) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + if (dwrAttr) + switch (dwrAttr->at_form) + { + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_udata: + case DW_FORM_sec_offset: + *retVal = dwrAttr->u.val; + return DW_DLV_OK; + + } + return DW_DLV_ERROR; +} + +int +DwrCU::read_ref_attr (Dwarf_Half attr, int64_t *retVal) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + if (dwrAttr) + switch (dwrAttr->at_form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + case DW_FORM_sec_offset: + case DW_FORM_exprloc: + case DW_FORM_ref_sig8: + *retVal = dwrAttr->u.val; + return DW_DLV_OK; + } + return DW_DLV_ERROR; +} + +int64_t +DwrCU::Dwarf_data (Dwarf_Half attr) +{ + int64_t retVal; + if (read_data_attr (attr, &retVal) == DW_DLV_OK) + return retVal; + return 0; +} + +int64_t +DwrCU::Dwarf_ref (Dwarf_Half attr) +{ + int64_t retVal; + if (read_ref_attr (attr, &retVal) == DW_DLV_OK) + return retVal; + return 0; +} + +Dwarf_Addr +DwrCU::Dwarf_location (Dwarf_Attribute attr) +{ + DwrSec *secp = Dwarf_block (attr); + if (secp) + { + DwrLocation loc; + DwrLocation *lp = dwr_get_location (secp, &loc); + delete secp; + if (lp) + return lp->lc_number; + } + return 0; +} + +void +DwrCU::map_dwarf_lines (Module *mod) +{ + DwrLineRegs *lineReg = get_dwrLineReg (); + long inlinedSubrCnt = VecSize (dwrInlinedSubrs); + if (isGNU && (inlinedSubrCnt > 0)) + { + Function *func = NULL; + mod->inlinedSubr = (InlinedSubr *) malloc (inlinedSubrCnt + * sizeof (InlinedSubr)); + for (long i = 0; i < inlinedSubrCnt; i++) + { + DwrInlinedSubr *inlinedSubr = dwrInlinedSubrs->get (i); + uint64_t low_pc; + Function *f = dwarf->stabs->map_PC_to_func (inlinedSubr->low_pc, + low_pc, mod->functions); + if (f == NULL) + continue; + if (func != f) + { + func = f; + func->inlinedSubrCnt = 0; + func->inlinedSubr = mod->inlinedSubr + i; + } + InlinedSubr *p = func->inlinedSubr + func->inlinedSubrCnt; + func->inlinedSubrCnt++; + int fileno = inlinedSubr->file - 1; + SourceFile *sf = ((fileno >= 0) && (fileno < VecSize (srcFiles))) ? + srcFiles->get (fileno) : dbeSession->get_Unknown_Source (); + p->dbeLine = sf->find_dbeline (inlinedSubr->line); + p->high_pc = inlinedSubr->high_pc - low_pc; + p->low_pc = inlinedSubr->low_pc - low_pc; + p->level = inlinedSubr->level; + p->func = NULL; + p->fname = NULL; + if (set_die (inlinedSubr->abstract_origin) == DW_DLV_OK) + p->fname = dbe_strdup (Dwarf_string (DW_AT_name)); + if (p->fname) + p->func = Stabs::find_func (p->fname, mod->functions, + Stabs::is_fortran (mod->lang_code)); + } + } + Vector<DwrLine *> *lines = lineReg->get_lines (); + + Include *includes = new Include; + includes->new_src_file (mod->getMainSrc (), 0, NULL); + char *path = NULL; + SourceFile *cur_src = NULL; + Function *cur_func = NULL; + for (long i = 0, sz = VecSize (lines); i < sz; i++) + { + DwrLine *dwrLine = lines->get (i); + char *filename = dwrLineReg->getPath (dwrLine->file); + if (filename == NULL) + continue; + uint64_t pc = dwrLine->address; + int lineno = dwrLine->line; + if (path != filename) + { + path = filename; + char *name = StrChr (path, ':'); + SourceFile *src = mod->setIncludeFile (name); + if (cur_src != src) + { + includes->new_src_file (src, lineno, cur_func); + cur_src = src; + } + } + uint64_t low_pc; + Function *func = dwarf->stabs->map_PC_to_func (pc, low_pc, mod->functions); + if (func && (func->module == mod)) + { + if (func != cur_func) + { + if (cur_func) + while (cur_func->popSrcFile () != NULL) + ; + cur_func = func; + includes->push_src_files (cur_func); + } + cur_func->add_PC_info (pc - low_pc, lineno); + } + } + if (cur_func) + while (cur_func->popSrcFile ()) + ; + delete includes; +} + +DwrLineRegs * +DwrCU::get_dwrLineReg () +{ + if (dwrLineReg == NULL) + dwrLineReg = new DwrLineRegs (new DwrSec (dwarf->debug_lineSec, + stmt_list_offset), comp_dir); + return dwrLineReg; +} + +void +DwrCU::parse_inlined_subroutine (Dwarf_cnt *ctx) +{ + int64_t abstract_origin = Dwarf_ref (DW_AT_abstract_origin); + int fileno = (int) Dwarf_data (DW_AT_call_file); + int lineno = (int) Dwarf_data (DW_AT_call_line); + int level = ctx->inlinedSubr ? (ctx->inlinedSubr->level + 1) : 0; + DwrInlinedSubr *inlinedSubr_old = ctx->inlinedSubr; + + if (dwrInlinedSubrs == NULL) + dwrInlinedSubrs = new Vector<DwrInlinedSubr*>; + Dwr_Attr *dwrAttr = dwrTag.get_attr (DW_AT_ranges); + if (dwrAttr) + { + uint64_t ranges = Dwarf_ref (DW_AT_ranges); + if (dwarf->debug_rangesSec && (ranges < dwarf->debug_rangesSec->size)) + { + dwarf->debug_rangesSec->offset = ranges; + for (;;) + { + uint64_t low_pc = dwarf->debug_rangesSec->GetADDR (); + uint64_t high_pc = dwarf->debug_rangesSec->GetADDR (); + if ((low_pc > 0) && (low_pc <= high_pc)) + { + DwrInlinedSubr *p = new DwrInlinedSubr (abstract_origin, + low_pc, high_pc, fileno, lineno, level); + dwrInlinedSubrs->append (p); + ctx->inlinedSubr = p; + } + else + break; + } + } + } + else + { + uint64_t low_pc = Dwarf_addr (DW_AT_low_pc); + uint64_t high_pc = get_high_pc (low_pc); + if ((low_pc > 0) && (low_pc <= high_pc)) + { + DwrInlinedSubr *p = new DwrInlinedSubr (abstract_origin, low_pc, + high_pc, fileno, lineno, level); + dwrInlinedSubrs->append (p); + ctx->inlinedSubr = p; + } + } + parseChild (ctx); + ctx->inlinedSubr = inlinedSubr_old; +} + + +////////////////////////////////////////////////////////// +// class DwrInlinedSubr +DwrInlinedSubr::DwrInlinedSubr (int64_t _abstract_origin, uint64_t _low_pc, + uint64_t _high_pc, int _file, int _line, int _level) +{ + abstract_origin = _abstract_origin; + low_pc = _low_pc; + high_pc = _high_pc; + file = _file; + line = _line; + level = _level; +} + +void +DwrInlinedSubr::dump () +{ + Dprintf (DUMP_DWARFLIB, + " level=%d 0x%08llx [0x%08llx - 0x%08llx] file=%d line=%d\n", + (int) level, (long long) abstract_origin, (long long) low_pc, + (long long) high_pc, (int) file, (int) line); +} diff --git a/gprofng/src/DwarfLib.h b/gprofng/src/DwarfLib.h new file mode 100644 index 0000000..95eff57 --- /dev/null +++ b/gprofng/src/DwarfLib.h @@ -0,0 +1,313 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DWARFLIB_H_ +#define _DWARFLIB_H_ + +#include "dwarf2.h" + +class ElfReloc; +class Dwr_type; +class SourceFile; + +template <class ITEM> class Vector; +template <class ITEM> class DbeArray; +template <typename Key_t, typename Value_t> class DefaultMap; + +typedef uint64_t ULEB128; +typedef int64_t SLEB128; +typedef unsigned short Dwarf_Half; +typedef unsigned char Dwarf_Small; +typedef uint64_t Dwarf_Off; +typedef uint64_t Dwarf_Addr; +typedef uint64_t Dwarf_Unsigned; +typedef int64_t Dwarf_Die; +typedef int32_t Dwarf_Debug; +typedef int32_t Dwarf_Attribute; + + +class DwrSec +{ +public: + DwrSec (unsigned char *_data, uint64_t _size, bool _need_swap_endian, bool _addr32); + DwrSec (DwrSec *secp, uint64_t _offset); + ~DwrSec (); + unsigned char Get_8 (); + unsigned short Get_16 (); + uint32_t Get_32 (); + uint64_t Get_64 (); + uint64_t GetRef (); + uint64_t GetADDR (); + uint64_t GetADDR_32 (); + uint64_t GetADDR_64 (); + uint64_t GetLong (); + uint64_t ReadLength (); + SLEB128 GetSLEB128 (); + ULEB128 GetULEB128 (); + char *GetString (uint64_t *lenp); + char *GetData (uint64_t len); + void dump (char *msg); + + inline uint32_t + GetULEB128_32 () + { + return (uint32_t) GetULEB128 (); + } + + bool + inRange (uint64_t left, uint64_t right) + { + return (offset >= left) && (offset < right); + }; + + ElfReloc *reloc; + uint64_t sizeSec; + uint64_t size; + uint64_t offset; + bool fmt64; + bool addr32; + bool need_swap_endian; + +private: + bool isCopy; + unsigned char *data; + bool bounds_violation (uint64_t sz); +}; + +class DwrFileName +{ +public: + DwrFileName (char *_fname); + ~DwrFileName (); + uint64_t timestamp; + uint64_t file_size; + int dir_index; + char *fname; + char *path; + bool isUsed; +}; + +class DwrLine +{ +public: + DwrLine (); + ~DwrLine (); + uint64_t address; + uint32_t file; + uint32_t line; + uint32_t column; +}; + +class DwrInlinedSubr +{ +public: + DwrInlinedSubr (int64_t _abstract_origin, uint64_t _low_pc, uint64_t _high_pc, + int _file, int _line, int _level); + void dump (); + int64_t abstract_origin; + uint64_t low_pc; + uint64_t high_pc; + int file; + int line; + int level; +}; + +class DwrLineRegs +{ +public: + DwrLineRegs (DwrSec *_secp, char *dirName); + ~DwrLineRegs (); + char *getPath (int fn); + Vector<DwrLine *> *get_lines (); + void dump (); + + Vector<DwrFileName *> *file_names; + +private: + void DoExtendedOpcode (); + void DoStandardOpcode (int opcode); + void DoSpecialOpcode (int opcode); + void EmitLine (); + void reset (); + + char *fname; + uint64_t dir_index; + uint64_t timestamp; + uint64_t file_size; + uint64_t address; + uint32_t file; + uint32_t line; + uint32_t column; + Dwarf_Half version; + uint64_t op_index_register; + Dwarf_Small maximum_operations_per_instruction; + Dwarf_Small minimum_instruction_length; + Dwarf_Small default_is_stmt; + Dwarf_Small line_range; + Dwarf_Small opcode_base; + signed char line_base; + bool is_stmt; + bool basic_block; + bool end_sequence; + Vector<DwrLine *> *lines; + Vector<char *> *include_directories; + Dwarf_Small *standard_opcode_length; + DwrSec *debug_lineSec; + uint64_t header_length; + uint64_t opcode_start; +}; + +typedef struct Dwr_Attr +{ + union + { + char *str; + unsigned char *block; + uint64_t offset; + int64_t val; + } u; + uint64_t len; // length of u.str + int at_form; + int at_name; +} Dwr_Attr; + +typedef struct Dwr_Tag +{ +public: + Dwr_Attr *get_attr (Dwarf_Half attr); + void dump (); + + DbeArray<Dwr_Attr> *abbrevAtForm; + int64_t die; + int64_t offset; + int firstAttribute; + int lastAttribute; + int tag; + int hasChild; + int num; + int level; +} Dwr_Tag; + +enum +{ + DW_DLV_OK, + DW_DLV_NO_ENTRY, + DW_DLV_ERROR, + DW_DLV_BAD_ELF, + DW_DLV_NO_DWARF, + DW_DLV_WRONG_ARG +}; + +typedef struct DwrLocation +{ + uint64_t offset; + uint64_t lc_number; + uint64_t lc_number2; + uint32_t op; +} DwrLocation; + +typedef struct DwrAbbrevTable +{ + int64_t offset; + int firstAtForm; + int lastAtForm; + int code; + int tag; + bool hasChild; +} DwrAbbrevTable; + +class Dwarf_cnt +{ +public: + Dwarf_cnt (); + int64_t cu_offset; + int64_t parent; + int64_t size; + Module *module; + char *name; + Function *func; + Function *fortranMAIN; + datatype_t *dtype; + DwrInlinedSubr *inlinedSubr; + DefaultMap <int64_t, Dwr_type*> *dwr_types; + int level; + + Dwr_type *get_dwr_type (int64_t cu_die_offset); + Dwr_type *put_dwr_type (int64_t cu_die_offset, int tag); + Dwr_type *put_dwr_type (Dwr_Tag *dwrTag); +}; + +class DwrCU +{ +public: + DwrCU (Dwarf *_dwarf); + ~DwrCU (); + Module *parse_cu_header (LoadObject *lo); + void parseChild (Dwarf_cnt *ctx); + void read_hwcprof_info (Dwarf_cnt *ctx); + void map_dwarf_lines (Module *mod); + int set_die (Dwarf_Die die); + DwrLineRegs *get_dwrLineReg (); + + static char *at2str (int tag); + static char *form2str (int tag); + static char *tag2str (int tag); + + uint64_t cu_header_offset; + uint64_t cu_offset; + uint64_t next_cu_offset; + Vector<DwrInlinedSubr*> *dwrInlinedSubrs; + Vector<SourceFile *> *srcFiles; + bool isMemop; + bool isGNU; + +private: + void build_abbrevTable (DwrSec *debug_abbrevSec, uint64_t stmt_list_offset); + Function *append_Function (Dwarf_cnt *ctx); + void parse_inlined_subroutine (Dwarf_cnt *ctx); + uint64_t get_low_pc (); + uint64_t get_high_pc (uint64_t low_pc); + DwrLocation *dwr_get_location (DwrSec *secp, DwrLocation *lp); + int read_data_attr (Dwarf_Half attr, int64_t *retVal); + int read_ref_attr (Dwarf_Half attr, int64_t *retVal); + char *get_linkage_name (); + char *Dwarf_string (Dwarf_Half attr); + int64_t Dwarf_data (Dwarf_Half attr); + int64_t Dwarf_ref (Dwarf_Half attr); + DwrSec *Dwarf_block (Dwarf_Half attr); + Dwarf_Addr Dwarf_addr (Dwarf_Half attr); + Dwarf_Addr Dwarf_location (Dwarf_Attribute attr); + Sp_lang_code Dwarf_lang (); + + Dwarf *dwarf; + DwrSec *debug_infoSec; + uint64_t debug_abbrev_offset; + uint64_t stmt_list_offset; // offset in .debug_line section (DW_AT_stmt_list) + char *comp_dir; // compilation directory (DW_AT_comp_dir) + Module *module; + Dwarf_Half version; + Dwarf_Small address_size; + Dwr_Tag dwrTag; + DwrLineRegs *dwrLineReg; + DbeArray<DwrAbbrevTable> *abbrevTable; + DbeArray<Dwr_Attr> *abbrevAtForm; +}; + +#endif /* _DWARFLIB_H_ */ diff --git a/gprofng/src/Elf.cc b/gprofng/src/Elf.cc new file mode 100644 index 0000000..4117cf2 --- /dev/null +++ b/gprofng/src/Elf.cc @@ -0,0 +1,1138 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <unistd.h> + +#include "util.h" +#include "bfd.h" +#include "elf-bfd.h" +#include "Elf.h" +#include "Map.h" +#include "StringBuilder.h" +#include "DbeFile.h" + +typedef uint32_t Elf32_Word; +typedef uint32_t Elf64_Word; +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; +typedef uint64_t Elf64_Xword; +typedef int32_t Elf32_Sword; +typedef int64_t Elf64_Sxword; +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +// Ancillary entry +typedef struct +{ + Elf32_Word a_tag; /* how to interpret value */ + union + { + Elf32_Word a_val; + Elf32_Addr a_ptr; + } a_un; +} Elf32_Ancillary; + +struct S_Elf64_Ancillary +{ + Elf64_Xword a_tag; /* how to interpret value */ + union + { + Elf64_Xword a_val; + Elf64_Addr a_ptr; + } a_un; +}; + +/* Dynamic section entry. */ +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +struct S_Elf64_Dyn +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +}; + + +// Symbol table +typedef struct +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; /* bind, type: ELF_32_ST_... */ + unsigned char st_other; + Elf32_Half st_shndx; /* SHN_... */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; + unsigned char st_info; /* bind, type: ELF_64_ST_... */ + unsigned char st_other; + Elf64_Half st_shndx; /* SHN_... */ + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + + +// Relocation +typedef struct +{ + Elf32_Addr r_offset; + Elf32_Word r_info; /* sym, type: ELF32_R_... */ +} Elf32_Rel; + +typedef struct +{ + Elf32_Addr r_offset; + Elf32_Word r_info; /* sym, type: ELF32_R_... */ + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; + Elf64_Xword r_info; /* sym, type: ELF64_R_... */ +} Elf64_Rel; + +typedef struct +{ + Elf64_Addr r_offset; + Elf64_Xword r_info; /* sym, type: ELF64_R_... */ + Elf64_Sxword r_addend; +} Elf64_Rela; + +int Elf::bfd_status = -1; + +void +Elf::elf_init () +{ + if (bfd_status == -1) + bfd_status = bfd_init (); +} + +Elf::Elf (char *filename) : DbeMessages (), Data_window (filename) +{ + ehdrp = NULL; + data = NULL; + ancillary_files = NULL; + elfSymbols = NULL; + gnu_debug_file = NULL; + dbeFile = NULL; + abfd = NULL; + if (bfd_status != BFD_INIT_MAGIC) + { + status = ELF_ERR_CANT_OPEN_FILE; + return; + } + abfd = bfd_openr (filename, NULL); + if (abfd == NULL) + { + status = ELF_ERR_CANT_OPEN_FILE; + return; + } + if (!bfd_check_format (abfd, bfd_object)) + { + bfd_close (abfd); + abfd = NULL; + status = ELF_ERR_CANT_OPEN_FILE; + return; + } + ehdrp = elf_getehdr (); + if (ehdrp == NULL) + { + bfd_close (abfd); + abfd = NULL; + status = ELF_ERR_BAD_ELF_FORMAT; + return; + } + elf_class = ehdrp->e_ident[EI_CLASS]; + elf_datatype = ehdrp->e_ident[EI_DATA]; + + if (not_opened ()) + { + status = ELF_ERR_CANT_OPEN_FILE; + return; + } + status = ELF_ERR_NONE; + +#if ARCH(SPARC) + need_swap_endian = is_Intel (); +#else + need_swap_endian = !is_Intel (); +#endif + + analyzerInfo = 0; + SUNW_ldynsym = 0; + gnuLink = 0; + stab = 0; + stabStr = 0; + stabIndex = 0; + stabIndexStr = 0; + stabExcl = 0; + stabExclStr = 0; + symtab = 0; + dynsym = 0; + info = 0; + plt = 0; + dwarf = false; + + for (unsigned int sec = 1; sec < elf_getehdr ()->e_shnum; sec++) + { + char *name = get_sec_name (sec); + if (name == NULL) + continue; + if (streq (name, NTXT (".stab"))) + stab = sec; + else if (streq (name, NTXT (".stabstr"))) + stabStr = sec; + else if (streq (name, NTXT (".stab.index"))) + stabIndex = sec; + else if (streq (name, NTXT (".stab.indexstr"))) + stabIndexStr = sec; + else if (streq (name, NTXT (".stab.excl"))) + stabExcl = sec; + else if (streq (name, NTXT (".stab.exclstr"))) + stabExclStr = sec; + else if (streq (name, NTXT (".gnu_debuglink"))) + gnuLink = sec; + else if (streq (name, NTXT (".__analyzer_info"))) + analyzerInfo = sec; + else if (streq (name, NTXT (".info"))) + info = true; + else if (streq (name, NTXT (".plt"))) + plt = sec; + else if (streq (name, NTXT (".SUNW_ldynsym"))) + SUNW_ldynsym = sec; + else if (streq (name, NTXT (".dynsym"))) + dynsym = sec; + else if (streq (name, NTXT (".symtab"))) + symtab = sec; + else if (strncmp (name, NTXT (".debug"), 6) == 0) + dwarf = true; + } + if (fd != -1) + { + close (fd); + fd = -1; + } +} + +Elf::~Elf () +{ + if (data) + { + for (int i = 0; i < (int) ehdrp->e_shnum; i++) + { + Elf_Data *p = data[i]; + if (p && !mmap_on_file && (p->d_flags & SHF_SUNW_ABSENT) == 0) + free (p->d_buf); + delete p; + } + free (data); + } + if (ancillary_files) + { + ancillary_files->destroy (); + delete ancillary_files; + } + delete elfSymbols; + delete gnu_debug_file; + delete dbeFile; + if (abfd) + bfd_close (abfd); +} + +Elf_Internal_Ehdr * +Elf::elf_getehdr () +{ + if (ehdrp == NULL && abfd) + ehdrp = elf_elfheader (abfd); + return ehdrp; +} + +Elf_Internal_Phdr * +Elf::get_phdr (unsigned int ndx) +{ + if (ehdrp == NULL || ndx >= ehdrp->e_phnum) + return NULL; + return &(elf_tdata (abfd)->phdr[ndx]); +} + +Elf_Internal_Shdr * +Elf::get_shdr (unsigned int ndx) +{ + if (ehdrp == NULL || ndx >= ehdrp->e_shnum) + return NULL; + return elf_elfsections (abfd)[ndx]; +} + +Elf64_Dyn * +Elf::elf_getdyn (Elf_Internal_Phdr *phdr, unsigned int ndx, Elf64_Dyn *pdyn) +{ + if (elf_getclass () == ELFCLASS32) + { + if (ndx * sizeof (Elf32_Dyn) >= phdr->p_filesz) + return NULL; + Elf32_Dyn *hdr = (Elf32_Dyn*) bind (phdr->p_offset + ndx * sizeof (Elf32_Dyn), + sizeof (Elf32_Dyn)); + if (hdr == NULL) + return NULL; + pdyn->d_tag = decode (hdr->d_tag); + pdyn->d_un.d_val = decode (hdr->d_un.d_val); + } + else + { + if (ndx * sizeof (Elf64_Dyn) >= phdr->p_filesz) + return NULL; + Elf64_Dyn *hdr = (Elf64_Dyn*) bind (phdr->p_offset + ndx * sizeof (Elf64_Dyn), + sizeof (Elf64_Dyn)); + if (hdr == NULL) + return NULL; + pdyn->d_tag = decode (hdr->d_tag); + pdyn->d_un.d_val = decode (hdr->d_un.d_val); + } + return pdyn; +} + +unsigned +Elf::elf_version (unsigned ver) +{ + // We compile locally, no need to check the version + return ver; +} + +Elf * +Elf::elf_begin (char *fname, Elf_status *stp) +{ + if (fname == NULL) + { + if (stp) + *stp = ELF_ERR_CANT_OPEN_FILE; + return NULL; + } + Elf *elf = new Elf (fname); + if (stp) + *stp = elf->status; + if (elf->status != ELF_ERR_NONE) + { + delete elf; + return NULL; + } +#if DEBUG + if (DUMP_ELF_SEC) + { + char *str = elf->dump (); + fprintf (stderr, NTXT ("%s\n\n"), str); + free (str); + } +#endif /* DEBUG */ + return elf; +} + +unsigned int +Elf::elf_get_sec_num (const char *name) +{ + if (name == NULL || ehdrp == NULL) + return 0; + for (unsigned int sec = 1; sec < ehdrp->e_shnum; sec++) + { + Elf_Internal_Shdr *shdr = get_shdr (sec); + if (shdr == NULL) + continue; + char *sname = elf_strptr (ehdrp->e_shstrndx, shdr->sh_name); + if (sname != NULL && strcmp (name, sname) == 0) + return sec; + } + return 0; +} + +char * +Elf::get_sec_name (unsigned int sec) +{ + Elf_Internal_Shdr *shdr = get_shdr (sec); + if (ehdrp == NULL || shdr == NULL) + return NULL; + return elf_strptr (ehdrp->e_shstrndx, shdr->sh_name); +} + +Elf_Data * +Elf::elf_getdata (unsigned int sec) +{ + if (data == NULL) + { + data = (Elf_Data **) malloc (ehdrp->e_shnum * sizeof (Elf_Data *)); + for (int i = 0; i < (int) ehdrp->e_shnum; i++) + data[i] = NULL; + } + Elf_Data *edta = data[sec]; + if (edta == NULL) + { + Elf_Internal_Shdr *shdr = get_shdr (sec); + if (shdr == NULL) + return NULL; + edta = new Elf_Data; + data[sec] = edta; + if ((shdr->sh_flags & SHF_SUNW_ABSENT) != 0) + { + char *sname = get_sec_name (sec); + for (int i = 0, sz = VecSize(ancillary_files); i < sz; i++) + { + Elf *ancElf = ancillary_files->fetch (i); + int secNum = sec; + if (dbe_strcmp (sname, ancElf->get_sec_name (sec)) != 0) + { + append_msg (CMSG_WARN, + "Warning: the section #%d (%s) is mismatch in ancillary file '%s')\n", + sec, STR (sname), STR (ancElf->fname)); + secNum = ancElf->elf_get_sec_num (sname); + } + if (secNum > 0) + { + Elf_Data *ed = ancElf->elf_getdata (secNum); + if (ed && ed->d_buf) + { + *edta = *ed; + edta->d_flags |= SHF_SUNW_ABSENT; + return edta; + } + } + } + } + edta->d_buf = get_data (shdr->sh_offset, (size_t) shdr->sh_size, NULL); + edta->d_flags = shdr->sh_flags; + edta->d_size = ((edta->d_buf == NULL) || (shdr->sh_type == SHT_NOBITS)) ? 0 : shdr->sh_size; + edta->d_off = shdr->sh_offset; + edta->d_align = shdr->sh_addralign; + } + return edta; +} + +int64_t +Elf::elf_checksum () +{ + if (ehdrp == NULL) + return 0; + int64_t chk = 0; + for (unsigned int ndx = 0; ndx < ehdrp->e_phnum; ndx++) + { + Elf_Internal_Phdr *phdr = get_phdr (ndx); + if (phdr == NULL) + continue; + if (phdr->p_type == PT_DYNAMIC) + { + Elf64_Dyn edyn; + for (unsigned int i = 0; elf_getdyn (phdr, i, &edyn) != NULL; i++) + { + if (!edyn.d_tag) + break; + if (edyn.d_tag == DT_CHECKSUM) + { + chk = edyn.d_un.d_val; + break; + } + } + } + } + return normalize_checksum (chk); +} + +uint64_t +Elf::get_baseAddr () +{ + uint64_t addr = 0; + for (unsigned int pnum = 0; pnum < elf_getehdr ()->e_phnum; pnum++) + { + Elf_Internal_Phdr *phdr = get_phdr (pnum); + if (phdr->p_type == PT_LOAD && phdr->p_flags == (PF_R | PF_X)) + { + if (addr == 0) + addr = phdr->p_vaddr; + else + { + addr = 0; + break; + } + } + } + return addr; +} + +char * +Elf::elf_strptr (unsigned int sec, uint64_t off) +{ + Elf_Data *edta = elf_getdata (sec); + if (edta && edta->d_buf && edta->d_size > off) + return ((char *) edta->d_buf) + off; + return NULL; +} + +Elf_Internal_Sym * +Elf::elf_getsym (Elf_Data *edta, unsigned int ndx, Elf_Internal_Sym *dst) +{ + if (dst == NULL || edta == NULL) + return NULL; + if (elf_getclass () == ELFCLASS32) + { + if (edta->d_size <= ndx * sizeof (Elf32_Sym)) + return NULL; + Elf32_Sym *hdr = (Elf32_Sym*) bind (edta->d_off + ndx * sizeof (Elf32_Sym), sizeof (Elf32_Sym)); + if (hdr == NULL) + return NULL; + dst->st_name = decode (hdr->st_name); + dst->st_value = decode (hdr->st_value); + dst->st_size = decode (hdr->st_size); + dst->st_info = ELF64_ST_INFO (ELF32_ST_BIND (decode (hdr->st_info)), + ELF32_ST_TYPE (decode (hdr->st_info))); + dst->st_other = decode (hdr->st_other); + dst->st_shndx = decode (hdr->st_shndx); + } + else + { + if (edta->d_size <= ndx * sizeof (Elf64_Sym)) + return NULL; + Elf64_Sym *hdr = (Elf64_Sym*) bind (edta->d_off + ndx * sizeof (Elf64_Sym), + sizeof (Elf64_Sym)); + if (hdr == NULL) + return NULL; + dst->st_name = decode (hdr->st_name); + dst->st_value = decode (hdr->st_value); + dst->st_size = decode (hdr->st_size); + dst->st_info = decode (hdr->st_info); + dst->st_other = decode (hdr->st_other); + dst->st_shndx = decode (hdr->st_shndx); + } + return dst; +} + +Elf_Internal_Rela * +Elf::elf_getrel (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst) +{ + if (dst == NULL || edta == NULL || edta->d_buf == NULL) + return NULL; + if (elf_getclass () == ELFCLASS32) + { + Elf32_Rel *rel = ((Elf32_Rel *) edta->d_buf) + ndx; + dst->r_offset = decode (rel->r_offset); + dst->r_info = ELF64_R_INFO (ELF32_R_SYM (decode (rel->r_info)), + ELF32_R_TYPE (decode (rel->r_info))); + } + else + { + Elf64_Rel *rel = ((Elf64_Rel *) edta->d_buf) + ndx; + dst->r_offset = decode (rel->r_offset); + dst->r_info = decode (rel->r_info); + } + return dst; +} + +Elf_Internal_Rela * +Elf::elf_getrela (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst) +{ + if (dst == NULL || edta == NULL || edta->d_buf == NULL) + return NULL; + if (elf_getclass () == ELFCLASS32) + { + Elf32_Rela *rela = ((Elf32_Rela *) edta->d_buf) + ndx; + dst->r_offset = decode (rela->r_offset); + dst->r_addend = decode (rela->r_addend); + dst->r_info = ELF64_R_INFO (ELF32_R_SYM (decode (rela->r_info)), + ELF32_R_TYPE (decode (rela->r_info))); + } + else + { + Elf64_Rela *rela = ((Elf64_Rela *) edta->d_buf) + ndx; + dst->r_offset = decode (rela->r_offset); + dst->r_addend = decode (rela->r_addend); + dst->r_info = decode (rela->r_info); + } + return dst; +} + +Elf64_Ancillary * +Elf::elf_getancillary (Elf_Data *edta, unsigned int ndx, Elf64_Ancillary *dst) +{ + if (dst == NULL || edta == NULL || edta->d_buf == NULL) + return NULL; + if (elf_getclass () == ELFCLASS32) + { + Elf32_Ancillary *p = ((Elf32_Ancillary *) edta->d_buf) + ndx; + dst->a_tag = decode (p->a_tag); + dst->a_un.a_val = decode (p->a_un.a_val); + } + else + { + Elf64_Ancillary *p = ((Elf64_Ancillary *) edta->d_buf) + ndx; + dst->a_tag = decode (p->a_tag); + dst->a_un.a_val = decode (p->a_un.a_val); + } + return dst; +} + +Elf * +Elf::get_related_file (const char *lo_name, const char *nm) +{ + DbeFile *df; + if (*nm == '/') + { + df = new DbeFile (nm); + df->filetype |= (DbeFile::F_FILE | DbeFile::F_DEBUG_FILE); + } + else + { + char *bname = get_basename (lo_name); + char *fnm = dbe_sprintf ("%.*s/%s", (int) (bname - lo_name), lo_name, nm); + df = new DbeFile (fnm); + df->filetype |= (DbeFile::F_FILE | DbeFile::F_DEBUG_FILE); + free (fnm); + } + Dprintf (DEBUG_STABS, "get_related_file: %s -> '%s'\n", nm, df->get_name ()); + Elf_status st = ELF_ERR_CANT_OPEN_FILE; + Elf *elf = elf_begin (df->get_location (), &st); + if (elf) + { + elf->dbeFile = df; + return elf; + } + switch (st) + { + case ELF_ERR_CANT_OPEN_FILE: + append_msg (CMSG_ERROR, GTXT ("Cannot open file `%s'"), df->get_name ()); + break; + case ELF_ERR_BAD_ELF_FORMAT: + default: + append_msg (CMSG_ERROR, GTXT ("Cannot read ELF header of `%s'"), + df->get_name ()); + break; + } + delete df; + return NULL; +} + +Elf * +Elf::find_ancillary_files (char *lo_name) +{ + // read the .gnu_debuglink and .SUNW_ancillary seections + if (gnu_debug_file) + return gnu_debug_file; + unsigned int sec = elf_get_sec_num (NTXT (".gnu_debuglink")); + if (sec > 0) + { + Elf_Data *dp = elf_getdata (sec); + if (dp) + { + gnu_debug_file = get_related_file (lo_name, (char *) (dp->d_buf)); + if (gnu_debug_file) + return gnu_debug_file; + } + } + + sec = elf_get_sec_num (NTXT (".SUNW_ancillary")); + if (sec > 0) + { + Elf_Internal_Shdr *shdr = get_shdr (sec); + uint64_t check_sum = 0; + char *ancName = NULL; + if (shdr) + { + Elf_Data *dp = elf_getdata (sec); + for (int i = 0, sz = (int) (shdr->sh_size / shdr->sh_entsize); + i < sz; i++) + { + Elf64_Ancillary anc; + if (elf_getancillary (dp, i, &anc) == NULL + || anc.a_tag == ANC_SUNW_NULL) + break; + if (anc.a_tag == ANC_SUNW_MEMBER) + ancName = elf_strptr (shdr->sh_link, anc.a_un.a_ptr); + else if (anc.a_tag == ANC_SUNW_CHECKSUM) + { + if (i == 0) + { + check_sum = anc.a_un.a_val; + continue; + } + if (check_sum == anc.a_un.a_val) + ancName = NULL; + if (ancName) + { + Elf *ancElf = get_related_file (lo_name, ancName); + if (ancElf == NULL) + continue; + int ancSec = ancElf->elf_get_sec_num (".SUNW_ancillary"); + if (ancSec > 0) + { + Elf_Internal_Shdr *ancHdr = ancElf->get_shdr (ancSec); + if (ancHdr) + { + Elf_Data *anc_dp = ancElf->elf_getdata (ancSec); + Elf64_Ancillary anc1; + if (ancElf->elf_getancillary (anc_dp, 0, &anc1) + && (anc1.a_tag == ANC_SUNW_CHECKSUM) && + anc1.a_un.a_val == anc.a_un.a_val) + { + if (ancillary_files == NULL) + ancillary_files = new Vector<Elf*>(2); + ancillary_files->append (ancElf); + } + else + append_msg (CMSG_WARN, GTXT ("Load Object: '%s' (checksum Ox%lld). The .anc file '%s' has checksum Ox%llx"), + STR (fname), (long long) check_sum, + STR (ancElf->dbeFile->get_location ()), + (long long) anc1.a_un.a_val); + } + } + ancName = NULL; + } + } + } + } + } + return NULL; +} + +char* +Elf::get_location () +{ + return dbeFile ? dbeFile->get_location () : fname; +} + +#define RET_S(x) if (t == x) return (char *) #x + +static char * +get_elf_class_name (int t) +{ + RET_S (ELFCLASSNONE); + RET_S (ELFCLASS32); + RET_S (ELFCLASS64); + return NTXT ("ELFCLASS_UNKNOWN"); +} + +static char * +get_elf_data_name (int t) +{ + RET_S (ELFDATANONE); + RET_S (ELFDATA2LSB); + RET_S (ELFDATA2MSB); + return NTXT ("ELFDATA_UNKNOWN"); +} + +static char * +get_elf_osabi_name (int t) +{ + RET_S (ELFOSABI_NONE); + RET_S (ELFOSABI_HPUX); + RET_S (ELFOSABI_NETBSD); + RET_S (ELFOSABI_LINUX); + RET_S (ELFOSABI_SOLARIS); + RET_S (ELFOSABI_AIX); + RET_S (ELFOSABI_IRIX); + RET_S (ELFOSABI_FREEBSD); + RET_S (ELFOSABI_TRU64); + RET_S (ELFOSABI_MODESTO); + RET_S (ELFOSABI_OPENBSD); + return NTXT ("ELFOSABI_UNKNOWN"); +} + +static char * +get_elf_etype_name (int t) +{ + RET_S (ET_NONE); + RET_S (ET_REL); + RET_S (ET_EXEC); + RET_S (ET_DYN); + RET_S (ET_CORE); + RET_S (ET_LOPROC); + RET_S (ET_HIPROC); + return NTXT ("ETYPE_UNKNOWN"); +} + +static char * +get_elf_ptype_name (int t) +{ + RET_S (PT_NULL); + RET_S (PT_LOAD); + RET_S (PT_DYNAMIC); + RET_S (PT_INTERP); + RET_S (PT_NOTE); + RET_S (PT_SHLIB); + RET_S (PT_PHDR); + RET_S (PT_TLS); + RET_S (PT_LOOS); + RET_S (PT_GNU_EH_FRAME); + RET_S (PT_GNU_EH_FRAME); + RET_S (PT_HIOS); + RET_S (PT_LOPROC); + RET_S (PT_HIPROC); + return NTXT ("PTYPE_UNKNOWN"); +} + +static char * +get_elf_shtype_name (unsigned int t) +{ + RET_S (SHT_NULL); + RET_S (SHT_PROGBITS); + RET_S (SHT_SYMTAB); + RET_S (SHT_STRTAB); + RET_S (SHT_RELA); + RET_S (SHT_HASH); + RET_S (SHT_DYNAMIC); + RET_S (SHT_NOTE); + RET_S (SHT_NOBITS); + RET_S (SHT_REL); + RET_S (SHT_SHLIB); + RET_S (SHT_DYNSYM); + RET_S (SHT_INIT_ARRAY); + RET_S (SHT_FINI_ARRAY); + RET_S (SHT_PREINIT_ARRAY); + RET_S (SHT_GROUP); + RET_S (SHT_SYMTAB_SHNDX); + RET_S (SHT_LOOS); + RET_S (SHT_SUNW_verdef); + RET_S (SHT_SUNW_verneed); + RET_S (SHT_HIOS); + RET_S (SHT_LOPROC); + RET_S (SHT_HIPROC); + RET_S (SHT_LOUSER); + RET_S (SHT_HIUSER); + return NTXT ("SHTYPE_UNKNOWN"); +} + +static char * +get_elf_machine_name (int t) +{ + RET_S (EM_NONE); + RET_S (EM_M32); + RET_S (EM_SPARC); + RET_S (EM_386); + RET_S (EM_68K); + RET_S (EM_88K); + RET_S (EM_860); + RET_S (EM_MIPS); + RET_S (EM_S370); + RET_S (EM_MIPS_RS3_LE); + RET_S (EM_SPARC32PLUS); + RET_S (EM_960); + RET_S (EM_PPC); + RET_S (EM_PPC64); + RET_S (EM_V800); + RET_S (EM_FR20); + RET_S (EM_RH32); + RET_S (EM_RCE); + RET_S (EM_ARM); + RET_S (EM_ALPHA); + RET_S (EM_SH); + RET_S (EM_SPARCV9); + RET_S (EM_TRICORE); + RET_S (EM_ARC); + RET_S (EM_H8_300); + RET_S (EM_H8_300H); + RET_S (EM_H8S); + RET_S (EM_H8_500); + RET_S (EM_IA_64); + RET_S (EM_MIPS_X); + RET_S (EM_COLDFIRE); + RET_S (EM_68HC12); + RET_S (EM_MMA); + RET_S (EM_PCP); + RET_S (EM_NCPU); + RET_S (EM_NDR1); + RET_S (EM_STARCORE); + RET_S (EM_ME16); + RET_S (EM_ST100); + RET_S (EM_TINYJ); + RET_S (EM_X86_64); + RET_S (EM_PDSP); + RET_S (EM_FX66); + RET_S (EM_ST9PLUS); + RET_S (EM_ST7); + RET_S (EM_68HC16); + RET_S (EM_68HC11); + RET_S (EM_68HC08); + RET_S (EM_68HC05); + RET_S (EM_SVX); + RET_S (EM_ST19); + RET_S (EM_VAX); + RET_S (EM_CRIS); + RET_S (EM_JAVELIN); + RET_S (EM_FIREPATH); + RET_S (EM_ZSP); + RET_S (EM_MMIX); + RET_S (EM_HUANY); + RET_S (EM_PRISM); + RET_S (EM_AVR); + RET_S (EM_FR30); + RET_S (EM_D10V); + RET_S (EM_D30V); + RET_S (EM_V850); + RET_S (EM_M32R); + RET_S (EM_MN10300); + RET_S (EM_MN10200); + RET_S (EM_PJ); + RET_S (EM_OPENRISC); + RET_S (EM_XTENSA); + return NTXT ("ELFMACHINE_UNKNOWN"); +} + +static char * +get_elf_version_name (int t) +{ + RET_S (EV_NONE); + RET_S (EV_CURRENT); + return NTXT ("VERSION_UNKNOWN"); +} + +static char * +get_elf_ancillary_tag (int t) +{ + RET_S (ANC_SUNW_NULL); + RET_S (ANC_SUNW_CHECKSUM); + RET_S (ANC_SUNW_MEMBER); + RET_S (ANC_SUNW_NUM); + return NTXT ("ANCILLARY_TAG_UNKNOWN"); +} + +#define ADD_S(x) if ((f & (x)) == (x)) { sb->append(' '); sb->append(#x); f &= ~(x); } + +static void +dump_sh_flags (StringBuilder *sb, long long flags) +{ + long long f = flags; + if (f != 0) + { + sb->append (NTXT (" [")); + ADD_S (SHF_WRITE) + ADD_S (SHF_ALLOC) + ADD_S (SHF_EXECINSTR) + ADD_S (SHF_MERGE) + ADD_S (SHF_STRINGS) + ADD_S (SHF_INFO_LINK) + ADD_S (SHF_LINK_ORDER) + ADD_S (SHF_OS_NONCONFORMING) + ADD_S (SHF_GROUP) + ADD_S (SHF_TLS) + ADD_S (SHF_SUNW_ABSENT) + ADD_S (SHF_EXCLUDE) + if (f != 0 && f != flags) + sb->appendf (NTXT (" 0x%llx"), (long long) f); + sb->append (NTXT (" ]")); + } + sb->append (NTXT ("\n")); +} + +static void +dump_p_flags (StringBuilder *sb, long long flags) +{ + long long f = flags; + if (f != 0) + { + sb->append (NTXT (" [")); + ADD_S (PF_X) + ADD_S (PF_W) + ADD_S (PF_R) + ADD_S (PF_MASKPROC) + if (f != 0 && f != flags) + sb->appendf (NTXT (" 0x%llx"), (long long) f); + sb->append (NTXT (" ]")); + } + sb->append (NTXT ("\n")); +} + +char * +Elf::dump () +{ + StringBuilder sb; + sb.sprintf (NTXT ("ELF Header: %s\n"), fname ? fname : GTXT ("(unknown)")); + if (ehdrp == NULL) + { + sb.appendf (GTXT ("\n\n Cannot read Elf header\n")); + return sb.toString (); + } + sb.appendf (NTXT (" %-15s "), NTXT ("e_ident")); + for (int i = 0; i < EI_NIDENT; i++) + sb.appendf (NTXT ("%x"), ehdrp->e_ident[i]); + sb.append (NTXT ("\n")); + char *fmt0 = NTXT (" %-15s %10lld ( %s )\n"); + char *fmt1 = NTXT (" %-15s 0x%08llx ( %lld )\n"); + char *fmt2 = NTXT (" %-15s 0x%08llx"); + sb.appendf (fmt0, NTXT ("EI_CLASS"), (long long) ehdrp->e_ident[EI_CLASS], + get_elf_class_name (ehdrp->e_ident[EI_CLASS])); + sb.appendf (fmt0, NTXT ("EI_DATA"), (long long) ehdrp->e_ident[EI_DATA], + get_elf_data_name (ehdrp->e_ident[EI_DATA])); + sb.appendf (fmt0, NTXT ("EI_OSABI"), (long long) ehdrp->e_ident[EI_OSABI], + get_elf_osabi_name (ehdrp->e_ident[EI_OSABI])); + sb.appendf (fmt0, NTXT ("e_type"), (long long) ehdrp->e_type, + get_elf_etype_name (ehdrp->e_type)); + sb.appendf (fmt0, NTXT ("e_machine"), (long long) ehdrp->e_machine, + get_elf_machine_name (ehdrp->e_machine)); + sb.appendf (fmt0, NTXT ("e_version"), (long long) ehdrp->e_version, + get_elf_version_name (ehdrp->e_version)); + sb.appendf (fmt1, NTXT ("e_entry"), (long long) ehdrp->e_entry, + (long long) ehdrp->e_entry); + sb.appendf (fmt1, NTXT ("e_phoff"), (long long) ehdrp->e_phoff, + (long long) ehdrp->e_phoff); + sb.appendf (fmt1, NTXT ("e_shoff"), (long long) ehdrp->e_shoff, + (long long) ehdrp->e_shoff); + sb.appendf (fmt1, NTXT ("e_flags"), (long long) ehdrp->e_flags, + (long long) ehdrp->e_flags); + sb.appendf (fmt1, NTXT ("e_ehsize"), (long long) ehdrp->e_ehsize, + (long long) ehdrp->e_ehsize); + sb.appendf (fmt1, NTXT ("e_phentsize"), (long long) ehdrp->e_phentsize, + (long long) ehdrp->e_phentsize); + sb.appendf (fmt1, NTXT ("e_phnum"), (long long) ehdrp->e_phnum, + (long long) ehdrp->e_phnum); + sb.appendf (fmt1, NTXT ("e_shentsize"), (long long) ehdrp->e_shentsize, + (long long) ehdrp->e_shentsize); + sb.appendf (fmt1, NTXT ("e_shnum"), (long long) ehdrp->e_shnum, + (long long) ehdrp->e_shnum); + sb.appendf (fmt1, NTXT ("e_shstrndx"), (long long) ehdrp->e_shstrndx, + (long long) ehdrp->e_shstrndx); + + for (unsigned int i = 0; i < ehdrp->e_phnum; i++) + { + sb.appendf (NTXT ("\nProgram Header[%d]:\n"), i); + Elf_Internal_Phdr *phdr = get_phdr (i); + if (phdr == NULL) + { + sb.appendf (NTXT (" ERROR: get_phdr(%d) failed\n"), i); + continue; + } + sb.appendf (fmt0, "p_type", (long long) phdr->p_type, + get_elf_ptype_name (phdr->p_type)); + sb.appendf (fmt2, "p_flags", (long long) phdr->p_flags); + dump_p_flags (&sb, phdr->p_flags); + sb.appendf (fmt1, "p_offset", (long long) phdr->p_offset, + (long long) phdr->p_offset); + sb.appendf (fmt1, "p_vaddr", (long long) phdr->p_vaddr, + (long long) phdr->p_vaddr); + sb.appendf (fmt1, "p_paddr", (long long) phdr->p_paddr, + (long long) phdr->p_paddr); + sb.appendf (fmt1, "p_filesz", (long long) phdr->p_filesz, + (long long) phdr->p_filesz); + sb.appendf (fmt1, "p_memsz", (long long) phdr->p_memsz, + (long long) phdr->p_memsz); + sb.appendf (fmt1, "p_align", (long long) phdr->p_align, + (long long) phdr->p_align); + } + + for (unsigned int i = 1; i < ehdrp->e_shnum; i++) + { + sb.appendf (NTXT ("\nSection Header[%d]:\n"), i); + Elf_Internal_Shdr *shdr = get_shdr (i); + if (shdr == NULL) + { + sb.appendf (NTXT (" ERROR: get_shdr(%d) failed\n"), i); + continue; + } + char *s = get_sec_name (i); + sb.appendf (fmt0, "sh_name", (long long) shdr->sh_name, + s ? s : NTXT ("NULL")); + sb.appendf (fmt0, "sh_type", (long long) shdr->sh_type, + get_elf_shtype_name (shdr->sh_type)); + sb.appendf (fmt2, "sh_flags", (long long) shdr->sh_flags); + dump_sh_flags (&sb, shdr->sh_flags); + sb.appendf (fmt1, "sh_addr", (long long) shdr->sh_addr, + (long long) shdr->sh_addr); + sb.appendf (fmt1, "sh_offset", (long long) shdr->sh_offset, + (long long) shdr->sh_offset); + sb.appendf (fmt1, "sh_size", (long long) shdr->sh_size, + (long long) shdr->sh_size); + sb.appendf (fmt1, "sh_link", (long long) shdr->sh_link, + (long long) shdr->sh_link); + sb.appendf (fmt1, "sh_info", (long long) shdr->sh_info, + (long long) shdr->sh_info); + sb.appendf (fmt1, "sh_addralign", (long long) shdr->sh_addralign, + (long long) shdr->sh_addralign); + sb.appendf (fmt1, "sh_entsize", (long long) shdr->sh_entsize, + (long long) shdr->sh_entsize); + } + + for (unsigned int i = 1; i < ehdrp->e_shnum; i++) + { + Elf_Internal_Shdr *shdr = get_shdr (i); + if (shdr == NULL) + continue; + char *secName = get_sec_name (i); + if (secName == NULL) + continue; + if (strcmp (NTXT (".SUNW_ancillary"), secName) == 0) + { + sb.appendf (NTXT ("\nSection[%d]: %s\n"), i, secName); + Elf_Data *dp = elf_getdata (i); + for (int j = 0, cnt = (int) (shdr->sh_size / shdr->sh_entsize); + j < cnt; j++) + { + Elf64_Ancillary anc; + if (elf_getancillary (dp, j, &anc) == NULL) + break; + sb.appendf (NTXT ("%10d %-20s 0x%08llx %6lld"), j, + get_elf_ancillary_tag ((int) anc.a_tag), + (long long) anc.a_un.a_ptr, (long long) anc.a_un.a_ptr); + if (anc.a_tag == ANC_SUNW_MEMBER) + sb.appendf (NTXT (" %s\n"), STR (elf_strptr (shdr->sh_link, anc.a_un.a_ptr))); + else + sb.append (NTXT ("\n")); + } + } + } + return sb.toString (); +} + +void +Elf::dump_elf_sec () +{ + if (!DUMP_ELF_SEC) + return; + if (ehdrp == NULL) + return; + Dprintf (DUMP_ELF_SEC, "======= DwarfLib::dump_elf_sec\n" + " N |type|flags| sh_addr | sh_offset | sh_size | sh_link |" + " sh_info | sh_addralign | sh_entsize | sh_name | name\n"); + for (unsigned int sec = 1; sec < ehdrp->e_shnum; sec++) + { + Elf_Internal_Shdr *shdr = get_shdr (sec); + if (shdr == NULL) + continue; + char *name = elf_strptr (ehdrp->e_shstrndx, shdr->sh_name); + Dprintf (DUMP_ELF_SEC, "%3d:%3d |%4d |%9lld | %9lld |%8lld |%8lld |" + "%8lld |%14d |%11lld | %6lld %s\n", + sec, (int) shdr->sh_type, (int) shdr->sh_flags, + (long long) shdr->sh_addr, (long long) shdr->sh_offset, + (long long) shdr->sh_size, (long long) shdr->sh_link, + (long long) shdr->sh_info, + (int) shdr->sh_addralign, (long long) shdr->sh_entsize, + (long long) shdr->sh_name, name ? name : NTXT ("NULL")); + } + Dprintf (DUMP_ELF_SEC, NTXT ("\n")); +} diff --git a/gprofng/src/Elf.h b/gprofng/src/Elf.h new file mode 100644 index 0000000..3648b88 --- /dev/null +++ b/gprofng/src/Elf.h @@ -0,0 +1,170 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _Elf_h_ +#define _Elf_h_ + +#include <string.h> +#include "ansidecl.h" +#include "bfd.h" +#include "elf/common.h" +#include "elf/internal.h" + +#include "Data_window.h" +#include "Emsg.h" + +class Symbol; +class DbeFile; +template <class ITEM> class Vector; +template <typename Key_t, typename Value_t> class Map; + +#define GELF_R_SYM(info) ((info)>>32) +#define GELF_ST_TYPE(info) ((info) & 0xf) +#define GELF_ST_BIND(info) ((info) >> 4) +#define GELF_R_TYPE(info) ((((uint64_t)(info))<<56)>>56) + +#define SHF_SUNW_ABSENT 0x00200000 /* section data not present */ + +// Ancillary values. +#define ANC_SUNW_NULL 0 +#define ANC_SUNW_CHECKSUM 1 /* elf checksum */ +#define ANC_SUNW_MEMBER 2 /* name of ancillary group object */ +#define ANC_SUNW_NUM 3 + + +typedef struct S_Elf64_Dyn Elf64_Dyn; +typedef struct S_Elf64_Ancillary Elf64_Ancillary; + +typedef struct +{ + void *d_buf; + uint64_t d_flags; + uint64_t d_size; + uint64_t d_off; // offset into section + uint64_t d_align; // alignment in section +} Elf_Data; + +class Elf : public DbeMessages, public Data_window +{ +public: + enum Elf_status + { + ELF_ERR_NONE, + ELF_ERR_CANT_OPEN_FILE, + ELF_ERR_CANT_MMAP, + ELF_ERR_BIG_FILE, + ELF_ERR_BAD_ELF_FORMAT, + ELF_ERR_READ_FILE + }; + + Elf (char *_fname); + ~Elf (); + + static void elf_init (); + static unsigned elf_version (unsigned ver); + static Elf *elf_begin (char *_fname, Elf_status *stp = NULL); + + unsigned int elf_get_sec_num (const char *sec_name); + char *get_sec_name (unsigned int sec); + Elf_Internal_Ehdr *elf_getehdr (); + Elf_Internal_Phdr *get_phdr (unsigned int ndx); + Elf_Internal_Shdr *get_shdr (unsigned int ndx); + Elf64_Dyn *elf_getdyn (Elf_Internal_Phdr *phdr, unsigned int ndx, Elf64_Dyn *pdyn); + Elf_Data *elf_getdata (unsigned int sec); + int64_t elf_checksum (); + uint64_t get_baseAddr(); + char *elf_strptr (unsigned int sec, uint64_t off); + Elf_Internal_Sym *elf_getsym (Elf_Data *edta, unsigned int ndx, Elf_Internal_Sym *dst); + Elf_Internal_Rela *elf_getrel (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst); + Elf_Internal_Rela *elf_getrela (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst); + Elf64_Ancillary *elf_getancillary (Elf_Data *edta, unsigned int ndx, Elf64_Ancillary *dst); + Elf *find_ancillary_files (char *lo_name); // read the .gnu_debuglink and .SUNW_ancillary seections + char *get_location (); + char *dump (); + void dump_elf_sec (); + + static inline int64_t + normalize_checksum (int64_t chk) + { + return (chk == 0xffffffff || chk == -1) ? 0 : chk; + }; + + inline bool + is_Intel () + { + return elf_datatype == ELFDATA2LSB; + }; + + inline int + elf_getclass () + { + return elf_class; + }; + + inline int + elf_getdatatype () + { + return elf_datatype; + }; + + Elf_status status; + Vector<Elf*> *ancillary_files; + Elf *gnu_debug_file; + DbeFile *dbeFile; + Map<const char*, Symbol*> *elfSymbols; + unsigned int gnuLink, analyzerInfo, SUNW_ldynsym, stab, stabStr, symtab, dynsym; + unsigned int stabIndex, stabIndexStr, stabExcl, stabExclStr, info, plt; + bool dwarf; + +protected: + Elf *get_related_file (const char *lo_name, const char *nm); + int elf_class; + int elf_datatype; + Elf_Internal_Ehdr *ehdrp; + Elf_Data **data; + bfd *abfd; + static int bfd_status; +}; + + +class ElfReloc +{ +public: + struct Sreloc + { + long long offset; + long long value; + int stt_type; + }; + + static ElfReloc *get_elf_reloc (Elf *elf, char *sec_name, ElfReloc *rlc); + ElfReloc (Elf *_elf); + ~ElfReloc (); + long long get_reloc_addr (long long offset); + void dump (); + void dump_rela_debug_sec (int sec); + +private: + Elf *elf; + Vector<Sreloc *> *reloc; + int cur_reloc_ind; +}; + +#endif diff --git a/gprofng/src/Emsg.cc b/gprofng/src/Emsg.cc new file mode 100644 index 0000000..11bad97 --- /dev/null +++ b/gprofng/src/Emsg.cc @@ -0,0 +1,614 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdarg.h> + +#include "util.h" +#include "Emsg.h" +#include "StringBuilder.h" + +// The Emsg, experiment message, has as objects I18N'd messages +// in a structure suitable for attaching to and fetching +// from a queue of such messages. It is intended to +// be used for collector errors, collector warnings, parser +// errors, and er_archive errors that are encountered when +// reading an experiment + +// ----------------------- Message -------------------------- + +Emsg::Emsg (Cmsg_warn w, const char *i18n_text) +{ + warn = w; + flavor = 0; + par = NULL; + text = strdup (i18n_text); + next = NULL; +} + +Emsg::Emsg (Cmsg_warn w, StringBuilder& sb) +{ + warn = w; + flavor = 0; + par = NULL; + text = sb.toString (); + next = NULL; +} + +Emsg::Emsg (Cmsg_warn w, int f, const char *param) +{ + char *type; + warn = w; + flavor = f; + if (param != NULL) + par = dbe_strdup (param); + else + par = dbe_strdup (""); + next = NULL; + + // determine type + switch (warn) + { + case CMSG_WARN: + type = GTXT ("*** Collector Warning"); + break; + case CMSG_ERROR: + type = GTXT ("*** Collector Error"); + break; + case CMSG_FATAL: + type = GTXT ("*** Collector Fatal Error"); + break; + case CMSG_COMMENT: + type = GTXT ("Comment"); + break; + case CMSG_PARSER: + type = GTXT ("*** Log Error"); + break; + case CMSG_ARCHIVE: + type = GTXT ("*** Archive Error"); + break; + default: + type = GTXT ("*** Internal Error"); + break; + }; + + // now convert the message to its I18N'd string + switch (flavor) + { + case COL_ERROR_NONE: + text = dbe_sprintf (GTXT ("%s: No error"), type); + break; + case COL_ERROR_ARGS2BIG: + text = dbe_sprintf (GTXT ("%s: Data argument too long"), type); + break; + case COL_ERROR_BADDIR: + text = dbe_sprintf (GTXT ("%s: Bad experiment directory name"), type); + break; + case COL_ERROR_ARGS: + text = dbe_sprintf (GTXT ("%s: Data argument format error `%s'"), type, par); + break; + case COL_ERROR_PROFARGS: + text = dbe_sprintf (GTXT ("%s: [UNUSED] Bad clock-profiling argument"), type); + break; + case COL_ERROR_SYNCARGS: + text = dbe_sprintf (GTXT ("%s: [UNUSED] Bad synchronization tracing argument"), type); + break; + case COL_ERROR_HWCARGS: + text = dbe_sprintf (GTXT ("%s: Bad hardware counter profiling argument"), type); + break; + case COL_ERROR_DIRPERM: + text = dbe_sprintf (GTXT ("%s: Experiment directory is not writeable; check umask and permissions"), type); + break; + case COL_ERROR_NOMSACCT: + text = dbe_sprintf (GTXT ("%s: Turning on microstate accounting failed"), type); + break; + case COL_ERROR_PROFINIT: + text = dbe_sprintf (GTXT ("%s: Initializing clock-profiling failed"), type); + break; + case COL_ERROR_SYNCINIT: + text = dbe_sprintf (GTXT ("%s: Initializing synchronization tracing failed"), type); + break; + case COL_ERROR_HWCINIT: + text = dbe_sprintf (GTXT ("%s: Initializing hardware counter profiling failed -- %s"), type, par); + break; + case COL_ERROR_HWCFAIL: + text = dbe_sprintf (GTXT ("%s: HW counter data collection failed; likely cause is that another process preempted the counters"), type); + break; + case COL_ERROR_EXPOPEN: + text = dbe_sprintf (GTXT ("%s: Experiment initialization failed, %s"), type, par); + break; + case COL_ERROR_SIZELIM: + text = dbe_sprintf (GTXT ("%s: Experiment size limit exceeded, writing %s"), type, par); + break; + case COL_ERROR_SYSINFO: + text = dbe_sprintf (GTXT ("%s: system name can not be determined"), type); + break; + case COL_ERROR_OVWOPEN: + text = dbe_sprintf (GTXT ("%s: Can't open overview %s"), type, par); + break; + case COL_ERROR_OVWWRITE: + text = dbe_sprintf (GTXT ("%s: Can't write overview %s"), type, par); + break; + case COL_ERROR_OVWREAD: + text = dbe_sprintf (GTXT ("%s: Can't read overview data for %s"), type, par); + break; + case COL_ERROR_NOZMEM: + text = dbe_sprintf (GTXT ("%s: Open of /dev/zero failed: %s"), type, par); + break; + case COL_ERROR_NOZMEMMAP: + text = dbe_sprintf (GTXT ("%s: Mmap of /dev/zero failed: %s"), type, par); + break; + case COL_ERROR_NOHNDL: + text = dbe_sprintf (GTXT ("%s: Out of data handles for %s"), type, par); + break; + case COL_ERROR_FILEOPN: + text = dbe_sprintf (GTXT ("%s: Open failed %s"), type, par); + break; + case COL_ERROR_FILETRNC: + text = dbe_sprintf (GTXT ("%s: Truncate failed for file %s"), type, par); + break; + case COL_ERROR_FILEMAP: + text = dbe_sprintf (GTXT ("%s: Mmap failed %s"), type, par); + break; + case COL_ERROR_HEAPINIT: + text = dbe_sprintf (GTXT ("%s: Initializing heap tracing failed"), type); + break; + case COL_ERROR_DISPINIT: + text = dbe_sprintf (GTXT ("%s: Initializing SIGPROF dispatcher failed"), type); + break; + case COL_ERROR_ITMRINIT: + text = dbe_sprintf (GTXT ("%s: Initializing interval timer failed; %s"), type, par); + break; + case COL_ERROR_SMPLINIT: + text = dbe_sprintf (GTXT ("%s: Initializing periodic sampling failed"), type); + break; + case COL_ERROR_MPIINIT: + text = dbe_sprintf (GTXT ("%s: Initializing MPI tracing failed"), type); + break; + case COL_ERROR_JAVAINIT: + text = dbe_sprintf (GTXT ("%s: Initializing Java profiling failed"), type); + break; + case COL_ERROR_LINEINIT: + text = dbe_sprintf (GTXT ("%s: Initializing descendant process lineage failed"), type); + break; + case COL_ERROR_NOSPACE: + text = dbe_sprintf (GTXT ("%s: Out of disk space writing `%s'"), type, par); + break; + case COL_ERROR_ITMRRST: + text = dbe_sprintf (GTXT ("%s: Resetting interval timer failed: %s"), type, par); + break; + case COL_ERROR_MKDIR: + text = dbe_sprintf (GTXT ("%s: Unable to create directory `%s'"), type, par); + break; + case COL_ERROR_JVM2NEW: + text = dbe_sprintf (GTXT ("%s: JVM version with JVMTI requires more recent release of the performance tools; please upgrade"), type); + break; + case COL_ERROR_JVMNOTSUPP: + text = dbe_sprintf (GTXT ("%s: JVM version does not support JVMTI; no java profiling is available"), type); + break; + case COL_ERROR_JVMNOJSTACK: + text = dbe_sprintf (GTXT ("%s: JVM version does not support java callstacks; java mode data will not be recorded"), type); + break; + case COL_ERROR_DYNOPEN: + text = dbe_sprintf (GTXT ("%s: Can't open dyntext file `%s'"), type, par); + break; + case COL_ERROR_DYNWRITE: + text = dbe_sprintf (GTXT ("%s: Can't write dyntext file `%s'"), type, par); + break; + case COL_ERROR_MAPOPEN: + text = dbe_sprintf (GTXT ("%s: Can't open map file `%s'"), type, par); + break; + case COL_ERROR_MAPREAD: + text = dbe_sprintf (GTXT ("%s: Can't read map file `%s'"), type, par); + break; + case COL_ERROR_MAPWRITE: + text = dbe_sprintf (GTXT ("%s: Can't write map file"), type); + break; + case COL_ERROR_RESOLVE: + text = dbe_sprintf (GTXT ("%s: Can't resolve map file `%s'"), type, par); + break; + case COL_ERROR_OMPINIT: + text = dbe_sprintf (GTXT ("%s: Initializing OpenMP tracing failed"), type); + break; + case COL_ERROR_DURATION_INIT: + text = dbe_sprintf (GTXT ("%s: Initializing experiment-duration setting to `%s' failed"), type, par); + break; + case COL_ERROR_RDTINIT: + text = dbe_sprintf (GTXT ("%s: Initializing RDT failed"), type); + break; + case COL_ERROR_GENERAL: + if (strlen (par)) + text = dbe_sprintf (GTXT ("%s: %s"), type, par); + else + text = dbe_sprintf (GTXT ("%s: General error"), type); + break; + case COL_ERROR_EXEC_FAIL: + text = dbe_sprintf (GTXT ("%s: Exec of process failed"), type); + break; + case COL_ERROR_THR_MAX: + text = dbe_sprintf (GTXT ("%s: Thread count exceeds maximum (%s); set SP_COLLECTOR_NUMTHREADS for higher value"), type, par); + break; + case COL_ERROR_IOINIT: + text = dbe_sprintf (GTXT ("%s: Initializing IO tracing failed"), type); + break; + case COL_ERROR_NODATA: + text = dbe_sprintf (GTXT ("%s: No data was recorded in the experiment"), type); + break; + case COL_ERROR_DTRACE_FATAL: + text = dbe_sprintf (GTXT ("%s: Fatal error reported from DTrace -- %s"), type, par); + break; + case COL_ERROR_MAPSEEK: + text = dbe_sprintf (GTXT ("%s: Seek error on map file `%s'"), type, par); + break; + case COL_ERROR_UNEXP_FOUNDER: + text = dbe_sprintf (GTXT ("%s: Unexpected value for founder `%s'"), type, par); + break; + case COL_ERROR_LOG_OPEN: + text = dbe_sprintf (GTXT ("%s: Failure to open log file"), type); + break; + case COL_ERROR_TSD_INIT: + text = dbe_sprintf (GTXT ("%s: TSD could not be initialized"), type); + break; + case COL_ERROR_UTIL_INIT: + text = dbe_sprintf (GTXT ("%s: libcol_util.c initialization failed"), type); + break; + case COL_ERROR_MAPCACHE: + text = dbe_sprintf (GTXT ("%s: Unable to cache mappings; internal error (`%s')"), type, par); + break; + case COL_WARN_NONE: + text = dbe_sprintf (GTXT ("%s: No warning"), type); + break; + case COL_WARN_FSTYPE: + text = dbe_sprintf (GTXT ("%s: Experiment was written to a filesystem of type `%s'; data may be distorted"), type, par); + break; + case COL_WARN_PROFRND: + text = dbe_sprintf (GTXT ("%s: Profiling interval was changed from requested %s (microsecs.) used"), type, par); + break; + case COL_WARN_SIZELIM: + text = dbe_sprintf (GTXT ("%s: Experiment size limit exceeded"), type); + break; + case COL_WARN_SIGPROF: + text = dbe_sprintf (GTXT ("%s: SIGPROF handler was changed (%s) during the run; profile data may be unreliable"), type, par); + break; + case COL_WARN_SMPLADJ: + text = dbe_sprintf (GTXT ("%s: Periodic sampling rate adjusted %s microseconds"), type, par); + break; + case COL_WARN_ITMROVR: + text = dbe_sprintf (GTXT ("%s: Application's attempt to set interval timer period to %s was ignored; its behavior may be changed"), type, par); + break; + case COL_WARN_ITMRREP: + text = dbe_sprintf (GTXT ("%s: Collection interval timer period was changed (%s); profile data may be unreliable"), type, par); + break; + case COL_WARN_SIGEMT: + text = dbe_sprintf (GTXT ("%s: SIGEMT handler was changed during the run; profile data may be unreliable"), type); + break; + case COL_WARN_CPCBLK: + text = dbe_sprintf (GTXT ("%s: libcpc access blocked for hardware counter profiling"), type); + break; + case COL_WARN_VFORK: + text = dbe_sprintf (GTXT ("%s: vfork(2) replaced by %s; execution may be affected"), type, par); + break; + case COL_WARN_EXECENV: + text = dbe_sprintf (GTXT ("%s: exec environment augmented with %s missing collection variables"), type, par); + break; + case COL_WARN_SAMPSIGUSED: + text = dbe_sprintf (GTXT ("%s: target installed handler for sample signal %s; samples may be lost"), type, par); + break; + case COL_WARN_PAUSESIGUSED: + text = dbe_sprintf (GTXT ("%s: target installed handler for pause/resume signal %s; data may be lost or unexpected"), + type, par); + break; + case COL_WARN_CPCNOTRESERVED: + text = dbe_sprintf (GTXT ("%s: unable to reserve HW counters; data may be distorted by other users of the counters"), type); + break; + case COL_WARN_LIBTHREAD_T1: /* par contains the aslwpid... do we want to report it? */ + text = dbe_sprintf (GTXT ("%s: application ran with a libthread version that may distort data; see collect(1) man page"), type); + break; + case COL_WARN_SIGMASK: + text = dbe_sprintf (GTXT ("%s: Blocking %s ignored while in use for collection"), type, par); + break; + case COL_WARN_NOFOLLOW: + text = dbe_sprintf (GTXT ("%s: Following disabled for uncollectable target (%s)"), type, par); + break; + case COL_WARN_RISKYFOLLOW: + text = dbe_sprintf (GTXT ("%s: Following unqualified target may be unreliable (%s)"), type, par); + break; + case COL_WARN_IDCHNG: + text = dbe_sprintf (GTXT ("%s: Imminent process ID change (%s) may result in an inconsistent experiment"), type, par); + break; + case COL_WARN_OLDJAVA: + text = dbe_sprintf (GTXT ("%s: Java profiling requires JVM version 1.4.2_02 or later"), type); + break; + case COL_WARN_ITMRPOVR: + text = dbe_sprintf (GTXT ("%s: Collector reset application's profile timer %s; application behavior may be changed"), type, par); + break; + case COL_WARN_NO_JAVA_HEAP: + text = dbe_sprintf (GTXT ("%s: Java heap profiling is not supported by JVMTI; disabled "), type); + break; + case COL_WARN_RDT_PAUSE_NOMEM: + text = dbe_sprintf (GTXT ("%s: Data race detection paused at %s because of running out of internal memory"), type, par); + break; + case COL_WARN_RDT_RESUME: + text = dbe_sprintf (GTXT ("%s: Data race detection resumed"), type); + break; + case COL_WARN_RDT_THROVER: + text = dbe_sprintf (GTXT ("%s: Too many concurrent/created threads; accesses with thread IDs above limit are not checked"), type); + break; + case COL_WARN_THR_PAUSE_RESUME: + text = dbe_sprintf (GTXT ("%s: The collector_thread_pause/collector_thread_resume APIs are deprecated, and will be removed in a future release"), type); + break; + case COL_WARN_NOPROF_DATA: + text = dbe_sprintf (GTXT ("%s: No profile data recorded in experiment"), type); + break; + case COL_WARN_LONG_FSTAT: + text = dbe_sprintf (GTXT ("%s: Long fstat call -- %s"), type, par); + break; + case COL_WARN_LONG_READ: + text = dbe_sprintf (GTXT ("%s: Long read call -- %s"), type, par); + break; + case COL_WARN_LINUX_X86_APICID: + text = dbe_sprintf (GTXT ("%s: Linux libc sched_getcpu() not found; using x86 %s IDs rather than CPU IDs"), type, par); + break; + + case COL_COMMENT_NONE: + text = dbe_sprintf (GTXT ("%s"), par); + break; + case COL_COMMENT_CWD: + text = dbe_sprintf (GTXT ("Initial execution directory `%s'"), par); + break; + case COL_COMMENT_ARGV: + text = dbe_sprintf (GTXT ("Argument list `%s'"), par); + break; + case COL_COMMENT_MAYASSNAP: + text = dbe_sprintf (GTXT ("Mayas snap file `%s'"), par); + break; + + case COL_COMMENT_LINEFORK: + text = dbe_sprintf (GTXT ("Target fork: %s"), par); + break; + case COL_COMMENT_LINEEXEC: + text = dbe_sprintf (GTXT ("Target exec: %s"), par); + break; + case COL_COMMENT_LINECOMBO: + text = dbe_sprintf (GTXT ("Target fork/exec: %s"), par); + break; + case COL_COMMENT_FOXSNAP: + text = dbe_sprintf (GTXT ("Fox snap file `%s'"), par); + break; + case COL_COMMENT_ROCKSNAP: + text = dbe_sprintf (GTXT ("Rock simulator snap file `%s'"), par); + break; + case COL_COMMENT_BITINSTRDATA: + text = dbe_sprintf (GTXT ("Bit instrument data file `%s'"), par); + break; + case COL_COMMENT_BITSNAP: + text = dbe_sprintf (GTXT ("Bit snap file `%s'"), par); + break; + case COL_COMMENT_SIMDSPSNAP: + text = dbe_sprintf (GTXT ("Simulator dataspace profiling snap file `%s'"), par); + break; + case COL_COMMENT_HWCADJ: + text = dbe_sprintf (GTXT ("%s: HWC overflow interval adjusted: %s"), type, par); + break; + case COL_WARN_APP_NOT_READY: + text = dbe_sprintf (GTXT ("*** Collect: %s"), par); + break; + case COL_WARN_RDT_DL_TERMINATE: + text = dbe_sprintf (GTXT ("%s: Actual deadlock detected; process terminated"), type); + break; + case COL_WARN_RDT_DL_TERMINATE_CORE: + text = dbe_sprintf (GTXT ("%s: Actual deadlock detected; process terminated and core dumped"), type); + break; + case COL_WARN_RDT_DL_CONTINUE: + text = dbe_sprintf (GTXT ("%s: Actual deadlock detected; process allowed to continue"), type); + break; + default: + text = dbe_sprintf (GTXT ("%s: Number %d (\"%s\")"), type, flavor, par); + break; + }; +} + +Emsg::~Emsg () +{ + free (par); + free (text); +} + +// ----------------------- Message Queue -------------------- +Emsgqueue::Emsgqueue (char *_qname) +{ + first = NULL; + last = NULL; + qname = strdup (_qname); +} + +Emsgqueue::~Emsgqueue () +{ + free (qname); + clear (); +} + +Emsg * +Emsgqueue::find_msg (Cmsg_warn w, char *msg) +{ + for (Emsg *m = first; m; m = m->next) + if (m->get_warn () == w && strcmp (m->get_msg (), msg) == 0) + return m; + return NULL; +} + +Emsg * +Emsgqueue::append (Cmsg_warn w, char *msg) +{ + Emsg *m = find_msg (w, msg); + if (m) + return m; + m = new Emsg (w, msg); + append (m); + return m; +} + +// Append a single message to a queue +void +Emsgqueue::append (Emsg* m) +{ + m->next = NULL; + if (last == NULL) + { + first = m; + last = m; + } + else + { + last->next = m; + last = m; + } +} + +// Append a queue of messages to a queue +void +Emsgqueue::appendqueue (Emsgqueue* mq) +{ + Emsg *m = mq->first; + if (m == NULL) + return; + if (last == NULL) + first = m; + else + last->next = m; + // now find the new last + while (m->next != NULL) + m = m->next; + last = m; +} + +Emsg * +Emsgqueue::fetch (void) +{ + return first; +} + +// Empty the queue, deleting all messages +void +Emsgqueue::clear (void) +{ + Emsg *pp; + Emsg *p = first; + while (p != NULL) + { + pp = p; + p = p->next; + delete pp; + } + first = NULL; + last = NULL; +} + +// Mark the queue empty, without deleting the messages -- +// used when the messages have been requeued somewhere else +void +Emsgqueue::mark_clear (void) +{ + first = NULL; + last = NULL; +} + +DbeMessages::DbeMessages () +{ + msgs = NULL; +} + +DbeMessages::~DbeMessages () +{ + if (msgs) + { + msgs->destroy (); + delete msgs; + } +} + +Emsg * +DbeMessages::get_error () +{ + for (int i = msgs ? msgs->size () - 1 : -1; i >= 0; i--) + { + Emsg *msg = msgs->get (i); + if (msg->get_warn () == CMSG_ERROR) + return msg; + } + return NULL; +} + +void +DbeMessages::remove_msg (Emsg *msg) +{ + for (int i = 0, sz = msgs ? msgs->size () : 0; i < sz; i++) + if (msg == msgs->get (i)) + { + msgs->remove (i); + delete msg; + return; + } +} + +Emsg * +DbeMessages::append_msg (Cmsg_warn w, const char *fmt, ...) +{ + char buffer[256]; + size_t buf_size; + Emsg *msg; + va_list vp; + + va_start (vp, fmt); + buf_size = vsnprintf (buffer, sizeof (buffer), fmt, vp) + 1; + va_end (vp); + if (buf_size < sizeof (buffer)) + msg = new Emsg (w, buffer); + else + { + va_start (vp, fmt); + char *buf = (char *) malloc (buf_size); + vsnprintf (buf, buf_size, fmt, vp); + va_end (vp); + msg = new Emsg (w, buf); + free (buf); + } + + if (msgs == NULL) + msgs = new Vector<Emsg*>(); + msgs->append (msg); + Dprintf (DEBUG_ERR_MSG, NTXT ("Warning: %s\n"), msg->get_msg ()); + return msg; +} + +void +DbeMessages::append_msgs (Vector<Emsg*> *lst) +{ + if (lst && (lst->size () != 0)) + { + if (msgs == NULL) + msgs = new Vector<Emsg*>(); + for (int i = 0, sz = lst->size (); i < sz; i++) + { + Emsg *m = lst->fetch (i); + msgs->append (new Emsg (m->get_warn (), m->get_msg ())); + } + } +} diff --git a/gprofng/src/Emsg.h b/gprofng/src/Emsg.h new file mode 100644 index 0000000..f1d47c5 --- /dev/null +++ b/gprofng/src/Emsg.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _EMSG_H +#define _EMSG_H + +#include "Emsgnum.h" +#include "vec.h" + +// +// The Emsg, experiment message, has as objects I18N'd messages +// in a structure suitable for attaching to and fetching +// from a queue of such messages. It is intended to +// be used for collector errors, collector warnings, parser +// errors, and er_archive errors that are encountered when +// reading an experiment + +class Emsg; +class Emsgqueue; +class StringBuilder; + +typedef enum +{ + CMSG_WARN = 0, + CMSG_ERROR, + CMSG_FATAL, + CMSG_COMMENT, + CMSG_PARSER, + CMSG_ARCHIVE +} Cmsg_warn; + +class Emsg +{ +public: + friend class Emsgqueue; + + Emsg (Cmsg_warn w, const char *i18n_text); + Emsg (Cmsg_warn w, StringBuilder& sb); + Emsg (Cmsg_warn w, int f, const char *param); + ~Emsg (); + + char * + get_msg () + { + return text; + }; + + Cmsg_warn + get_warn () + { + return warn; + }; + + Emsg *next; // next message in a queue + +protected: + Cmsg_warn warn; // error/warning/... + int flavor; // the message flavor + char *par; // the input parameter string + char *text; // The I18N text of the message +}; + +class Emsgqueue +{ +public: + Emsgqueue (char *); + ~Emsgqueue (); + + void append (Emsg*); + Emsg *append (Cmsg_warn w, char *msg); + Emsg *find_msg (Cmsg_warn w, char *msg); + void appendqueue (Emsgqueue*); + Emsg *fetch (void); + void clear (void); // empty the queue + void mark_clear (void); // mark the queue empty, without touching messages + +protected: + Emsg *first; + Emsg *last; + char *qname; +}; + +class DbeMessages +{ +public: + DbeMessages (); + ~DbeMessages (); + Vector<Emsg*> *msgs; + void remove_msg (Emsg *msg); + Emsg *get_error (); + Emsg *append_msg (Cmsg_warn w, const char *fmt, ...); + void append_msgs (Vector<Emsg*> *lst); +}; + +#endif /* _EMSG_H */ diff --git a/gprofng/src/Emsgnum.h b/gprofng/src/Emsgnum.h new file mode 100644 index 0000000..cef8332 --- /dev/null +++ b/gprofng/src/Emsgnum.h @@ -0,0 +1,135 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _EMSGNUM_H +#define _EMSGNUM_H + +// Define numerical codes for all messages and warnings + +#define COL_ERROR_NONE 0 /* OK */ +#define COL_ERROR_ARGS2BIG 1 /* data descriptor too long */ +#define COL_ERROR_BADDIR 2 /* experiment directory error */ +#define COL_ERROR_ARGS 3 /* data descriptor format error */ +#define COL_ERROR_PROFARGS 4 /* clock profile parameter error */ +#define COL_ERROR_SYNCARGS 5 /* synctrace parameter error */ +#define COL_ERROR_HWCARGS 6 /* HWC profile parameter error */ +#define COL_ERROR_DIRPERM 7 /* experiment directory not writable */ +#define COL_ERROR_NOMSACCT 8 /* failed to turn on microstate accounting */ +#define COL_ERROR_PROFINIT 9 /* failed to initialize profiling */ +#define COL_ERROR_SYNCINIT 10 /* failed to initialize synchronization tracing */ +#define COL_ERROR_HWCINIT 11 /* failed to initialize HWC profiling */ +#define COL_ERROR_HWCFAIL 12 /* HWC profiling failed during run */ +#define COL_ERROR_EXPOPEN 13 /* Experiment initialization failed */ +#define COL_ERROR_SIZELIM 14 /* Experiment exceeded size limit */ +#define COL_ERROR_SYSINFO 15 /* uname call failed */ +#define COL_ERROR_OVWOPEN 16 /* Opening the overview file failed */ +#define COL_ERROR_OVWWRITE 17 /* Writing the overview file failed */ +#define COL_ERROR_OVWREAD 18 /* Reading the overview data failed */ +#define COL_ERROR_NOZMEM 19 /* Unable to open /dev/zero */ +#define COL_ERROR_NOZMEMMAP 20 /* Unable to map /dev/zero */ +#define COL_ERROR_NOHNDL 21 /* No more handles available for data */ +#define COL_ERROR_FILEOPN 22 /* Unable to open file */ +#define COL_ERROR_FILETRNC 23 /* Unable to truncate file */ +#define COL_ERROR_FILEMAP 24 /* Unable to mmap file */ +#define COL_ERROR_HEAPINIT 25 /* Unable to install heap tracing */ +#define COL_ERROR_DISPINIT 26 /* Failed to install dispatcher */ +#define COL_ERROR_ITMRINIT 27 /* Failed to install interval timer */ +#define COL_ERROR_SMPLINIT 28 /* Failed to initialize periodic sampling */ +#define COL_ERROR_MPIINIT 29 /* Failed to initialize MPI tracing */ +#define COL_ERROR_JAVAINIT 30 /* Failed to initialize Java profiling */ +#define COL_ERROR_LINEINIT 31 /* Failed to initialize lineage tracing */ +#define COL_ERROR_NOSPACE 32 /* Ran out of disk space writing file */ +#define COL_ERROR_ITMRRST 33 /* Failed to reset interval timer */ +#define COL_ERROR_MKDIR 34 /* Failed to create (sub)directory */ +#define COL_ERROR_JVM2NEW 35 /* JVM is too new for us to cope (JVMTI interface) */ +#define COL_ERROR_JVMNOTSUPP 36 /* JVM does not support profiling (no JVMTI interface) */ +#define COL_ERROR_JVMNOJSTACK 37 /* JVM does not support java stack unwind */ +#define COL_ERROR_DYNOPEN 38 /* Unable to open dyntext file */ +#define COL_ERROR_DYNWRITE 39 /* Unable to write dyntext file */ +#define COL_ERROR_MAPOPEN 40 /* Unable to open map file */ +#define COL_ERROR_MAPREAD 41 /* Unable to read map file */ +#define COL_ERROR_MAPWRITE 42 /* Unable to write map file */ +#define COL_ERROR_RESOLVE 43 /* Unable to resolve map file */ +#define COL_ERROR_OMPINIT 44 /* Failure to initialize OpenMP tracing */ +#define COL_ERROR_DURATION_INIT 45 /* Failure to initialize -t (duration) processing */ +#define COL_ERROR_RDTINIT 46 /* Unable to install RDT */ +#define COL_ERROR_GENERAL 47 /* General error */ +#define COL_ERROR_EXEC_FAIL 48 /* Can't exec the process */ +#define COL_ERROR_THR_MAX 49 /* More threads than are supported */ +#define COL_ERROR_IOINIT 50 /* failed to initialize IO tracing */ +#define COL_ERROR_NODATA 51 /* No data recorded in experiment */ +#define COL_ERROR_DTRACE_FATAL 52 /* Fatal error from er_kernel DTrace code */ +#define COL_ERROR_MAPSEEK 53 /* Error on seek of map file */ +#define COL_ERROR_UNEXP_FOUNDER 54 /* Unexpected value for SP_COLLECTOR_FOUNDER */ +#define COL_ERROR_LOG_OPEN 55 /* Failure to open log.xml file */ +#define COL_ERROR_TSD_INIT 56 /* TSD could not be initialized */ +#define COL_ERROR_UTIL_INIT 57 /* libcol_util.c could not be initialized */ +#define COL_ERROR_MAPCACHE 58 /* Unable to cache mappings */ + +#define COL_WARN_NONE 200 /* just a note, not a real warning */ +#define COL_WARN_FSTYPE 201 /* Writing to a potentially-distorting file system */ +#define COL_WARN_PROFRND 202 /* Profile interval rounded */ +#define COL_WARN_SIZELIM 203 /* Size limit specified */ +#define COL_WARN_SIGPROF 204 /* SIGPROF handler replaced */ +#define COL_WARN_SMPLADJ 205 /* Periodic sampling rate adjusted */ +#define COL_WARN_ITMROVR 206 /* Application interval timer resetting prevented */ +#define COL_WARN_ITMRREP 207 /* Collection interval timer found to have been overridden */ +#define COL_WARN_SIGEMT 208 /* SIGEMT handler replaced */ +#define COL_WARN_CPCBLK 209 /* libcpc access blocked */ +#define COL_WARN_VFORK 210 /* vfork(2) switched to fork1(2) */ +#define COL_WARN_EXECENV 211 /* incomplete exec environment */ +#define COL_WARN_SAMPSIGUSED 212 /* target installed handler for sample signal */ +#define COL_WARN_PAUSESIGUSED 213 /* target installed handler for pause signal */ +#define COL_WARN_CPCNOTRESERVED 214 /* unable to reserve HW counters for kernel profiling */ +#define COL_WARN_LIBTHREAD_T1 215 /* collection with classic libthread */ +#define COL_WARN_SIGMASK 216 /* profiling signal masking overridden */ +#define COL_WARN_NOFOLLOW 217 /* descendant following disabled */ +#define COL_WARN_RISKYFOLLOW 218 /* descendant following unqualified */ +#define COL_WARN_IDCHNG 219 /* process ID change requested */ +#define COL_WARN_OLDJAVA 220 /* Java profiling requires JVM version 1.4.2_02 or later */ +#define COL_WARN_ITMRPOVR 221 /* Overriding app-set interval timer */ +#define COL_WARN_NO_JAVA_HEAP 222 /* Java heap tracing not supported (JVM 1.5) */ +#define COL_WARN_RDT_PAUSE_NOMEM 223 /* RDT paused because of running out of memory */ +#define COL_WARN_RDT_RESUME 224 /* RDT resumed */ +#define COL_WARN_RDT_THROVER 225 /* RDT: too many threads */ +#define COL_WARN_THR_PAUSE_RESUME 226 /* use of thread pause/resume API is deprecateds */ +#define COL_WARN_APP_NOT_READY 227 /* Application is not instrumented for RDT */ +#define COL_WARN_RDT_DL_TERMINATE 228 /* RDT: terminate execution on actual deadlock */ +#define COL_WARN_RDT_DL_TERMINATE_CORE 229 /* RDT: dump core and terminate execution on actual deadlock */ +#define COL_WARN_RDT_DL_CONTINUE 230 /* RDT: continue execution on actual deadlock */ +#define COL_WARN_NOPROF_DATA 231 /* No profile data recorded in experiment */ +#define COL_WARN_LONG_FSTAT 232 /* fstat call on /proc/self/map took > 200 ms. */ +#define COL_WARN_LONG_READ 233 /* read call on /proc/self/map took > 200 ms. */ +#define COL_WARN_LINUX_X86_APICID 234 /* using x86 APIC IDs rather than Linux sched_getcpu() */ + +#define COL_COMMENT_NONE 400 /* no comment */ +#define COL_COMMENT_CWD 401 /* initial execution directory */ +#define COL_COMMENT_ARGV 402 /* arguments */ +#define COL_COMMENT_MAYASSNAP 403 /* Mayas snap file name */ +#define COL_COMMENT_LINEFORK 404 /* process fork'd */ +#define COL_COMMENT_LINEEXEC 405 /* process exec'd */ +#define COL_COMMENT_LINECOMBO 406 /* process combo fork/exec */ +#define COL_COMMENT_FOXSNAP 407 /* Fox snap file name */ +#define COL_COMMENT_ROCKSNAP 408 /* Rock simulator snap file name */ +#define COL_COMMENT_BITINSTRDATA 409 /* Bit instrdata file name */ +#define COL_COMMENT_BITSNAP 410 /* Bit snap file name */ +#define COL_COMMENT_SIMDSPSNAP 411 /* Simulator dataspace profiling snap file name */ +#define COL_COMMENT_HWCADJ 412 /* HWC overflow interval adjusted */ +#endif /* _EMSGNUM_H */ diff --git a/gprofng/src/ExpGroup.cc b/gprofng/src/ExpGroup.cc new file mode 100644 index 0000000..0ad269a --- /dev/null +++ b/gprofng/src/ExpGroup.cc @@ -0,0 +1,163 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "util.h" +#include "ExpGroup.h" +#include "Experiment.h" +#include "LoadObject.h" +#include "DbeSession.h" + +////////////////////////////////////////////////////////// +// class ExpGroup + +int ExpGroup::phaseCompareIdx = 0; + +ExpGroup::ExpGroup (char *nm) +{ + name = dbe_strdup (nm); + canonical_path (name); + exps = new Vector<Experiment*>; + founder = NULL; + groupId = 0; + phaseCompareIdx++; + loadObjs = NULL; + loadObjsMap = NULL; +} + +ExpGroup::~ExpGroup () +{ + phaseCompareIdx++; + free (name); + delete exps; + delete loadObjs; + delete loadObjsMap; +} + +void +ExpGroup::append (Experiment *exp) +{ + for (int i = 0, sz = exps->size (); i < sz; i++) + { + Experiment *e = exps->fetch (i); + if (exp == e) + return; + } + exps->append (exp); + if (exps->size () == 1) + founder = exp; +} + +void +ExpGroup::drop_experiment (Experiment *exp) +{ + for (int i = 0, sz = exps->size (); i < sz; i++) + { + Experiment *e = exps->fetch (i); + if (exp == e) + { + exps->remove (i); + break; + } + } + if (founder == exp) + founder = NULL; +} + +Vector<Experiment*> * +ExpGroup::get_founders () +{ + Vector<Experiment*> *expList = NULL; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->founder_exp == NULL) + { + if (expList == NULL) + expList = new Vector<Experiment*>; + expList->append (exp); + } + } + return expList; +} + +void +ExpGroup::create_list_of_loadObjects () +{ + if (loadObjs == NULL) + { + loadObjs = new Vector<LoadObject*>(); + loadObjsMap = new DefaultMap<LoadObject*, int>(); + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + for (int i1 = 0, sz1 = VecSize(exp->loadObjs); i1 < sz1; i1++) + { + LoadObject *lo = exp->loadObjs->fetch (i1); + if (!loadObjsMap->get (lo)) + { + loadObjs->append (lo); + loadObjsMap->put (lo, loadObjs->size ()); + } + } + } + } +} + +LoadObject * +ExpGroup::get_comparable_loadObject (LoadObject *lo) +{ + create_list_of_loadObjects (); + if (loadObjsMap->get (lo)) + return lo; + if ((lo->flags & SEG_FLAG_EXE) != 0) + if (dbeSession->expGroups->size () == dbeSession->nexps ()) + for (int i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lobj = loadObjs->fetch (i); + if ((lobj->flags & SEG_FLAG_EXE) != 0) + return lobj; + } + + long first_ind = -1; + char *bname = get_basename (lo->get_pathname ()); + for (long i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lobj = loadObjs->get (i); + if (lobj->comparable_objs == NULL + && strcmp (bname, get_basename (lobj->get_pathname ())) == 0) + { + if (lo->platform == lobj->platform) + { + if ((lo->flags & SEG_FLAG_DYNAMIC) != 0) + { + if (dbe_strcmp (lo->firstExp->uarglist, + lobj->firstExp->uarglist) == 0) + return lobj; + } + else + return lobj; + } + if (first_ind == -1) + first_ind = i; + } + } + return first_ind == -1 ? NULL : loadObjs->get (first_ind); +} diff --git a/gprofng/src/ExpGroup.h b/gprofng/src/ExpGroup.h new file mode 100644 index 0000000..b3c9422 --- /dev/null +++ b/gprofng/src/ExpGroup.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _EXPGROUP_H +#define _EXPGROUP_H + +#include "vec.h" +#include "Map.h" + +class Experiment; +class LoadObject; + +class ExpGroup +{ +public: + ExpGroup (char *nm); + ~ExpGroup (); + void append (Experiment *exp); + void drop_experiment (Experiment *exp); + Vector<Experiment*> *get_founders (); + void create_list_of_loadObjects (); + LoadObject *get_comparable_loadObject (LoadObject *lo); + + Vector<Experiment*> *exps; + Vector<LoadObject*> *loadObjs; + Map <LoadObject*, int> *loadObjsMap; + Experiment *founder; + char *name; + int groupId; + static int phaseCompareIdx; +}; + +#endif /* _EXPGROUP_H */ diff --git a/gprofng/src/Exp_Layout.cc b/gprofng/src/Exp_Layout.cc new file mode 100644 index 0000000..dfe1432 --- /dev/null +++ b/gprofng/src/Exp_Layout.cc @@ -0,0 +1,422 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "CallStack.h" +#include "DbeSession.h" +#include "Exp_Layout.h" +#include "Experiment.h" +#include "Function.h" +#include "Table.h" +#include "dbe_types.h" +#include "util.h" + +/* + * PrUsage is a class which wraps access to the values of prusage + * system structure. It was expanded to 64 bit entities in 2.7 + * (experiment version 6 & 7). + */ +PrUsage::PrUsage () +{ + pr_tstamp = pr_create = pr_term = pr_rtime = (hrtime_t) 0; + pr_utime = pr_stime = pr_ttime = pr_tftime = pr_dftime = (hrtime_t) 0; + pr_kftime = pr_ltime = pr_slptime = pr_wtime = pr_stoptime = (hrtime_t) 0; + + pr_minf = pr_majf = pr_nswap = pr_inblk = pr_oublk = 0; + pr_msnd = pr_mrcv = pr_sigs = pr_vctx = pr_ictx = pr_sysc = pr_ioch = 0; +} + +/* + * Resource usage. /proc/<pid>/usage /proc/<pid>/lwp/<lwpid>/lwpusage + */ +struct timestruc_32 +{ /* v8 timestruc_t */ + uint32_t tv_sec; /* seconds */ + uint32_t tv_nsec; /* and nanoseconds */ +}; + +typedef struct ana_prusage +{ + id_t pr_lwpid; /* lwp id. 0: process or defunct */ + int pr_count; /* number of contributing lwps */ + timestruc_32 pr_tstamp; /* current time stamp */ + timestruc_32 pr_create; /* process/lwp creation time stamp */ + timestruc_32 pr_term; /* process/lwp termination time stamp */ + timestruc_32 pr_rtime; /* total lwp real (elapsed) time */ + timestruc_32 pr_utime; /* user level cpu time */ + timestruc_32 pr_stime; /* system call cpu time */ + timestruc_32 pr_ttime; /* other system trap cpu time */ + timestruc_32 pr_tftime; /* text page fault sleep time */ + timestruc_32 pr_dftime; /* data page fault sleep time */ + timestruc_32 pr_kftime; /* kernel page fault sleep time */ + timestruc_32 pr_ltime; /* user lock wait sleep time */ + timestruc_32 pr_slptime; /* all other sleep time */ + timestruc_32 pr_wtime; /* wait-cpu (latency) time */ + timestruc_32 pr_stoptime; /* stopped time */ + timestruc_32 filltime[6]; /* filler for future expansion */ + uint32_t pr_minf; /* minor page faults */ + uint32_t pr_majf; /* major page faults */ + uint32_t pr_nswap; /* swaps */ + uint32_t pr_inblk; /* input blocks */ + uint32_t pr_oublk; /* output blocks */ + uint32_t pr_msnd; /* messages sent */ + uint32_t pr_mrcv; /* messages received */ + uint32_t pr_sigs; /* signals received */ + uint32_t pr_vctx; /* voluntary context switches */ + uint32_t pr_ictx; /* involuntary context switches */ + uint32_t pr_sysc; /* system calls */ + uint32_t pr_ioch; /* chars read and written */ + uint32_t filler[10]; /* filler for future expansion */ +} raw_prusage_32; + +uint64_t +PrUsage::bind32Size () +{ + uint64_t bindSize = sizeof (raw_prusage_32); + return bindSize; +} + +#define timestruc2hr(x) ((hrtime_t)(x).tv_sec*NANOSEC + (hrtime_t)(x).tv_nsec) + +PrUsage * +PrUsage::bind32 (void *p, bool need_swap_endian) +{ + if (p == NULL) + return NULL; + raw_prusage_32 pu, *tmp = (raw_prusage_32*) p; + if (need_swap_endian) + { + pu = *tmp; + tmp = &pu; + SWAP_ENDIAN (pu.pr_tstamp.tv_sec); + SWAP_ENDIAN (pu.pr_tstamp.tv_nsec); + SWAP_ENDIAN (pu.pr_create.tv_sec); + SWAP_ENDIAN (pu.pr_create.tv_nsec); + SWAP_ENDIAN (pu.pr_term.tv_sec); + SWAP_ENDIAN (pu.pr_term.tv_nsec); + SWAP_ENDIAN (pu.pr_rtime.tv_sec); + SWAP_ENDIAN (pu.pr_rtime.tv_nsec); + SWAP_ENDIAN (pu.pr_utime.tv_sec); + SWAP_ENDIAN (pu.pr_utime.tv_nsec); + SWAP_ENDIAN (pu.pr_stime.tv_sec); + SWAP_ENDIAN (pu.pr_stime.tv_nsec); + SWAP_ENDIAN (pu.pr_ttime.tv_sec); + SWAP_ENDIAN (pu.pr_ttime.tv_nsec); + SWAP_ENDIAN (pu.pr_tftime.tv_sec); + SWAP_ENDIAN (pu.pr_tftime.tv_nsec); + SWAP_ENDIAN (pu.pr_dftime.tv_sec); + SWAP_ENDIAN (pu.pr_dftime.tv_nsec); + SWAP_ENDIAN (pu.pr_kftime.tv_sec); + SWAP_ENDIAN (pu.pr_kftime.tv_nsec); + SWAP_ENDIAN (pu.pr_ltime.tv_sec); + SWAP_ENDIAN (pu.pr_ltime.tv_nsec); + SWAP_ENDIAN (pu.pr_slptime.tv_sec); + SWAP_ENDIAN (pu.pr_slptime.tv_nsec); + SWAP_ENDIAN (pu.pr_wtime.tv_sec); + SWAP_ENDIAN (pu.pr_wtime.tv_nsec); + SWAP_ENDIAN (pu.pr_stoptime.tv_sec); + SWAP_ENDIAN (pu.pr_stoptime.tv_nsec); + SWAP_ENDIAN (pu.pr_minf); + SWAP_ENDIAN (pu.pr_majf); + SWAP_ENDIAN (pu.pr_nswap); + SWAP_ENDIAN (pu.pr_inblk); + SWAP_ENDIAN (pu.pr_oublk); + SWAP_ENDIAN (pu.pr_msnd); + SWAP_ENDIAN (pu.pr_mrcv); + SWAP_ENDIAN (pu.pr_sigs); + SWAP_ENDIAN (pu.pr_vctx); + SWAP_ENDIAN (pu.pr_ictx); + SWAP_ENDIAN (pu.pr_sysc); + SWAP_ENDIAN (pu.pr_ioch); + } + pr_tstamp = timestruc2hr (tmp->pr_tstamp); + pr_create = timestruc2hr (tmp->pr_create); + pr_term = timestruc2hr (tmp->pr_term); + pr_rtime = timestruc2hr (tmp->pr_rtime); + pr_utime = timestruc2hr (tmp->pr_utime); + pr_stime = timestruc2hr (tmp->pr_stime); + pr_ttime = timestruc2hr (tmp->pr_ttime); + pr_tftime = timestruc2hr (tmp->pr_tftime); + pr_dftime = timestruc2hr (tmp->pr_dftime); + pr_kftime = timestruc2hr (tmp->pr_kftime); + pr_ltime = timestruc2hr (tmp->pr_ltime); + pr_slptime = timestruc2hr (tmp->pr_slptime); + pr_wtime = timestruc2hr (tmp->pr_wtime); + pr_stoptime = timestruc2hr (tmp->pr_stoptime); + pr_minf = tmp->pr_minf; + pr_majf = tmp->pr_majf; + pr_nswap = tmp->pr_nswap; + pr_inblk = tmp->pr_inblk; + pr_oublk = tmp->pr_oublk; + pr_msnd = tmp->pr_msnd; + pr_mrcv = tmp->pr_mrcv; + pr_sigs = tmp->pr_sigs; + pr_vctx = tmp->pr_vctx; + pr_ictx = tmp->pr_ictx; + pr_sysc = tmp->pr_sysc; + pr_ioch = tmp->pr_ioch; + return this; +} + +struct timestruc_64 +{ /* 64-bit timestruc_t */ + uint64_t tv_sec; /* seconds */ + uint64_t tv_nsec; /* and nanoseconds */ +}; + +typedef struct +{ + id_t pr_lwpid; /* lwp id. 0: process or defunct */ + int pr_count; /* number of contributing lwps */ + timestruc_64 pr_tstamp; /* current time stamp */ + timestruc_64 pr_create; /* process/lwp creation time stamp */ + timestruc_64 pr_term; /* process/lwp termination time stamp */ + timestruc_64 pr_rtime; /* total lwp real (elapsed) time */ + timestruc_64 pr_utime; /* user level cpu time */ + timestruc_64 pr_stime; /* system call cpu time */ + timestruc_64 pr_ttime; /* other system trap cpu time */ + timestruc_64 pr_tftime; /* text page fault sleep time */ + timestruc_64 pr_dftime; /* data page fault sleep time */ + timestruc_64 pr_kftime; /* kernel page fault sleep time */ + timestruc_64 pr_ltime; /* user lock wait sleep time */ + timestruc_64 pr_slptime; /* all other sleep time */ + timestruc_64 pr_wtime; /* wait-cpu (latency) time */ + timestruc_64 pr_stoptime; /* stopped time */ + timestruc_64 filltime[6]; /* filler for future expansion */ + uint64_t pr_minf; /* minor page faults */ + uint64_t pr_majf; /* major page faults */ + uint64_t pr_nswap; /* swaps */ + uint64_t pr_inblk; /* input blocks */ + uint64_t pr_oublk; /* output blocks */ + uint64_t pr_msnd; /* messages sent */ + uint64_t pr_mrcv; /* messages received */ + uint64_t pr_sigs; /* signals received */ + uint64_t pr_vctx; /* voluntary context switches */ + uint64_t pr_ictx; /* involuntary context switches */ + uint64_t pr_sysc; /* system calls */ + uint64_t pr_ioch; /* chars read and written */ + uint64_t filler[10]; /* filler for future expansion */ +} raw_prusage_64; + +uint64_t +PrUsage::bind64Size () +{ + uint64_t bindSize = sizeof (raw_prusage_64); + return bindSize; +} + +PrUsage * +PrUsage::bind64 (void *p, bool need_swap_endian) +{ + if (p == NULL) + { + return NULL; + } + raw_prusage_64 pu, *tmp = (raw_prusage_64*) p; + if (need_swap_endian) + { + pu = *tmp; + tmp = &pu; + SWAP_ENDIAN (pu.pr_tstamp.tv_sec); + SWAP_ENDIAN (pu.pr_tstamp.tv_nsec); + SWAP_ENDIAN (pu.pr_create.tv_sec); + SWAP_ENDIAN (pu.pr_create.tv_nsec); + SWAP_ENDIAN (pu.pr_term.tv_sec); + SWAP_ENDIAN (pu.pr_term.tv_nsec); + SWAP_ENDIAN (pu.pr_rtime.tv_sec); + SWAP_ENDIAN (pu.pr_rtime.tv_nsec); + SWAP_ENDIAN (pu.pr_utime.tv_sec); + SWAP_ENDIAN (pu.pr_utime.tv_nsec); + SWAP_ENDIAN (pu.pr_stime.tv_sec); + SWAP_ENDIAN (pu.pr_stime.tv_nsec); + SWAP_ENDIAN (pu.pr_ttime.tv_sec); + SWAP_ENDIAN (pu.pr_ttime.tv_nsec); + SWAP_ENDIAN (pu.pr_tftime.tv_sec); + SWAP_ENDIAN (pu.pr_tftime.tv_nsec); + SWAP_ENDIAN (pu.pr_dftime.tv_sec); + SWAP_ENDIAN (pu.pr_dftime.tv_nsec); + SWAP_ENDIAN (pu.pr_kftime.tv_sec); + SWAP_ENDIAN (pu.pr_kftime.tv_nsec); + SWAP_ENDIAN (pu.pr_ltime.tv_sec); + SWAP_ENDIAN (pu.pr_ltime.tv_nsec); + SWAP_ENDIAN (pu.pr_slptime.tv_sec); + SWAP_ENDIAN (pu.pr_slptime.tv_nsec); + SWAP_ENDIAN (pu.pr_wtime.tv_sec); + SWAP_ENDIAN (pu.pr_wtime.tv_nsec); + SWAP_ENDIAN (pu.pr_stoptime.tv_sec); + SWAP_ENDIAN (pu.pr_stoptime.tv_nsec); + SWAP_ENDIAN (pu.pr_minf); + SWAP_ENDIAN (pu.pr_majf); + SWAP_ENDIAN (pu.pr_nswap); + SWAP_ENDIAN (pu.pr_inblk); + SWAP_ENDIAN (pu.pr_oublk); + SWAP_ENDIAN (pu.pr_msnd); + SWAP_ENDIAN (pu.pr_mrcv); + SWAP_ENDIAN (pu.pr_sigs); + SWAP_ENDIAN (pu.pr_vctx); + SWAP_ENDIAN (pu.pr_ictx); + SWAP_ENDIAN (pu.pr_sysc); + SWAP_ENDIAN (pu.pr_ioch); + } + + pr_tstamp = timestruc2hr (tmp->pr_tstamp); + pr_create = timestruc2hr (tmp->pr_create); + pr_term = timestruc2hr (tmp->pr_term); + pr_rtime = timestruc2hr (tmp->pr_rtime); + pr_utime = timestruc2hr (tmp->pr_utime); + pr_stime = timestruc2hr (tmp->pr_stime); + pr_ttime = timestruc2hr (tmp->pr_ttime); + pr_tftime = timestruc2hr (tmp->pr_tftime); + pr_dftime = timestruc2hr (tmp->pr_dftime); + pr_kftime = timestruc2hr (tmp->pr_kftime); + pr_ltime = timestruc2hr (tmp->pr_ltime); + pr_slptime = timestruc2hr (tmp->pr_slptime); + pr_wtime = timestruc2hr (tmp->pr_wtime); + pr_stoptime = timestruc2hr (tmp->pr_stoptime); + pr_minf = tmp->pr_minf; + pr_majf = tmp->pr_majf; + pr_nswap = tmp->pr_nswap; + pr_inblk = tmp->pr_inblk; + pr_oublk = tmp->pr_oublk; + pr_msnd = tmp->pr_msnd; + pr_mrcv = tmp->pr_mrcv; + pr_sigs = tmp->pr_sigs; + pr_vctx = tmp->pr_vctx; + pr_ictx = tmp->pr_ictx; + pr_sysc = tmp->pr_sysc; + pr_ioch = tmp->pr_ioch; + return this; +} + +Vector<long long> * +PrUsage::getMstateValues () +{ + const PrUsage *prusage = this; + Vector<long long> *states = new Vector<long long>; + states->store (0, prusage->pr_utime); + states->store (1, prusage->pr_stime); + states->store (2, prusage->pr_ttime); + states->store (3, prusage->pr_tftime); + states->store (4, prusage->pr_dftime); + states->store (5, prusage->pr_kftime); + states->store (6, prusage->pr_ltime); + states->store (7, prusage->pr_slptime); + states->store (8, prusage->pr_wtime); + states->store (9, prusage->pr_stoptime); + assert (LMS_NUM_SOLARIS_MSTATES == states->size ()); + return states; +} + +void* CommonPacket::jvm_overhead = NULL; + +CommonPacket::CommonPacket () +{ + for (int i = 0; i < NTAGS; i++) + tags[i] = 0; + tstamp = 0; + jthread_TBR = NULL; + frinfo = 0; + leafpc = 0; + nat_stack = NULL; + user_stack = NULL; +} + +int +CommonPacket::cmp (const void *a, const void *b) +{ + if ((*(CommonPacket **) a)->tstamp > (*(CommonPacket **) b)->tstamp) + return 1; + else if ((*(CommonPacket **) a)->tstamp < (*(CommonPacket **) b)->tstamp) + return -1; + else + return 0; +} + +void * +CommonPacket::getStack (VMode view_mode) +{ + if (view_mode == VMODE_MACHINE) + return nat_stack; + else if (view_mode == VMODE_USER) + { + if (jthread_TBR == JTHREAD_NONE || (jthread_TBR && jthread_TBR->is_system ())) + return jvm_overhead; + } + else if (view_mode == VMODE_EXPERT) + { + Histable *hist = CallStack::getStackPC (user_stack, 0); + if (hist->get_type () == Histable::INSTR) + { + DbeInstr *instr = (DbeInstr*) hist; + if (instr->func == dbeSession->get_JUnknown_Function ()) + return nat_stack; + } + else if (hist->get_type () == Histable::LINE) + { + DbeLine *line = (DbeLine *) hist; + if (line->func == dbeSession->get_JUnknown_Function ()) + return nat_stack; + } + } + return user_stack; +} + +Histable * +CommonPacket::getStackPC (int n, VMode view_mode) +{ + return CallStack::getStackPC (getStack (view_mode), n); +} + +Vector<Histable*> * +CommonPacket::getStackPCs (VMode view_mode) +{ + return CallStack::getStackPCs (getStack (view_mode)); +} + +void * +getStack (VMode view_mode, DataView *dview, long idx) +{ + void *stack = NULL; + if (view_mode == VMODE_MACHINE) + stack = dview->getObjValue (PROP_MSTACK, idx); + else if (view_mode == VMODE_USER) + stack = dview->getObjValue (PROP_USTACK, idx); + else if (view_mode == VMODE_EXPERT) + stack = dview->getObjValue (PROP_XSTACK, idx); + return stack; +} + +int +stackSize (VMode view_mode, DataView *dview, long idx) +{ + return CallStack::stackSize (getStack (view_mode, dview, idx)); +} + +Histable * +getStackPC (int n, VMode view_mode, DataView *dview, long idx) +{ + return CallStack::getStackPC (getStack (view_mode, dview, idx), n); +} + +Vector<Histable*> * +getStackPCs (VMode view_mode, DataView *dview, long idx) +{ + return CallStack::getStackPCs (getStack (view_mode, dview, idx)); +} diff --git a/gprofng/src/Exp_Layout.h b/gprofng/src/Exp_Layout.h new file mode 100644 index 0000000..c5fbe4c --- /dev/null +++ b/gprofng/src/Exp_Layout.h @@ -0,0 +1,158 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _EXP_LAYOUT_H +#define _EXP_LAYOUT_H + +#include <sys/time.h> +#include <sys/types.h> + +#include "dbe_types.h" +#include "gp-experiment.h" +#include "data_pckts.h" +#include "ABS.h" +#include "Data_window.h" +#include "Histable.h" +#include "vec.h" + +class PrUsage +{ +public: + PrUsage (); + PrUsage *bind32 (void *p, bool need_swap_endian); + PrUsage *bind64 (void *p, bool need_swap_endian); + static uint64_t bind32Size (); + static uint64_t bind64Size (); + Vector<long long> * getMstateValues (); + + hrtime_t pr_tstamp; + hrtime_t pr_create; + hrtime_t pr_term; + hrtime_t pr_rtime; + + // the following correspond to PROP_MSTATE LMS_* offsets; see newMstateVec() + hrtime_t pr_utime; + hrtime_t pr_stime; + hrtime_t pr_ttime; + hrtime_t pr_tftime; + hrtime_t pr_dftime; + hrtime_t pr_kftime; + hrtime_t pr_ltime; + hrtime_t pr_slptime; + hrtime_t pr_wtime; + hrtime_t pr_stoptime; + + uint64_t pr_minf; + uint64_t pr_majf; + uint64_t pr_nswap; + uint64_t pr_inblk; + uint64_t pr_oublk; + uint64_t pr_msnd; + uint64_t pr_mrcv; + uint64_t pr_sigs; + uint64_t pr_vctx; + uint64_t pr_ictx; + uint64_t pr_sysc; + uint64_t pr_ioch; +}; + +class DataView; +extern void *getStack (VMode, DataView*, long); +extern int stackSize (VMode, DataView*, long); +extern Histable *getStackPC (int, VMode, DataView*, long); +extern Vector<Histable*> *getStackPCs (VMode, DataView*, long); + +class CommonPacket // use only for RacePacket, please +{ +public: + CommonPacket (); + void *getStack (VMode); + Histable *getStackPC (int, VMode); + Vector<Histable*>*getStackPCs (VMode); + static int cmp (const void *a, const void *b); + + enum Tag_type { LWP, THR, CPU }; + static const int NTAGS = 3; + uint32_t tags[NTAGS]; // lwp_id, thr_id, cpu_id + hrtime_t tstamp; + struct JThread *jthread_TBR; // pointer to JThread or NULL + uint64_t frinfo; // frame info + Vaddr leafpc; // raw leaf PC if availabe + void *nat_stack; // native stack + void *user_stack; // user stack (Java, OMP, etc.) + static void *jvm_overhead; +}; + +class FramePacket +{ +public: + int + stackSize (bool java = false) + { + return java ? jstack->size () / 2 : stack->size (); + } + + Vaddr + getFromStack (int n) + { + return stack->fetch (n); + } + + Vaddr + getMthdFromStack (int n) + { + return jstack->fetch (2 * n + 1); + } + + int + getBciFromStack (int n) + { + return (int) jstack->fetch (2 * n); + } + + bool + isLeafMark (int n) + { + return stack->fetch (n) == (Vaddr) SP_LEAF_CHECK_MARKER; + } + + bool + isTruncatedStack (bool java = false) + { + return java ? jtruncated : truncated == (Vaddr) SP_TRUNC_STACK_MARKER; + } + + bool + isFailedUnwindStack () + { + return truncated == (Vaddr) SP_FAILED_UNWIND_MARKER; + } + uint32_t omp_state; // OpenMP thread state + uint32_t mpi_state; // MPI state + uint64_t omp_cprid; // OpenMP parallel region id (omptrace) + Vector<Vaddr> *stack; + Vaddr truncated; + Vector<Vaddr> *jstack; + bool jtruncated; + Vector<Vaddr> *ompstack; + Vaddr omptruncated; +}; + +#endif /* _EXP_LAYOUT_H */ diff --git a/gprofng/src/Experiment.cc b/gprofng/src/Experiment.cc new file mode 100644 index 0000000..a23c10c --- /dev/null +++ b/gprofng/src/Experiment.cc @@ -0,0 +1,6961 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <errno.h> +#include <utime.h> +#include <alloca.h> +#include <dirent.h> +#include <ctype.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/param.h> +#include <set> + +#include "util.h" +#include "CacheMap.h" +#include "DbeFile.h" +#include "DbeCacheMap.h" +#include "DefaultHandler.h" +#include "DefaultMap2D.h" +#include "Emsg.h" +#include "Elf.h" +#include "SAXParser.h" +#include "SAXParserFactory.h" +#include "StringBuilder.h" +#include "DbeSession.h" +#include "DbeThread.h" +#include "Application.h" +#include "CallStack.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "DataStream.h" +#include "Expression.h" +#include "Function.h" +#include "HeapMap.h" +#include "LoadObject.h" +#include "Module.h" +#include "Ovw_data.h" +#include "PRBTree.h" +#include "Sample.h" +#include "SegMem.h" +#include "StringMap.h" +#include "UserLabel.h" +#include "Table.h" +#include "dbe_types.h" +#include "FileData.h" +#include "cc_libcollector.h" +#include "ExpGroup.h" + +int nPush; +int nPop; +int pushCnt; +int popCnt; +int pushCnt3; +int popCnt3; + +struct Experiment::UIDnode +{ + uint64_t uid; + uint64_t val; + UIDnode *next; +}; + +struct Experiment::RawFramePacket +{ + uint64_t uid; + UIDnode *uidn; + UIDnode *uidj; + UIDnode *omp_uid; + uint32_t omp_state; +}; + +static hrtime_t +parseTStamp (const char *s) +{ + hrtime_t ts = (hrtime_t) 0; + ts = (hrtime_t) atoi (s) * NANOSEC; + s = strchr (s, '.'); + if (s != NULL) + ts += (hrtime_t) atoi (s + 1); + return ts; +} + +class Experiment::ExperimentFile +{ +public: + + enum + { + EF_NOT_OPENED, + EF_OPENED, + EF_CLOSED, + EF_FAILURE + }; + + ExperimentFile (Experiment *_exp, const char *_fname); + ~ExperimentFile (); + + bool open (bool new_open = false); + + char * + get_name () + { + return fname; + } + + inline int + get_status () + { + return ef_status; + } + + char *fgets (); + void close (); + + FILE *fh; + +private: + Experiment *exp; + char *fname; + off64_t offset; + int bufsz, ef_status; + char *buffer; +}; + +class Experiment::ExperimentHandler : public DefaultHandler +{ +public: + + ExperimentHandler (Experiment *_exp); + ~ExperimentHandler (); + + void + startDocument () { } + void endDocument (); + void startElement (char *uri, char *localName, char *qName, Attributes *attrs); + void endElement (char *uri, char *localName, char *qName); + void characters (char *ch, int start, int length); + + void + ignorableWhitespace (char*, int, int) { } + void + error (SAXParseException *e); + +private: + + enum Element + { + EL_NONE, + EL_EXPERIMENT, + EL_COLLECTOR, + EL_SETTING, + EL_PROCESS, + EL_SYSTEM, + EL_EVENT, + EL_PROFILE, + EL_DATAPTR, + EL_PROFDATA, + EL_PROFPCKT, + EL_FIELD, + EL_CPU, + EL_STATE, + EL_FREQUENCY, + EL_POWERM, + EL_DTRACEFATAL + }; + + static int toInt (Attributes *attrs, const char *atr); + static char*toStr (Attributes *attrs, const char *atr); + void pushElem (Element); + void popElem (); + + Experiment *exp; + Element curElem; + Vector<Element> *stack; + Module *dynfuncModule; + DataDescriptor *dDscr; + PacketDescriptor *pDscr; + PropDescr *propDscr; + char *text; + Cmsg_warn mkind; + int mnum; + int mec; +}; + + +// HTableSize is the size of smemHTable and instHTable +// omazur: both HTableSize and the hash function haven't been tuned; +static const int HTableSize = 8192; + +//-------------------------------------------------- Experiment file handler + +Experiment::ExperimentFile::ExperimentFile (Experiment *_exp, const char *_fname) +{ + exp = _exp; + fh = NULL; + bufsz = 0; + buffer = NULL; + ef_status = EF_NOT_OPENED; + offset = 0; + fname = dbe_sprintf (NTXT ("%s/%s"), exp->expt_name, _fname); +} + +Experiment::ExperimentFile::~ExperimentFile () +{ + close (); + free (buffer); + free (fname); +} + +bool +Experiment::ExperimentFile::open (bool new_open) +{ + if (fh == NULL) + { + fh = fopen64 (fname, NTXT ("r")); + if (fh == NULL) + { + ef_status = EF_FAILURE; + return false; + } + ef_status = EF_OPENED; + if (new_open) + offset = 0; + if (offset != 0) + fseeko64 (fh, offset, SEEK_SET); + } + return true; +} + +char * +Experiment::ExperimentFile::fgets () +{ + if (bufsz == 0) + { + bufsz = 1024; + buffer = (char *) malloc (bufsz); + if (buffer == NULL) + return NULL; + buffer[bufsz - 1] = (char) 1; // sentinel + } + char *res = ::fgets (buffer, bufsz, fh); + if (res == NULL) + return NULL; + while (buffer[bufsz - 1] == (char) 0) + { + int newsz = bufsz + 1024; + char *newbuf = (char *) malloc (newsz); + if (newbuf == NULL) + return NULL; + memcpy (newbuf, buffer, bufsz); + free (buffer); + buffer = newbuf; + buffer[newsz - 1] = (char) 1; // sentinel + // we don't care about fgets result here + ::fgets (buffer + bufsz - 1, newsz - bufsz + 1, fh); + bufsz = newsz; + } + return buffer; +} + +void +Experiment::ExperimentFile::close () +{ + if (fh) + { + offset = ftello64 (fh); + fclose (fh); + ef_status = EF_CLOSED; + fh = NULL; + } +} + + +//-------------------------------------------------- Experiment XML parser +int +Experiment::ExperimentHandler::toInt (Attributes *attrs, const char *atr) +{ + const char *str = attrs->getValue (atr); + return str ? atoi (str) : 0; +} + +char * +Experiment::ExperimentHandler::toStr (Attributes *attrs, const char *atr) +{ + const char *str = attrs->getValue (atr); + return dbe_strdup (str ? str : NTXT ("")); +} + +Experiment::ExperimentHandler::ExperimentHandler (Experiment *_exp) +{ + exp = _exp; + stack = new Vector<Element>; + pushElem (EL_NONE); + dynfuncModule = NULL; + dDscr = NULL; + pDscr = NULL; + propDscr = NULL; + text = NULL; + mkind = (Cmsg_warn) - 1; // CMSG_NONE + mnum = -1; + mec = -1; +} + +Experiment::ExperimentHandler::~ExperimentHandler () +{ + delete stack; + free (text); +} + +void +Experiment::ExperimentHandler::endDocument () +{ + { // SP_TAG_STATE should be used to describe states, but it isn't + // let's do it here: + DataDescriptor *dd = exp->getDataDescriptor (DATA_HEAP); + if (dd != NULL) + { + PropDescr *prop = dd->getProp (PROP_HTYPE); + if (prop != NULL) + { + char * stateNames [HEAPTYPE_LAST] = HEAPTYPE_STATE_STRINGS; + char * stateUNames[HEAPTYPE_LAST] = HEAPTYPE_STATE_USTRINGS; + for (int ii = 0; ii < HEAPTYPE_LAST; ii++) + prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + } + dd = exp->getDataDescriptor (DATA_IOTRACE); + if (dd != NULL) + { + PropDescr *prop = dd->getProp (PROP_IOTYPE); + if (prop != NULL) + { + char * stateNames [IOTRACETYPE_LAST] = IOTRACETYPE_STATE_STRINGS; + char * stateUNames[IOTRACETYPE_LAST] = IOTRACETYPE_STATE_USTRINGS; + for (int ii = 0; ii < IOTRACETYPE_LAST; ii++) + prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + } + } +} + +void +Experiment::ExperimentHandler::pushElem (Element elem) +{ + curElem = elem; + stack->append (curElem); +} + +void +Experiment::ExperimentHandler::popElem () +{ + stack->remove (stack->size () - 1); + curElem = stack->fetch (stack->size () - 1); +} + +void +Experiment::ExperimentHandler::startElement (char*, char*, char *qName, Attributes *attrs) +{ + DEBUG_CODE if (DEBUG_SAXPARSER) dump_startElement (qName, attrs); + if (strcmp (qName, SP_TAG_EXPERIMENT) == 0) + { + pushElem (EL_EXPERIMENT); + const char *str = attrs->getValue (NTXT ("version")); + if (str != NULL) + { + int major = atoi (str); + str = strchr (str, '.'); + int minor = str ? atoi (str + 1) : 0; + exp->exp_maj_version = major; + exp->exp_min_version = minor; + if (major != SUNPERF_VERNUM || minor != SUNPERF_VERNUM_MINOR) + { + // not the current version, see if we support some earlier versions + if (major < 12) + { + StringBuilder sb; + sb.sprintf (GTXT ("*** Error: experiment %s version %d.%d is not supported;\nuse the version of the tools that recorded the experiment to read it"), + exp->get_expt_name (), major, minor); + // exp->errorq->append( new Emsg(CMSG_FATAL, sb) ); + exp->status = FAILURE; + exp->obsolete = 1; + throw new SAXException (sb.toString ()); + } + } + } + } + else if (strcmp (qName, SP_TAG_COLLECTOR) == 0) + pushElem (EL_COLLECTOR); + else if (strcmp (qName, SP_TAG_SETTING) == 0) + { + int found = 0; + pushElem (EL_SETTING); + const char *str = attrs->getValue (SP_JCMD_LIMIT); + if (str != NULL) + { + found = 1; + exp->coll_params.limit = atoi (str); + } + str = attrs->getValue (SP_JCMD_BLKSZ); + if (str != NULL) + { + found = 1; + exp->blksz = strtol (str, NULL, 0); + } + str = attrs->getValue (SP_JCMD_STACKBASE); + if (str) + { + found = 1; + exp->stack_base = strtoull (str, NULL, 0); + } + str = attrs->getValue (SP_JCMD_HWC_DEFAULT); + if (str != NULL) + { + found = 1; + exp->hwc_default = true; + } + str = attrs->getValue (SP_JCMD_NOIDLE); + if (str != NULL) + { + found = 1; + exp->commentq->append (new Emsg (CMSG_COMMENT, + GTXT ("*** Note: experiment does not have events from idle CPUs"))); + } + str = attrs->getValue (SP_JCMD_FAKETIME); + if (str != NULL) + { + found = 1; + exp->timelineavail = false; + exp->commentq->append (new Emsg (CMSG_COMMENT, + GTXT ("*** Note: experiment does not have timestamps; timeline unavailable"))); + } + str = attrs->getValue (SP_JCMD_DELAYSTART); + if (str != NULL) + { + found = 1; + exp->coll_params.start_delay = strdup (str); + } + str = attrs->getValue (SP_JCMD_TERMINATE); + if (str != NULL) + { + found = 1; + exp->coll_params.terminate = strdup (str); + } + str = attrs->getValue (SP_JCMD_PAUSE_SIG); + if (str != NULL) + { + found = 1; + exp->coll_params.pause_sig = strdup (str); + } + str = attrs->getValue (SP_JCMD_SAMPLE_PERIOD); + if (str != NULL) + { + found = 1; + exp->coll_params.sample_periodic = 1; + exp->coll_params.sample_timer = atoi (str); + } + str = attrs->getValue (SP_JCMD_SAMPLE_SIG); + if (str != NULL) + { + found = 1; + exp->coll_params.sample_sig = str; + } + str = attrs->getValue (SP_JCMD_SRCHPATH); + if (str != NULL) + { + found = 1; + StringBuilder sb; + sb.sprintf (GTXT ("Search path: %s"), str); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + dbeSession->add_classpath ((char*) str); + } + str = attrs->getValue (SP_JCMD_LINETRACE); + if (str != NULL) + { + found = 1; + exp->coll_params.linetrace = strdup (str); + } + + str = attrs->getValue (SP_JCMD_COLLENV); + if (str != NULL) + { + found = 1; + StringBuilder sb; + sb.sprintf (GTXT (" Data collection environment variable: %s"), str); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (found == 0) + { + int nattr = attrs->getLength (); + if (nattr != 0) + { + fprintf (stderr, "XXX Unexpected setting found; %d attributes:\n", + nattr); + for (int k = 0; k < nattr; k++) + { + const char *qn = attrs->getQName (k); + const char *vl = attrs->getValue (k); + fprintf (stderr, "XXX %s = %s\n", qn, vl); + } + } + } + // END OF CODE FOR "setting" + } + else if (strcmp (qName, SP_TAG_SYSTEM) == 0) + { + pushElem (EL_SYSTEM); + const char *str = attrs->getValue (NTXT ("hostname")); + if (str != NULL) + exp->hostname = strdup (str); + str = attrs->getValue (NTXT ("os")); + if (str != NULL) + { + exp->os_version = strdup (str); + /* For Linux experiments expect sparse thread ID's */ + if (strncmp (str, NTXT ("SunOS"), 5) != 0) + exp->sparse_threads = true; + } + str = attrs->getValue (NTXT ("arch")); + if (str != NULL) + { + if (strcmp (str, "i86pc") == 0 || strcmp (str, "i686") == 0 + || strcmp (str, "x86_64") == 0) + exp->platform = Intel; + else if (strcmp (str, "aarch64") == 0) + exp->platform = Aarch64; + else + exp->platform = Sparc; + exp->need_swap_endian = (DbeSession::platform == Sparc) ? + (exp->platform != Sparc) : (exp->platform == Sparc); + exp->architecture = strdup (str); + } + str = attrs->getValue (NTXT ("pagesz")); + if (str != NULL) + exp->page_size = atoi (str); + str = attrs->getValue (NTXT ("npages")); + if (str != NULL) + exp->npages = atoi (str); + } + else if (strcmp (qName, SP_TAG_POWERM) == 0) + pushElem (EL_POWERM); + else if (strcmp (qName, SP_TAG_FREQUENCY) == 0) + { + pushElem (EL_FREQUENCY); + const char *str = attrs->getValue (NTXT ("clk")); + if (str != NULL) + exp->set_clock (atoi (str)); + // check for frequency_scaling or turbo_mode recorded from libcollector under dbx + str = attrs->getValue (NTXT ("frequency_scaling")); + const char *str2 = attrs->getValue (NTXT ("turbo_mode")); + if (str != NULL || str2 != NULL) + exp->varclock = 1; + } + else if (strcmp (qName, SP_TAG_CPU) == 0) + { + pushElem (EL_CPU); + exp->ncpus++; + const char *str = attrs->getValue (NTXT ("clk")); + if (str != NULL) + { + int clk = atoi (str); + if (exp->maxclock == 0) + { + exp->minclock = clk; + exp->maxclock = clk; + } + else + { + if (clk < exp->minclock) + exp->minclock = clk; + if (clk > exp->maxclock) + exp->maxclock = clk; + } + exp->clock = clk; + } + // check for frequency_scaling or turbo_mode + str = attrs->getValue (NTXT ("frequency_scaling")); + const char *str2 = attrs->getValue (NTXT ("turbo_mode")); + if (str != NULL || str2 != NULL) + exp->varclock = 1; + } + else if (strcmp (qName, SP_TAG_PROCESS) == 0) + { + pushElem (EL_PROCESS); + const char *str = attrs->getValue (NTXT ("wsize")); + if (str != NULL) + { + int wsz = atoi (str); + if (wsz == 32) + exp->wsize = W32; + else if (wsz == 64) + exp->wsize = W64; + } + str = attrs->getValue (NTXT ("pid")); + if (str != NULL) + exp->pid = atoi (str); + str = attrs->getValue (NTXT ("ppid")); + if (str != NULL) + exp->ppid = atoi (str); + str = attrs->getValue (NTXT ("pgrp")); + if (str != NULL) + exp->pgrp = atoi (str); + str = attrs->getValue (NTXT ("sid")); + if (str != NULL) + exp->sid = atoi (str); + str = attrs->getValue (NTXT ("cwd")); + if (str != NULL) + exp->ucwd = strdup (str); + str = attrs->getValue (NTXT ("pagesz")); + if (str != NULL) + exp->page_size = atoi (str); + } + else if (strcmp (qName, SP_TAG_EVENT) == 0) + { // Start code for event + pushElem (EL_EVENT); + hrtime_t ts = (hrtime_t) 0; + const char *str = attrs->getValue (NTXT ("tstamp")); + if (str != NULL) + ts = parseTStamp (str); + str = attrs->getValue (NTXT ("kind")); + if (str != NULL) + { + if (strcmp (str, SP_JCMD_RUN) == 0) + { + exp->broken = 0; + exp->exp_start_time = ts; + str = attrs->getValue (NTXT ("time")); + if (str != NULL) + exp->start_sec = atol (str); + str = attrs->getValue (NTXT ("pid")); + if (str != NULL) + exp->pid = atoi (str); + str = attrs->getValue (NTXT ("ppid")); + if (str != NULL) + exp->ppid = atoi (str); + str = attrs->getValue (NTXT ("pgrp")); + if (str != NULL) + exp->pgrp = atoi (str); + str = attrs->getValue (NTXT ("sid")); + if (str != NULL) + exp->sid = atoi (str); + exp->status = Experiment::INCOMPLETE; + } + else if (strcmp (str, SP_JCMD_ARCHIVE) == 0) + { + StringBuilder sb; + sb.sprintf (GTXT ("er_archive run: XXXXXXX")); + exp->pprocq->append (new Emsg (CMSG_WARN, sb)); + } + else if (strcmp (str, SP_JCMD_SAMPLE) == 0) + { + exp->update_last_event (exp->exp_start_time + ts); // ts is 0-based + str = attrs->getValue (NTXT ("id")); + int id = str ? atoi (str) : -1; + char *label = dbe_strdup (attrs->getValue (NTXT ("label"))); + exp->process_sample_cmd (NULL, ts, id, label); + } + else if (strcmp (str, SP_JCMD_EXIT) == 0) + { + // don't treat EXIT as an event w.r.t. last_event and non_paused_time + exp->status = Experiment::SUCCESS; + } + else if (strcmp (str, SP_JCMD_CERROR) == 0) + { + mkind = CMSG_ERROR; + str = attrs->getValue (NTXT ("id")); + if (str != NULL) + { + mnum = atoi (str); + } + str = attrs->getValue (NTXT ("ec")); + if (str != NULL) + { + mec = atoi (str); + } + } + else if (strcmp (str, SP_JCMD_CWARN) == 0) + { + mkind = CMSG_WARN; + str = attrs->getValue (NTXT ("id")); + if (str != NULL) + mnum = atoi (str); + } + else if (strcmp (str, SP_JCMD_COMMENT) == 0) + { + mkind = CMSG_COMMENT; + str = attrs->getValue (NTXT ("id")); + if (str != NULL) + mnum = atoi (str); + str = attrs->getValue (NTXT ("text")); + if (str != NULL) + { + StringBuilder sb; + sb.sprintf (GTXT ("*** Note: %s"), str); + exp->commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + } + else if (strcmp (str, SP_JCMD_DESC_START) == 0) + { + char *variant = toStr (attrs, NTXT ("variant")); + char *lineage = toStr (attrs, NTXT ("lineage")); + int follow = toInt (attrs, NTXT ("follow")); + char *msg = toStr (attrs, NTXT ("msg")); + exp->process_desc_start_cmd (NULL, ts, variant, lineage, follow, msg); + } + else if (strcmp (str, SP_JCMD_DESC_STARTED) == 0) + { + char *variant = toStr (attrs, NTXT ("variant")); + char *lineage = toStr (attrs, NTXT ("lineage")); + int follow = toInt (attrs, NTXT ("follow")); + char *msg = toStr (attrs, NTXT ("msg")); + exp->process_desc_started_cmd (NULL, ts, variant, lineage, follow, msg); + } + else if (strcmp (str, SP_JCMD_EXEC_START) == 0) + { + // if successful, acts like experiment termination - no "exit" entry will follow + exp->update_last_event (exp->exp_start_time + ts); // ts is 0-based + char *variant = toStr (attrs, NTXT ("variant")); + char *lineage = toStr (attrs, NTXT ("lineage")); + int follow = toInt (attrs, NTXT ("follow")); + char *msg = toStr (attrs, NTXT ("msg")); + exp->process_desc_start_cmd (NULL, ts, variant, lineage, follow, msg); + exp->exec_started = true; + } + else if (strcmp (str, SP_JCMD_EXEC_ERROR) == 0) + { + exp->update_last_event (exp->exp_start_time + ts); // ts is 0-based + char *variant = toStr (attrs, NTXT ("variant")); + char *lineage = toStr (attrs, NTXT ("lineage")); + int follow = toInt (attrs, NTXT ("follow")); + char *msg = toStr (attrs, NTXT ("msg")); + exp->process_desc_started_cmd (NULL, ts, variant, lineage, follow, msg); + exp->exec_started = false; + } + else if (strcmp (str, SP_JCMD_JTHRSTART) == 0) + { + char *name = dbe_strdup (attrs->getValue (NTXT ("name"))); + char *grpname = dbe_strdup (attrs->getValue (NTXT ("grpname"))); + char *prntname = dbe_strdup (attrs->getValue (NTXT ("prntname"))); + str = attrs->getValue (NTXT ("tid")); + uint64_t tid = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("jthr")); + Vaddr jthr = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("jenv")); + Vaddr jenv = str ? strtoull (str, NULL, 0) : 0; + exp->process_jthr_start_cmd (NULL, name, grpname, prntname, tid, jthr, jenv, ts); + } + else if (strcmp (str, SP_JCMD_JTHREND) == 0) + { + str = attrs->getValue (NTXT ("tid")); + uint64_t tid = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("jthr")); + Vaddr jthr = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("jenv")); + Vaddr jenv = str ? strtoull (str, NULL, 0) : 0; + exp->process_jthr_end_cmd (NULL, tid, jthr, jenv, ts); + } + else if (strcmp (str, SP_JCMD_GCEND) == 0) + { + if (exp->getDataDescriptor (DATA_GCEVENT) == NULL) + exp->newDataDescriptor (DATA_GCEVENT); + exp->process_gc_end_cmd (ts); + } + else if (strcmp (str, SP_JCMD_GCSTART) == 0) + { + if (exp->getDataDescriptor (DATA_GCEVENT) == NULL) + exp->newDataDescriptor (DATA_GCEVENT); + exp->process_gc_start_cmd (ts); + } + else if (strcmp (str, SP_JCMD_PAUSE) == 0) + { + if (exp->resume_ts != MAX_TIME) + { + // data collection was active + hrtime_t delta = ts - exp->resume_ts; + exp->non_paused_time += delta; + exp->resume_ts = MAX_TIME; // collection is paused + } + StringBuilder sb; + str = attrs->getValue (NTXT ("name")); + if (str == NULL) + sb.sprintf (GTXT ("Pause: %ld.%09ld"), (long) (ts / NANOSEC), + (long) (ts % NANOSEC)); + else + sb.sprintf (GTXT ("Pause (%s): %ld.%09ld"), str, + (long) (ts / NANOSEC), (long) (ts % NANOSEC)); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + else if (strcmp (str, SP_JCMD_RESUME) == 0) + { + if (exp->resume_ts == MAX_TIME) + // data collection was paused + exp->resume_ts = ts; // remember start time + StringBuilder sb; + sb.sprintf (GTXT ("Resume: %ld.%09ld"), (long) (ts / NANOSEC), (long) (ts % NANOSEC)); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + if (exp->exp_start_time == ZERO_TIME) + exp->exp_start_time = ts; + } + else if (strcmp (str, SP_JCMD_THREAD_PAUSE) == 0) + { + str = attrs->getValue (NTXT ("tid")); + uint64_t tid = str ? strtoull (str, NULL, 0) : 0; + StringBuilder sb; + sb.sprintf (GTXT ("Thread %llu pause: %ld.%09ld"), (unsigned long long) tid, + (long) (ts / NANOSEC), (long) (ts % NANOSEC)); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + else if (strcmp (str, SP_JCMD_THREAD_RESUME) == 0) + { + str = attrs->getValue (NTXT ("tid")); + uint64_t tid = str ? strtoull (str, NULL, 0) : 0; + StringBuilder sb; + sb.sprintf (GTXT ("Thread %llu resume: %ld.%09ld"), (unsigned long long) tid, + (long) (ts / NANOSEC), (long) (ts % NANOSEC)); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + else if (strcmp (str, NTXT ("map")) == 0) + { + ts += exp->exp_start_time; + str = attrs->getValue (NTXT ("vaddr")); + Vaddr vaddr = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("size")); + int msize = str ? atoi (str) : 0; + str = attrs->getValue (NTXT ("foffset")); + int64_t offset = str ? strtoll (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("modes")); + int64_t modes = str ? strtoll (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("chksum")); + int64_t chksum = 0; + if (str) + chksum = Elf::normalize_checksum (strtoll (str, NULL, 0)); + char *name = (char *) attrs->getValue (NTXT ("name")); + str = attrs->getValue (NTXT ("object")); + if (strcmp (str, NTXT ("segment")) == 0) + { + if (strcmp (name, NTXT ("LinuxKernel")) == 0) + exp->process_Linux_kernel_cmd (ts); + else + exp->process_seg_map_cmd (NULL, ts, vaddr, msize, 0, + offset, modes, chksum, name); + } + else if (strcmp (str, NTXT ("function")) == 0) + { + exp->process_fn_load_cmd (dynfuncModule, name, vaddr, msize, ts); + dynfuncModule = NULL; + } + else if (strcmp (str, NTXT ("dynfunc")) == 0) + { + if (dynfuncModule == NULL) + { + dynfuncModule = dbeSession->createModule (exp->get_dynfunc_lo (DYNFUNC_SEGMENT), name); + dynfuncModule->flags |= MOD_FLAG_UNKNOWN; + dynfuncModule->set_file_name (dbe_strdup (dynfuncModule->getMainSrc ()->get_name ())); + } + (void) exp->create_dynfunc (dynfuncModule, + (char*) attrs->getValue (NTXT ("funcname")), vaddr, msize); + } + else if (strcmp (str, NTXT ("jcm")) == 0) + { + str = attrs->getValue (NTXT ("methodId")); + Vaddr mid = str ? strtoull (str, NULL, 0) : 0; + exp->process_jcm_load_cmd (NULL, mid, vaddr, msize, ts); + } + } + else if (strcmp (str, NTXT ("unmap")) == 0) + { + ts += exp->exp_start_time; + str = attrs->getValue (NTXT ("vaddr")); + Vaddr vaddr = str ? strtoull (str, NULL, 0) : 0; + exp->process_seg_unmap_cmd (NULL, ts, vaddr); + } + } + // end of code for event + } + else if (strcmp (qName, SP_TAG_PROFILE) == 0) + { + pushElem (EL_PROFILE); + const char *str = attrs->getValue (NTXT ("name")); + if (str == NULL) + return; + if (strcmp (str, NTXT ("profile")) == 0) + { + exp->coll_params.profile_mode = 1; + str = attrs->getValue (NTXT ("numstates")); + if (str != NULL) + exp->coll_params.lms_magic_id = atoi (str); + str = attrs->getValue (NTXT ("ptimer")); + if (str != NULL) + exp->coll_params.ptimer_usec = atoi (str); // microseconds + + PropDescr *mstate_prop = NULL; + char * stateNames [/*LMS_NUM_STATES*/] = LMS_STATE_STRINGS; + char * stateUNames[/*LMS_NUM_STATES*/] = LMS_STATE_USTRINGS; + { + dDscr = exp->newDataDescriptor (DATA_CLOCK); + PropDescr *prop = new PropDescr (PROP_MSTATE, NTXT ("MSTATE")); + prop->uname = dbe_strdup (GTXT ("Thread state")); + prop->vtype = TYPE_UINT32; + // (states added below) + dDscr->addProperty (prop); + mstate_prop = prop; + + prop = new PropDescr (PROP_NTICK, NTXT ("NTICK")); + prop->uname = dbe_strdup (GTXT ("Number of Profiling Ticks")); + prop->vtype = TYPE_UINT32; + dDscr->addProperty (prop); + } + + switch (exp->coll_params.lms_magic_id) + { + case LMS_MAGIC_ID_SOLARIS: + exp->register_metric (Metric::CP_TOTAL); + exp->register_metric (Metric::CP_TOTAL_CPU); + exp->register_metric (Metric::CP_LMS_USER); + exp->register_metric (Metric::CP_LMS_SYSTEM); + exp->register_metric (Metric::CP_LMS_TRAP); + exp->register_metric (Metric::CP_LMS_DFAULT); + exp->register_metric (Metric::CP_LMS_TFAULT); + exp->register_metric (Metric::CP_LMS_KFAULT); + exp->register_metric (Metric::CP_LMS_STOPPED); + exp->register_metric (Metric::CP_LMS_WAIT_CPU); + exp->register_metric (Metric::CP_LMS_SLEEP); + exp->register_metric (Metric::CP_LMS_USER_LOCK); + for (int ii = 0; ii < LMS_NUM_SOLARIS_MSTATES; ii++) + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + break; + case LMS_MAGIC_ID_ERKERNEL_KERNEL: + exp->register_metric (Metric::CP_KERNEL_CPU); + { + int ii = LMS_KERNEL_CPU; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + break; + case LMS_MAGIC_ID_ERKERNEL_USER: + exp->register_metric (Metric::CP_TOTAL_CPU); + exp->register_metric (Metric::CP_LMS_USER); + exp->register_metric (Metric::CP_LMS_SYSTEM); + { + int ii = LMS_KERNEL_CPU; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + ii = LMS_USER; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + ii = LMS_SYSTEM; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + break; + case LMS_MAGIC_ID_LINUX: + exp->register_metric (Metric::CP_TOTAL_CPU); + { + int ii = LMS_LINUX_CPU; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + break; + default: + // odd + break; + } + } + else if (strcmp (str, NTXT ("heaptrace")) == 0) + { + exp->coll_params.heap_mode = 1; + exp->leaklistavail = true; + exp->heapdataavail = true; + exp->register_metric (Metric::HEAP_ALLOC_BYTES); + exp->register_metric (Metric::HEAP_ALLOC_CNT); + exp->register_metric (Metric::HEAP_LEAK_BYTES); + exp->register_metric (Metric::HEAP_LEAK_CNT); + dDscr = exp->newDataDescriptor (DATA_HEAP); + } + else if (strcmp (str, NTXT ("iotrace")) == 0) + { + exp->coll_params.io_mode = 1; + exp->iodataavail = true; + exp->register_metric (Metric::IO_READ_TIME); + exp->register_metric (Metric::IO_READ_BYTES); + exp->register_metric (Metric::IO_READ_CNT); + exp->register_metric (Metric::IO_WRITE_TIME); + exp->register_metric (Metric::IO_WRITE_BYTES); + exp->register_metric (Metric::IO_WRITE_CNT); + exp->register_metric (Metric::IO_OTHER_TIME); + exp->register_metric (Metric::IO_OTHER_CNT); + exp->register_metric (Metric::IO_ERROR_TIME); + exp->register_metric (Metric::IO_ERROR_CNT); + dDscr = exp->newDataDescriptor (DATA_IOTRACE); + } + else if (strcmp (str, NTXT ("synctrace")) == 0) + { + exp->coll_params.sync_mode = 1; + str = attrs->getValue (NTXT ("threshold")); + if (str != NULL) + exp->coll_params.sync_threshold = atoi (str); + str = attrs->getValue (NTXT ("scope")); + if (str != NULL) + exp->coll_params.sync_scope = atoi (str); + else // Should only happen with old experiments; use the old default + exp->coll_params.sync_scope = SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA; + exp->register_metric (Metric::SYNC_WAIT_TIME); + exp->register_metric (Metric::SYNC_WAIT_COUNT); + dDscr = exp->newDataDescriptor (DATA_SYNCH); + } + else if (strcmp (str, NTXT ("omptrace")) == 0) + { + exp->coll_params.omp_mode = 1; + dDscr = exp->newDataDescriptor (DATA_OMP, DDFLAG_NOSHOW); + } + else if (strcmp (str, NTXT ("hwcounter")) == 0) + { + str = attrs->getValue (NTXT ("cpuver")); + int cpuver = str ? atoi (str) : 0; + char *counter = dbe_strdup (attrs->getValue (NTXT ("hwcname"))); + char *int_name = dbe_strdup (attrs->getValue (NTXT ("int_name"))); // may not be present + str = attrs->getValue (NTXT ("interval")); + int interval = str ? atoi (str) : 0; + str = attrs->getValue (NTXT ("tag")); + int tag = str ? atoi (str) : 0; + str = attrs->getValue (NTXT ("memop")); + int i_tpc = str ? atoi (str) : 0; + char *modstr = dbe_strdup (attrs->getValue (NTXT ("modstr"))); + exp->process_hwcounter_cmd (NULL, cpuver, counter, int_name, interval, tag, i_tpc, modstr); + dDscr = exp->newDataDescriptor (DATA_HWC); + } + else if (strcmp (str, NTXT ("hwsimctr")) == 0) + { + int cpuver = toInt (attrs, NTXT ("cpuver")); + char *hwcname = dbe_strdup (attrs->getValue (NTXT ("hwcname"))); + char *int_name = dbe_strdup (attrs->getValue (NTXT ("int_name"))); + char *metric = dbe_strdup (attrs->getValue (NTXT ("metric"))); + int reg = toInt (attrs, NTXT ("reg_num")); + int interval = toInt (attrs, NTXT ("interval")); + int timecvt = toInt (attrs, NTXT ("timecvt")); + int i_tpc = toInt (attrs, NTXT ("memop")); + int tag = toInt (attrs, NTXT ("tag")); + exp->process_hwsimctr_cmd (NULL, cpuver, hwcname, int_name, metric, reg, + interval, timecvt, i_tpc, tag); + dDscr = exp->newDataDescriptor (DATA_HWC); + } + else if (strcmp (str, NTXT ("dversion")) == 0) + exp->dversion = dbe_strdup (attrs->getValue (NTXT ("version"))); + else if (strcmp (str, NTXT ("jprofile")) == 0) + { + exp->has_java = true; + str = attrs->getValue (NTXT ("jversion")); + if (str != NULL) + exp->jversion = strdup (str); + } + else if (strcmp (str, NTXT ("datarace")) == 0) + { + exp->coll_params.race_mode = 1; + exp->racelistavail = true; + str = attrs->getValue (NTXT ("scheme")); + exp->coll_params.race_stack = str ? atoi (str) : 0; + exp->register_metric (Metric::RACCESS); + dDscr = exp->newDataDescriptor (DATA_RACE); + } + else if (strcmp (str, NTXT ("deadlock")) == 0) + { + exp->coll_params.deadlock_mode = 1; + exp->deadlocklistavail = true; + exp->register_metric (Metric::DEADLOCKS); + dDscr = exp->newDataDescriptor (DATA_DLCK); + } + } + /* XXX -- obsolete tag, but is still written to experiments */ + else if (strcmp (qName, SP_TAG_DATAPTR) == 0) + { + pushElem (EL_DATAPTR); + return; + } + else if (strcmp (qName, SP_TAG_PROFDATA) == 0) + { + pushElem (EL_PROFDATA); + // SS12 HWC experiments are not well structured + const char *fname = attrs->getValue (NTXT ("fname")); + if (fname && strcmp (fname, SP_HWCNTR_FILE) == 0) + dDscr = exp->newDataDescriptor (DATA_HWC); + } + else if (strcmp (qName, SP_TAG_PROFPCKT) == 0) + { + pushElem (EL_PROFPCKT); + const char *str = attrs->getValue (NTXT ("kind")); // see Pckt_type + int kind = str ? atoi (str) : -1; + if (kind < 0) + return; + if (exp->coll_params.omp_mode == 1) + { + if (kind == OMP_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP, DDFLAG_NOSHOW); + else if (kind == OMP2_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP2, DDFLAG_NOSHOW); + else if (kind == OMP3_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP3, DDFLAG_NOSHOW); + else if (kind == OMP4_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP4, DDFLAG_NOSHOW); + else if (kind == OMP5_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP5, DDFLAG_NOSHOW); + } + pDscr = exp->newPacketDescriptor (kind, dDscr); + return; + } + else if (strcmp (qName, SP_TAG_FIELD) == 0) + { + pushElem (EL_FIELD); + if (pDscr != NULL) + { + const char *name = attrs->getValue (NTXT ("name")); + if (name == NULL) + return; + int propID = dbeSession->registerPropertyName (name); + propDscr = new PropDescr (propID, name); + FieldDescr *fldDscr = new FieldDescr (propID, name); + + const char *str = attrs->getValue (NTXT ("type")); + if (str) + { + if (strcmp (str, NTXT ("INT32")) == 0) + fldDscr->vtype = TYPE_INT32; + else if (strcmp (str, NTXT ("UINT32")) == 0) + fldDscr->vtype = TYPE_UINT32; + else if (strcmp (str, NTXT ("INT64")) == 0) + fldDscr->vtype = TYPE_INT64; + else if (strcmp (str, NTXT ("UINT64")) == 0) + fldDscr->vtype = TYPE_UINT64; + else if (strcmp (str, NTXT ("STRING")) == 0) + fldDscr->vtype = TYPE_STRING; + else if (strcmp (str, NTXT ("DOUBLE")) == 0) + fldDscr->vtype = TYPE_DOUBLE; + else if (strcmp (str, NTXT ("DATE")) == 0) + { + fldDscr->vtype = TYPE_DATE; + const char *fmt = attrs->getValue (NTXT ("format")); + fldDscr->format = strdup (fmt ? fmt : ""); + } + } + propDscr->vtype = fldDscr->vtype; + + // TYPE_DATE is converted to TYPE_UINT64 in propDscr + if (fldDscr->vtype == TYPE_DATE) + propDscr->vtype = TYPE_UINT64; + + // Fix some types until they are fixed in libcollector + if (propID == PROP_VIRTPC || propID == PROP_PHYSPC) + { + if (fldDscr->vtype == TYPE_INT32) + propDscr->vtype = TYPE_UINT32; + else if (fldDscr->vtype == TYPE_INT64) + propDscr->vtype = TYPE_UINT64; + } + + // The following props get mapped to 32-bit values in readPacket + if (propID == PROP_CPUID || propID == PROP_THRID + || propID == PROP_LWPID) + propDscr->vtype = TYPE_UINT32; // override experiment property + + str = attrs->getValue (NTXT ("uname")); + if (str) + propDscr->uname = strdup (PTXT ((char*) str)); + str = attrs->getValue (NTXT ("noshow")); + if (str && atoi (str) != 0) + propDscr->flags |= PRFLAG_NOSHOW; + + if (dDscr == NULL) + { + StringBuilder sb; + sb.sprintf (GTXT ("*** Error: data parsing failed. Log file is corrupted.")); + exp->warnq->append (new Emsg (CMSG_ERROR, sb)); + throw new SAXException (sb.toString ()); + } + + dDscr->addProperty (propDscr); + str = attrs->getValue (NTXT ("offset")); + if (str) + fldDscr->offset = atoi (str); + pDscr->addField (fldDscr); + } + } + else if (strcmp (qName, SP_TAG_STATE) == 0) + { + pushElem (EL_STATE); + if (propDscr != NULL) + { + const char *str = attrs->getValue (NTXT ("value")); + int value = str ? atoi (str) : -1; + str = attrs->getValue (NTXT ("name")); + const char *ustr = attrs->getValue (NTXT ("uname")); + propDscr->addState (value, str, ustr); + } + } + else if (strcmp (qName, SP_TAG_DTRACEFATAL) == 0) + pushElem (EL_DTRACEFATAL); + else + { + StringBuilder sb; + sb.sprintf (GTXT ("*** Warning: unrecognized element %s"), qName); + exp->warnq->append (new Emsg (CMSG_WARN, sb)); + pushElem (EL_NONE); + } +} + +void +Experiment::ExperimentHandler::characters (char *ch, int start, int length) +{ + switch (curElem) + { + case EL_COLLECTOR: + exp->cversion = dbe_strndup (ch + start, length); + break; + case EL_PROCESS: + exp->process_arglist_cmd (NULL, dbe_strndup (ch + start, length)); + break; + case EL_EVENT: + free (text); + text = dbe_strndup (ch + start, length); + break; + default: + break; + } +} + +void +Experiment::ExperimentHandler::endElement (char*, char*, char*) +{ + if (curElem == EL_EVENT && mkind >= 0 && mnum >= 0) + { + char *str; + if (mec > 0) + str = dbe_sprintf ("%s -- %s", text != NULL ? text : "", strerror (mec)); + else + str = dbe_sprintf ("%s", text != NULL ? text : ""); + Emsg *msg = new Emsg (mkind, mnum, str); + if (mkind == CMSG_WARN) + { + if (mnum != COL_WARN_FSTYPE + || dbeSession->check_ignore_fs_warn () == false) + exp->warnq->append (msg); + else + exp->commentq->append (msg); + } + else if (mkind == CMSG_ERROR || mkind == CMSG_FATAL) + exp->errorq->append (msg); + else if (mkind == CMSG_COMMENT) + exp->commentq->append (msg); + else + delete msg; + mkind = (Cmsg_warn) - 1; + mnum = -1; + mec = -1; + } + else if (curElem == EL_PROFILE) + dDscr = NULL; + else if (curElem == EL_PROFPCKT) + pDscr = NULL; + else if (curElem == EL_FIELD) + propDscr = NULL; + free (text); + text = NULL; + popElem (); +} + +void +Experiment::ExperimentHandler::error (SAXParseException *e) +{ + StringBuilder sb; + sb.sprintf (GTXT ("%s at line %d, column %d"), + e->getMessage (), e->getLineNumber (), e->getColumnNumber ()); + char *msg = sb.toString (); + SAXException *e1 = new SAXException (msg); + free (msg); + throw ( e1); +} + +//-------------------------------------------------- Experiment + +Experiment::Experiment () +{ + groupId = 0; + userExpId = expIdx = -1; + founder_exp = NULL; + baseFounder = NULL; + children_exps = new Vector<Experiment*>; + loadObjs = new Vector<LoadObject*>; + loadObjMap = new StringMap<LoadObject*>(128, 128); + sourcesMap = NULL; + + // Initialize configuration information. + status = FAILURE; + start_sec = 0; + mtime = 0; + hostname = NULL; + username = NULL; + architecture = NULL; + os_version = NULL; + uarglist = NULL; + utargname = NULL; + ucwd = NULL; + cversion = NULL; + dversion = NULL; + jversion = NULL; + exp_maj_version = 0; + exp_min_version = 0; + platform = Unknown; + wsize = Wnone; + page_size = 4096; + npages = 0; + stack_base = 0xf0000000; + broken = 1; + obsolete = 0; + hwc_bogus = 0; + hwc_lost_int = 0; + hwc_scanned = 0; + hwc_default = false; + invalid_packet = 0; + + // clear HWC event stats + dsevents = 0; + dsnoxhwcevents = 0; + + memset (&coll_params, 0, sizeof (coll_params)); + ncpus = 0; + minclock = 0; + maxclock = 0; + clock = 0; + varclock = 0; + exec_started = false; + timelineavail = true; + leaklistavail = false; + heapdataavail = false; + iodataavail = false; + dataspaceavail = false; + ifreqavail = false; + racelistavail = false; + deadlocklistavail = false; + ompavail = false; + tiny_threshold = -1; + pid = 0; + ppid = 0; + pgrp = 0; + sid = 0; + + gc_duration = ZERO_TIME; + exp_start_time = ZERO_TIME; // not known. Wall-clock hrtime (not zero based) + last_event = ZERO_TIME; // not known. Wall-clock hrtime (not zero based) + non_paused_time = 0; // 0 non-paused time (will sum as experiment is processed) + resume_ts = 0; // by default, collection is "resumed" (not paused) from time=0 + need_swap_endian = false; + exp_rel_start_time_set = false; + exp_rel_start_time = ZERO_TIME; + has_java = false; + hex_field_width = 8; + hw_cpuver = CPUVER_UNDEFINED; + machinemodel = NULL; + expt_name = NULL; + arch_name = NULL; + fndr_arch_name = NULL; + logFile = NULL; + + dataDscrs = new Vector<DataDescriptor*>; + for (int i = 0; i < DATA_LAST; ++i) + dataDscrs->append (NULL); + + pcktDscrs = new Vector<PacketDescriptor*>; + blksz = PROFILE_BUFFER_CHUNK; + jthreads = new Vector<JThread*>; + jthreads_idx = new Vector<JThread*>; + gcevents = new Vector<GCEvent*>; + gcevent_last_used = (GCEvent *) NULL; + heapUnmapEvents = new Vector<UnmapChunk*>; + cstack = NULL; + cstackShowHide = NULL; + frmpckts = new Vector<RawFramePacket*>; + typedef DefaultMap2D<uint32_t, hrtime_t, uint64_t> OmpMap0; + mapPRid = new OmpMap0 (OmpMap0::Interval); + typedef DefaultMap2D<uint32_t, hrtime_t, void*> OmpMap; + mapPReg = new OmpMap (OmpMap::Interval); + mapTask = new OmpMap (OmpMap::Interval); + openMPdata = NULL; + archiveMap = NULL; + nnodes = 0; + nchunks = 0; + chunks = 0; + uidHTable = NULL; + uidnodes = new Vector<UIDnode*>; + mrecs = new Vector<MapRecord*>; + samples = new Vector<Sample*>; + sample_last_used = (Sample *) NULL; + first_sample_label = (char*) NULL; + fDataMap = NULL; + vFdMap = NULL; + resolveFrameInfo = true; + discardTiny = false; + init (); +} + +Experiment::~Experiment () +{ + fini (); + free (coll_params.linetrace); + for (int i = 0; i < MAX_HWCOUNT; i++) + { + free (coll_params.hw_aux_name[i]); + free (coll_params.hw_username[i]); + } + free (hostname); + free (username); + free (architecture); + free (os_version); + free (uarglist); + free (utargname); + free (ucwd); + free (cversion); + free (dversion); + free (jversion); + delete logFile; + free (expt_name); + free (arch_name); + free (fndr_arch_name); + delete jthreads_idx; + delete cstack; + delete cstackShowHide; + delete mapPRid; + delete mapPReg; + delete mapTask; + delete openMPdata; + destroy_map (DbeFile *, archiveMap); + delete[] uidHTable; + delete uidnodes; + delete mrecs; + delete children_exps; + delete loadObjs; + delete loadObjMap; + delete sourcesMap; + free (first_sample_label); + free (machinemodel); + + dataDscrs->destroy (); + delete dataDscrs; + pcktDscrs->destroy (); + delete pcktDscrs; + jthreads->destroy (); + delete jthreads; + gcevents->destroy (); + delete gcevents; + heapUnmapEvents->destroy (); + delete heapUnmapEvents; + frmpckts->destroy (); + delete frmpckts; + samples->destroy (); + delete samples; + delete fDataMap; + delete vFdMap; + + for (long i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; +} + +void +Experiment::init_cache () +{ + if (smemHTable) + return; + smemHTable = new SegMem*[HTableSize]; + instHTable = new DbeInstr*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + { + smemHTable[i] = NULL; + instHTable[i] = NULL; + } + uidHTable = new UIDnode*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + uidHTable[i] = NULL; + + cstack = CallStack::getInstance (this); + cstackShowHide = CallStack::getInstance (this); +} + +void +Experiment::init () +{ + userLabels = NULL; + seg_items = new Vector<SegMem*>; + maps = new PRBTree (); + jmaps = NULL; // used by JAVA_CLASSES only + jmidHTable = NULL; + smemHTable = NULL; + instHTable = NULL; + min_thread = (uint64_t) - 1; + max_thread = 0; + thread_cnt = 0; + min_lwp = (uint64_t) - 1; + max_lwp = 0; + lwp_cnt = 0; + min_cpu = (uint64_t) - 1; + max_cpu = 0; + cpu_cnt = 0; + + commentq = new Emsgqueue (NTXT ("commentq")); + runlogq = new Emsgqueue (NTXT ("runlogq")); + errorq = new Emsgqueue (NTXT ("errorq")); + warnq = new Emsgqueue (NTXT ("warnq")); + notesq = new Emsgqueue (NTXT ("notesq")); + pprocq = new Emsgqueue (NTXT ("pprocq")); + ifreqq = NULL; + + metrics = new Vector<BaseMetric*>; + tagObjs = new Vector<Vector<Histable*>*>; + tagObjs->store (PROP_THRID, new Vector<Histable*>); + tagObjs->store (PROP_LWPID, new Vector<Histable*>); + tagObjs->store (PROP_CPUID, new Vector<Histable*>); + tagObjs->store (PROP_EXPID, new Vector<Histable*>); + sparse_threads = false; +} + +void +Experiment::fini () +{ + seg_items->destroy (); + delete seg_items; + delete maps; + delete jmaps; + delete[] smemHTable; + delete[] instHTable; + delete jmidHTable; + delete commentq; + delete runlogq; + delete errorq; + delete warnq; + delete notesq; + delete pprocq; + if (ifreqq != NULL) + { + delete ifreqq; + ifreqq = NULL; + } + + int index; + BaseMetric *mtr; + Vec_loop (BaseMetric*, metrics, index, mtr) + { + dbeSession->drop_metric (mtr); + } + delete metrics; + tagObjs->fetch (PROP_THRID)->destroy (); + tagObjs->fetch (PROP_LWPID)->destroy (); + tagObjs->fetch (PROP_CPUID)->destroy (); + tagObjs->fetch (PROP_EXPID)->destroy (); + tagObjs->destroy (); + delete tagObjs; +} + +// These are the data files which can be read in parallel +// for multiple sub-experiments. +// Postpone calling resolve_frame_info() +void +Experiment::read_experiment_data (bool read_ahead) +{ + + read_frameinfo_file (); + if (read_ahead) + { + resolveFrameInfo = false; + (void) get_profile_events (); + resolveFrameInfo = true; + } +} + +Experiment::Exp_status +Experiment::open_epilogue () +{ + + // set up mapping for tagObj(PROP_EXPID) + (void) mapTagValue (PROP_EXPID, userExpId); + + post_process (); + if (last_event != ZERO_TIME) + { // if last_event is known + StringBuilder sb; + hrtime_t ts = last_event - exp_start_time; + sb.sprintf (GTXT ("Experiment Ended: %ld.%09ld\nData Collection Duration: %ld.%09ld"), + (long) (ts / NANOSEC), (long) (ts % NANOSEC), + (long) (non_paused_time / NANOSEC), + (long) (non_paused_time % NANOSEC)); + runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // Check for incomplete experiment, and inform the user + if (status == INCOMPLETE) + { + if (exec_started == true) + // experiment ended with the exec, not abnormally + status = SUCCESS; + else + { + char * cmnt = GTXT ("*** Note: experiment was not closed"); + commentq->append (new Emsg (CMSG_COMMENT, cmnt)); + // runlogq->append(new Emsg(CMSG_COMMENT, cmnt)); + } + } + // write a descriptive header for the experiment + write_header (); + return status; +} + +Experiment::Exp_status +Experiment::open (char *path) +{ + + // Find experiment directory + if (find_expdir (path) != SUCCESS) + // message will have been queued and status set + return status; + + // Get creation time for experiment + struct stat64 st; + if (dbe_stat (path, &st) == 0) + mtime = st.st_mtime; + + // Read the warnings file + read_warn_file (); + + // Open the log file + read_log_file (); + if (status == SUCCESS && last_event // last event is initialized + && (last_event - exp_start_time) / 1000000 < tiny_threshold) + { + // Process "tiny_threshold" (SP_ANALYZER_DISCARD_TINY_EXPERIMENTS) + // At this point, we've only processed log.xml. + // Note: if an experiment terminated abnormally, last_event will not yet + // represent events from clock profiling and other metrics. + // Other events will often have timestamps after the last log.xml entry. + discardTiny = true; + return status; + } + if (status == FAILURE) + { + if (logFile->get_status () == ExperimentFile::EF_FAILURE) + { + Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: log file in experiment cannot be read")); + errorq->append (m); + } + else if (fetch_errors () == NULL) + { + if (broken == 1) + { + Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: log does not show target starting")); + errorq->append (m); + } + else + { + Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: log file in experiment could not be parsed")); + errorq->append (m); + } + } + return status; + } + init_cache (); + if (varclock != 0) + { + StringBuilder sb; + sb.sprintf ( + GTXT ("*** Warning: system has variable clock frequency, which may cause variable execution times and inaccurate conversions of cycle counts into time.")); + warnq->append (new Emsg (CMSG_WARN, sb)); + } + + // Read the notes file + read_notes_file (); + read_labels_file (); + read_archives (); + + // The log file shows experiment started + read_java_classes_file (); + + read_map_file (); + + // Dyntext file has to be processed after loadobjects file + // as we need to be able to map (vaddr,ts) to dynamic functions. + read_dyntext_file (); + + // Read the overview file and create samples. + // Profiling data hasn't been read yet so we may have + // events after the last recorded sample. + // We'll create a fake sample to cover all those + // events later. + read_overview_file (); + + // Check if instruction frequency data is available + read_ifreq_file (); + + // Check if OMP data is available + read_omp_file (); + + return status; +} + +/* XXX -- update() is a no-op now, but may be needed for auto-update */ +Experiment::Exp_status +Experiment::update () +{ + return status; +} + +void +Experiment::append (LoadObject *lo) +{ + loadObjs->append (lo); + char *obj_name = lo->get_pathname (); + char *bname = get_basename (obj_name); + loadObjMap->put (obj_name, lo); + loadObjMap->put (bname, lo); + if (lo->flags & SEG_FLAG_EXE) + loadObjMap->put (COMP_EXE_NAME, lo); +} + +void +Experiment::read_notes_file () +{ + Emsg *m; + + // Open log file: + char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_NOTES_FILE); + FILE *f = fopen (fname, NTXT ("r")); + free (fname); + if (f == NULL) + return; + if (!dbeSession->is_interactive ()) + { + m = new Emsg (CMSG_COMMENT, NTXT ("Notes:")); + notesq->append (m); + } + + while (1) + { + char str[MAXPATHLEN]; + char *e = fgets (str, ((int) sizeof (str)) - 1, f); + if (e == NULL) + { + if (!dbeSession->is_interactive ()) + { + m = new Emsg (CMSG_COMMENT, + "============================================================"); + notesq->append (m); + } + break; + } + size_t i = strlen (str); + if (i > 0 && str[i - 1] == '\n') + // remove trailing nl + str[i - 1] = 0; + m = new Emsg (CMSG_COMMENT, str); + notesq->append (m); + } + (void) fclose (f); +} + +int +Experiment::save_notes (char* text, bool handle_file) +{ + if (handle_file) + { + FILE *fnotes; + char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_NOTES_FILE); + fnotes = fopen (fname, NTXT ("w")); + free (fname); + if (fnotes != NULL) + { + (void) fprintf (fnotes, NTXT ("%s"), text); + fclose (fnotes); + } + else + return 1; // Cannot write file + } + notesq->clear (); + Emsg *m = new Emsg (CMSG_COMMENT, text); + notesq->append (m); + + return 0; +} + +int +Experiment::delete_notes (bool handle_file) +{ + if (handle_file) + { + char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_NOTES_FILE); + if (unlink (fname) != 0) + { + free (fname); + return 1; // Cannot delete file + } + free (fname); + } + notesq->clear (); + return 0; +} + +int +Experiment::read_warn_file () +{ + int local_status = SUCCESS; + + ExperimentFile *warnFile = new ExperimentFile (this, SP_WARN_FILE); + if (warnFile == NULL) + return FAILURE; + if (!warnFile->open ()) + { + delete warnFile; + return FAILURE; + } + SAXParserFactory *factory = SAXParserFactory::newInstance (); + SAXParser *saxParser = factory->newSAXParser (); + DefaultHandler *dh = new ExperimentHandler (this); + try + { + saxParser->parse ((File*) warnFile->fh, dh); + } + catch (SAXException *e) + { + // Fatal error in the parser + StringBuilder sb; + sb.sprintf (NTXT ("%s: %s"), SP_WARN_FILE, e->getMessage ()); + char *str = sb.toString (); + Emsg *m = new Emsg (CMSG_FATAL, str); + errorq->append (m); + local_status = FAILURE; + delete e; + } + delete warnFile; + delete dh; + delete saxParser; + delete factory; + + return local_status; +} + +int +Experiment::read_log_file () +{ + if (logFile == NULL) + logFile = new ExperimentFile (this, SP_LOG_FILE); + if (!logFile->open ()) + { + status = FAILURE; + return status; + } + + SAXParserFactory *factory = SAXParserFactory::newInstance (); + SAXParser *saxParser = factory->newSAXParser (); + DefaultHandler *dh = new ExperimentHandler (this); + try + { + saxParser->parse ((File*) logFile->fh, dh); + } + catch (SAXException *e) + { + // Fatal error in the parser + StringBuilder sb; + if (obsolete == 1) + sb.sprintf (NTXT ("%s"), e->getMessage ()); + else + sb.sprintf (NTXT ("%s: %s"), SP_LOG_FILE, e->getMessage ()); + char *str = sb.toString (); + Emsg *m = new Emsg (CMSG_FATAL, str); + errorq->append (m); + status = FAILURE; + delete e; + } + logFile->close (); + dbeSession->register_metric (GTXT ("IPC"), GTXT ("Instructions Per Cycle"), + NTXT ("insts/cycles")); + dbeSession->register_metric (GTXT ("CPI"), GTXT ("Cycles Per Instruction"), + NTXT ("cycles/insts")); + dbeSession->register_metric (GTXT ("K_IPC"), + GTXT ("Kernel Instructions Per Cycle"), + NTXT ("K_insts/K_cycles")); + dbeSession->register_metric (GTXT ("K_CPI"), + GTXT ("Kernel Cycles Per Instruction"), + NTXT ("K_cycles/K_insts")); + + delete dh; + delete saxParser; + delete factory; + + return status; +} + +//////////////////////////////////////////////////////////////////////////////// +// class Experiment::ExperimentLabelsHandler +// + +class Experiment::ExperimentLabelsHandler : public DefaultHandler +{ +public: + + ExperimentLabelsHandler (Experiment *_exp) + { + exp = _exp; + } + + ~ExperimentLabelsHandler () { }; + void startDocument () { } + void endDocument () { } + void endElement (char * /*uri*/, char * /*localName*/, char * /*qName*/) { } + void characters (char * /*ch*/, int /*start*/, int /*length*/) { } + void ignorableWhitespace (char*, int, int) { } + void error (SAXParseException * /*e*/) { } + + void startElement (char *uri, char *localName, char *qName, Attributes *attrs); + +private: + + inline const char * + s2s (const char *s) + { + return s ? s : "NULL"; + } + + Experiment *exp; + char *hostname; + hrtime_t time, tstamp; +}; + +void +Experiment::ExperimentLabelsHandler::startElement (char*, char*, char *qName, + Attributes *attrs) +{ + DEBUG_CODE if (DEBUG_SAXPARSER) dump_startElement (qName, attrs); + if (qName == NULL || strcmp (qName, NTXT ("id")) != 0) + return; + char *name = NULL, *all_times = NULL, *comment = NULL, *hostName = NULL; + long startSec = 0; + // long tm_zone = 0; + hrtime_t startHrtime = (hrtime_t) 0; + long long lbl_ts = 0; + int relative = 0; + timeval start_tv; + start_tv.tv_usec = start_tv.tv_sec = 0; + for (int i = 0, sz = attrs ? attrs->getLength () : 0; i < sz; i++) + { + const char *qn = attrs->getQName (i); + const char *vl = attrs->getValue (i); + if (strcmp (qn, NTXT ("name")) == 0) + name = dbe_xml2str (vl); + else if (strcmp (qn, NTXT ("cmd")) == 0) + all_times = dbe_xml2str (vl); + else if (strcmp (qn, NTXT ("comment")) == 0) + comment = dbe_xml2str (vl); + else if (strcmp (qn, NTXT ("relative")) == 0) + relative = atoi (vl); + else if (strcmp (qn, NTXT ("hostname")) == 0) + hostName = dbe_xml2str (vl); + else if (strcmp (qn, NTXT ("time")) == 0) + startSec = atol (vl); + else if (strcmp (qn, NTXT ("tstamp")) == 0) + startHrtime = parseTStamp (vl); + else if (strcmp (qn, NTXT ("lbl_ts")) == 0) + { + if (*vl == '-') + lbl_ts = -parseTStamp (vl + 1); + else + lbl_ts = parseTStamp (vl); + } + } + if (name == NULL || hostName == NULL || (all_times == NULL && comment == NULL)) + { + free (name); + free (hostName); + free (all_times); + free (comment); + return; + } + UserLabel *lbl = new UserLabel (name); + lbl->comment = comment; + lbl->hostname = hostName; + lbl->start_sec = startSec; + lbl->start_hrtime = startHrtime; + exp->userLabels->append (lbl); + if (all_times) + { + lbl->all_times = all_times; + lbl->start_tv = start_tv; + lbl->relative = relative; + if (relative == UserLabel::REL_TIME) + lbl->atime = lbl_ts; + else + { // relative == UserLabel::CUR_TIME + long long delta = 0; + if (exp->hostname && strcmp (lbl->hostname, exp->hostname) == 0) + delta = lbl_ts + (lbl->start_hrtime - exp->exp_start_time); + else + for (int i = 0; i < exp->userLabels->size (); i++) + { + UserLabel *firstLbl = exp->userLabels->fetch (i); + if (strcmp (lbl->hostname, firstLbl->hostname) == 0) + { + delta = lbl_ts + (lbl->start_hrtime - firstLbl->start_hrtime) + + ((long long) (firstLbl->start_sec - exp->start_sec)) * NANOSEC; + break; + } + } + lbl->atime = delta > 0 ? delta : 0; + } + } +} + +static int +sortUserLabels (const void *a, const void *b) +{ + UserLabel *l1 = *((UserLabel **) a); + UserLabel *l2 = *((UserLabel **) b); + int v = dbe_strcmp (l1->name, l2->name); + if (v != 0) + return v; + if (l1->atime < l2->atime) + return -1; + else if (l1->atime > l2->atime) + return 1; + if (l1->id < l2->id) + return -1; + else if (l1->id > l2->id) + return 1; + return 0; +} + +static char * +append_string (char *s, char *str) +{ + if (s == NULL) + return dbe_strdup (str); + char *new_s = dbe_sprintf (NTXT ("%s %s"), s, str); + free (s); + return new_s; +} + +void +Experiment::read_labels_file () +{ + ExperimentFile *fp = new ExperimentFile (this, SP_LABELS_FILE); + if (!fp->open ()) + { + delete fp; + return; + } + userLabels = new Vector<UserLabel*>(); + SAXParserFactory *factory = SAXParserFactory::newInstance (); + SAXParser *saxParser = factory->newSAXParser (); + DefaultHandler *dh = new ExperimentLabelsHandler (this); + try + { + saxParser->parse ((File*) fp->fh, dh); + } + catch (SAXException *e) + { + // Fatal error in the parser + StringBuilder sb; + sb.sprintf (NTXT ("%s: %s"), SP_LABELS_FILE, e->getMessage ()); + char *str = sb.toString (); + Emsg *m = new Emsg (CMSG_FATAL, str); + errorq->append (m); + delete e; + } + fp->close (); + delete fp; + delete dh; + delete saxParser; + delete factory; + + userLabels->sort (sortUserLabels); + UserLabel::dump ("After sortUserLabels:", userLabels); + UserLabel *ulbl = NULL; + for (int i = 0, sz = userLabels->size (); i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + if (ulbl == NULL) + ulbl = new UserLabel (lbl->name); + else if (dbe_strcmp (lbl->name, ulbl->name) != 0) + { // new Label + ulbl->register_user_label (groupId); + if (ulbl->expr == NULL) + delete ulbl; + ulbl = new UserLabel (lbl->name); + } + if (lbl->all_times) + { + if (strncmp (lbl->all_times, NTXT ("start"), 5) == 0) + { + if (!ulbl->start_f) + { + ulbl->start_f = true; + ulbl->timeStart = lbl->atime; + } + } + else + { // stop + if (!ulbl->start_f) + continue; + ulbl->all_times = append_string (ulbl->all_times, lbl->all_times); + ulbl->stop_f = true; + ulbl->timeStop = lbl->atime; + ulbl->gen_expr (); + } + } + if (lbl->comment != NULL) + ulbl->comment = append_string (ulbl->comment, lbl->comment); + } + if (ulbl) + { + ulbl->register_user_label (groupId); + if (ulbl->expr == NULL) + delete ulbl; + } + Destroy (userLabels); +} + +void +Experiment::read_archives () +{ + if (founder_exp) + return; + char *allocated_str = NULL; + char *nm = get_arch_name (); + DIR *exp_dir = opendir (nm); + if (exp_dir == NULL) + { + if (founder_exp == NULL) + { + // Check if the user uses a subexperiment only + nm = dbe_sprintf (NTXT ("%s/../%s"), expt_name, SP_ARCHIVES_DIR); + exp_dir = opendir (nm); + if (exp_dir == NULL) + { + free (nm); + return; + } + allocated_str = nm; + } + else + return; + } + + StringBuilder sb; + sb.append (nm); + sb.append ('/'); + int dlen = sb.length (); + free (allocated_str); + archiveMap = new StringMap<DbeFile *>(); + + struct dirent *entry = NULL; + while ((entry = readdir (exp_dir)) != NULL) + { + char *dname = entry->d_name; + if (dname[0] == '.' + && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))) + // skip links to ./ or ../ + continue; + sb.setLength (dlen); + sb.append (dname); + char *fnm = sb.toString (); + DbeFile *df = new DbeFile (fnm); + df->set_location (fnm); + df->filetype |= DbeFile::F_FILE; + df->inArchive = true; + df->experiment = this; + archiveMap->put (dname, df); + free (fnm); + } + closedir (exp_dir); +} + +static char * +gen_file_name (const char *packet_name, const char *src_name) +{ + char *fnm, *bname = get_basename (packet_name); + if (bname == packet_name) + fnm = dbe_strdup (src_name); + else + fnm = dbe_sprintf ("%.*s%s", (int) (bname - packet_name), + packet_name, src_name); + + // convert "java.lang.Object/Integer.java" => "java/lang/Object/Integer.java" + bname = get_basename (fnm); + for (char *s = fnm; s < bname; s++) + if (*s == '.') + *s = '/'; + return fnm; +} + +static char * +get_jlass_name (const char *nm) +{ + // Convert "Ljava/lang/Object;" => "java/lang/Object.class" + if (*nm == 'L') + { + size_t len = strlen (nm); + if (nm[len - 1] == ';') + return dbe_sprintf ("%.*s.class", (int) (len - 2), nm + 1); + } + return dbe_strdup (nm); +} + +static char * +get_jmodule_name (const char *nm) +{ + // convert "Ljava/lang/Object;" => "java.lang.Object" + if (*nm == 'L') + { + size_t len = strlen (nm); + if (nm[len - 1] == ';') + { + char *mname = dbe_sprintf (NTXT ("%.*s"), (int) (len - 2), nm + 1); + for (char *s = mname; *s; s++) + if (*s == '/') + *s = '.'; + return mname; + } + } + return dbe_strdup (nm); +} + +LoadObject * +Experiment::get_j_lo (const char *className, const char *fileName) +{ + char *class_name = get_jlass_name (className); + Dprintf (DUMP_JCLASS_READER, + "Experiment::get_j_lo: className='%s' class_name='%s' fileName='%s'\n", + STR (className), STR (class_name), STR (fileName)); + LoadObject *lo = loadObjMap->get (class_name); + if (lo == NULL) + { + lo = createLoadObject (class_name, fileName); + lo->type = LoadObject::SEG_TEXT; + lo->mtime = (time_t) 0; + lo->size = 0; + lo->set_platform (Java, wsize); + lo->dbeFile->filetype |= DbeFile::F_FILE | DbeFile::F_JAVACLASS; + append (lo); + Dprintf (DUMP_JCLASS_READER, + "Experiment::get_j_lo: creates '%s' location='%s'\n", + STR (lo->get_name ()), STR (lo->dbeFile->get_location (false))); + } + free (class_name); + return lo; +} + +Module * +Experiment::get_jclass (const char *className, const char *fileName) +{ + LoadObject *lo = get_j_lo (className, NULL); + char *mod_name = get_jmodule_name (className); + Module *mod = lo->find_module (mod_name); + if (mod == NULL) + { + mod = dbeSession->createClassFile (mod_name); + mod->loadobject = lo; + if (strcmp (fileName, NTXT ("<Unknown>")) != 0) + mod->set_file_name (gen_file_name (lo->get_pathname (), fileName)); + else + mod->set_file_name (dbe_strdup (fileName)); + lo->append_module (mod); + mod_name = NULL; + } + else if (mod->file_name && (strcmp (mod->file_name, "<Unknown>") == 0) + && strcmp (fileName, "<Unknown>") != 0) + mod->set_file_name (gen_file_name (lo->get_pathname (), fileName)); + Dprintf (DUMP_JCLASS_READER, + "Experiment::get_jclass: class_name='%s' mod_name='%s' fileName='%s'\n", + mod->loadobject->get_pathname (), mod->get_name (), mod->file_name); + free (mod_name); + return mod; +} + +#define ARCH_STRLEN(s) ( ( strlen(s) + 4 ) & ~0x3 ) + +int +Experiment::read_java_classes_file () +{ + char *data_file_name = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_JCLASSES_FILE); + Data_window *dwin = new Data_window (data_file_name); + free (data_file_name); + if (dwin->not_opened ()) + { + delete dwin; + return INCOMPLETE; + } + dwin->need_swap_endian = need_swap_endian; + jmaps = new PRBTree (); + jmidHTable = new DbeCacheMap<unsigned long long, JMethod>; + + hrtime_t cur_loaded = 0; + Module *cur_mod = NULL; + for (int64_t offset = 0;;) + { + CM_Packet *cpkt = (CM_Packet*) dwin->bind (offset, sizeof (CM_Packet)); + if (cpkt == NULL) + break; + uint16_t v16 = (uint16_t) cpkt->tsize; + size_t cpktsize = dwin->decode (v16); + cpkt = (CM_Packet*) dwin->bind (offset, cpktsize); + if ((cpkt == NULL) || (cpktsize == 0)) + { + char *buf = dbe_sprintf (GTXT ("archive file malformed %s"), + arch_name); + errorq->append (new Emsg (CMSG_ERROR, buf)); + free (buf); + break; + } + v16 = (uint16_t) cpkt->type; + v16 = dwin->decode (v16); + switch (v16) + { + case ARCH_JCLASS: + { + ARCH_jclass *ajcl = (ARCH_jclass*) cpkt; + uint64_t class_id = dwin->decode (ajcl->class_id); + char *className = ((char*) ajcl) + sizeof (*ajcl); + char *fileName = className + ARCH_STRLEN (className); + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: ARCH_JCLASS(Ox%x)" + "class_id=Ox%llx className='%s' fileName='%s' \n", + (int) v16, (long long) class_id, className, fileName); + cur_mod = NULL; + if (*className == 'L') + { // Old libcollector generated '[' (one array dimension). + cur_mod = get_jclass (className, fileName); + cur_loaded = dwin->decode (ajcl->tstamp); + jmaps->insert (class_id, cur_loaded, cur_mod); + } + break; + } + case ARCH_JCLASS_LOCATION: + { + ARCH_jclass_location *ajcl = (ARCH_jclass_location *) cpkt; + uint64_t class_id = dwin->decode (ajcl->class_id); + char *className = ((char*) ajcl) + sizeof (*ajcl); + char *fileName = className + ARCH_STRLEN (className); + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: ARCH_JCLASS_LOCATION(Ox%x)" + "class_id=Ox%llx className='%s' fileName='%s' \n", + (int) v16, (long long) class_id, className, fileName); + get_j_lo (className, fileName); + break; + } + case ARCH_JMETHOD: + { + if (cur_mod == NULL) + break; + ARCH_jmethod *ajmt = (ARCH_jmethod*) cpkt; + uint64_t method_id = dwin->decode (ajmt->method_id); + char *s_name = ((char*) ajmt) + sizeof (*ajmt); + char *s_signature = s_name + ARCH_STRLEN (s_name); + char *fullname = dbe_sprintf ("%s.%s", cur_mod->get_name (), s_name); + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: ARCH_JMETHOD(Ox%x) " + "method_id=Ox%llx name='%s' signature='%s' fullname='%s'\n", + (int) v16, (long long) method_id, s_name, + s_signature, fullname); + JMethod *jmthd = cur_mod->find_jmethod (fullname, s_signature); + if (jmthd == NULL) + { + jmthd = dbeSession->createJMethod (); + jmthd->size = (unsigned) - 1; // unknown until later (maybe) + jmthd->module = cur_mod; + jmthd->set_signature (s_signature); + jmthd->set_name (fullname); + cur_mod->functions->append (jmthd); + cur_mod->loadobject->functions->append (jmthd); + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: ARCH_JMETHOD CREATE fullname=%s\n", + fullname); + } + jmaps->insert (method_id, cur_loaded, jmthd); + free (fullname); + break; + } + default: + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: type=Ox%x (%d) cpktsize=%d\n", + (int) v16, (int) v16, (int) cpktsize); + break; // ignore unknown packets + } + offset += cpktsize; + } + delete dwin; + return SUCCESS; +} + +void +Experiment::read_map_file () +{ + ExperimentFile *mapFile = new ExperimentFile (this, SP_MAP_FILE); + if (!mapFile->open ()) + { + delete mapFile; + return; + } + + SAXParserFactory *factory = SAXParserFactory::newInstance (); + SAXParser *saxParser = factory->newSAXParser (); + DefaultHandler *dh = new ExperimentHandler (this); + try + { + saxParser->parse ((File*) mapFile->fh, dh); + } + catch (SAXException *e) + { + // Fatal error in the parser + StringBuilder sb; + sb.sprintf (NTXT ("%s: %s"), SP_MAP_FILE, e->getMessage ()); + char *str = sb.toString (); + Emsg *m = new Emsg (CMSG_FATAL, str); + errorq->append (m); + status = FAILURE; + free (str); + delete e; + } + delete mapFile; + delete dh; + delete saxParser; + delete factory; + + for (int i = 0, sz = mrecs ? mrecs->size () : 0; i < sz; i++) + { + MapRecord *mrec = mrecs->fetch (i); + SegMem *smem, *sm_lo, *sm_hi; + switch (mrec->kind) + { + case MapRecord::LOAD: + smem = new SegMem; + smem->base = mrec->base; + smem->size = mrec->size; + smem->load_time = mrec->ts; + smem->unload_time = MAX_TIME; + smem->obj = mrec->obj; + smem->set_file_offset (mrec->foff); + seg_items->append (smem); // add to the master list + + // Check if the new segment overlaps other active segments + sm_lo = (SegMem*) maps->locate (smem->base, smem->load_time); + if (sm_lo && sm_lo->base + sm_lo->size > smem->base) + { + // check to see if it is a duplicate record: same address and size, and + if ((smem->base == sm_lo->base) && (smem->size == sm_lo->size)) + { + // addresses and sizes match, check name + if (strstr (smem->obj->get_name (), sm_lo->obj->get_name ()) != NULL + || strstr (sm_lo->obj->get_name (), smem->obj->get_name ()) != NULL) + // this is a duplicate; just move on the the next map record + continue; + fprintf (stderr, + GTXT ("*** Warning: Segment `%s' loaded with same address, size as `%s' [0x%llx-0x%llx]\n"), + smem->obj->get_name (), sm_lo->obj->get_name (), + sm_lo->base, sm_lo->base + sm_lo->size); + } + + // Not a duplicate; implicitly unload the old one + // Note: implicit unloading causes high <Unknown> + // when such overlapping is bogus + StringBuilder sb; + sb.sprintf (GTXT ("*** Warning: Segment %s [0x%llx-0x%llx] overlaps %s [0x%llx-0x%llx], which has been implicitly unloaded"), + smem->obj->get_name (), smem->base, smem->base + smem->size, + sm_lo->obj->get_name (), sm_lo->base, sm_lo->base + sm_lo->size); + warnq->append (new Emsg (CMSG_WARN, sb)); + } + + // now look for other segments with which this might overlap + sm_hi = (SegMem*) maps->locate_up (smem->base, smem->load_time); + while (sm_hi && sm_hi->base < smem->base + smem->size) + { + + // Note: implicit unloading causes high <Unknown> when such overlapping is bogus + // maps->remove( sm_hi->base, smem->load_time ); + StringBuilder sb; + sb.sprintf (GTXT ("*** Warning: Segment %s [0x%llx-0x%llx] overlaps %s [0x%llx-0x%llx], which has been implicitly unloaded"), + smem->obj->get_name (), smem->base, + smem->base + smem->size, sm_hi->obj->get_name (), + sm_hi->base, sm_hi->base + sm_hi->size); + warnq->append (new Emsg (CMSG_WARN, sb)); + sm_hi = (SegMem*) maps->locate_up (sm_hi->base + sm_hi->size, + smem->load_time); + } + + maps->insert (smem->base, smem->load_time, smem); + break; + case MapRecord::UNLOAD: + smem = (SegMem*) maps->locate (mrec->base, mrec->ts); + if (smem && smem->base == mrec->base) + { + smem->unload_time = mrec->ts; + maps->remove (mrec->base, mrec->ts); + } + break; + } + } + mrecs->destroy (); + + // See if there are comments or warnings for a load object; + // if so, queue them to Experiment + for (long i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + for (Emsg *m = lo->fetch_warnings (); m; m = m->next) + warnq->append (m->get_warn (), m->get_msg ()); + for (Emsg *m = lo->fetch_comments (); m; m = m->next) + commentq->append (m->get_warn (), m->get_msg ()); + } +} + +void +Experiment::read_frameinfo_file () +{ + init_cache (); + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading CallStack Data: %s"), base_name); + read_data_file ("data." SP_FRINFO_FILE, msg); + free (msg); + frmpckts->sort (frUidCmp); + uidnodes->sort (uidNodeCmp); +} + +void +Experiment::read_omp_preg () +{ + // Parallel region descriptions + DataDescriptor *pregDdscr = getDataDescriptor (DATA_OMP4); + if (pregDdscr == NULL) + return; + DataView *pregData = pregDdscr->createView (); + pregData->sort (PROP_CPRID); // omptrace PROP_CPRID + + // OpenMP enter parreg events + DataDescriptor *dDscr = getDataDescriptor (DATA_OMP2); + if (dDscr == NULL || dDscr->getSize () == 0) + { + delete pregData; + return; + } + + char *idxname = NTXT ("OMP_preg"); + delete dbeSession->indxobj_define (idxname, GTXT ("OpenMP Parallel Region"), + NTXT ("CPRID"), NULL, NULL); + int idxtype = dbeSession->findIndexSpaceByName (idxname); + if (idxtype < 0) + { + delete pregData; + return; + } + ompavail = true; + + // Pre-create parallel region with id == 0 + Histable *preg0 = dbeSession->createIndexObject (idxtype, (int64_t) 0); + preg0->set_name (dbe_strdup (GTXT ("Implicit OpenMP Parallel Region"))); + + // Take care of the progress bar + char *msg = dbe_sprintf (GTXT ("Processing OpenMP Parallel Region Data: %s"), + get_basename (expt_name)); + theApplication->set_progress (0, msg); + free (msg); + long deltaReport = 1000; + long nextReport = 0; + long errors_found = 0; + Vector<Histable*> pregs; + + long size = dDscr->getSize (); + for (long i = 0; i < size; ++i) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / size); + if (percent > 0) + theApplication->set_progress (percent, NULL); + nextReport += deltaReport; + } + + uint32_t thrid = dDscr->getIntValue (PROP_THRID, i); + hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i); + uint64_t cprid = dDscr->getLongValue (PROP_CPRID, i); // omptrace CPRID + mapPRid->put (thrid, tstamp, cprid); + + pregs.reset (); + /* + * We will use 2 pointers to make sure there is no loop. + * First pointer "curpreg" goes to the next element, + * second pointer "curpreg_loop_control" goes to the next->next element. + * If these pointers have the same value - there is a loop. + */ + uint64_t curpreg_loop_control = cprid; + Datum tval_loop_control; + if (curpreg_loop_control != 0) + { + tval_loop_control.setUINT64 (curpreg_loop_control); + long idx = pregData->getIdxByVals (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curpreg_loop_control = 0; + else + curpreg_loop_control = pregData->getLongValue (PROP_PPRID, idx); + } + for (uint64_t curpreg = cprid; curpreg != 0;) + { + Histable *val = NULL; + Datum tval; + tval.setUINT64 (curpreg); + long idx = pregData->getIdxByVals (&tval, DataView::REL_EQ); + if (idx < 0) + break; + /* + * Check if there is a loop + */ + if (0 != curpreg_loop_control) + { + if (curpreg == curpreg_loop_control) + { + errors_found++; + if (1 == errors_found) + { + Emsg *m = new Emsg (CMSG_WARN, GTXT ("*** Warning: circular links in OMP regions; data may not be correct.")); + warnq->append (m); + } + break; + } + } + uint64_t pragmapc = pregData->getLongValue (PROP_PRPC, idx); + DbeInstr *instr = map_Vaddr_to_PC (pragmapc, tstamp); + if (instr == NULL) + { + break; + } + val = instr; + DbeLine *dbeline = (DbeLine*) instr->convertto (Histable::LINE); + if (dbeline->lineno > 0) + { + if (instr->func->usrfunc) + dbeline = dbeline->sourceFile->find_dbeline + (instr->func->usrfunc, dbeline->lineno); + dbeline->set_flag (DbeLine::OMPPRAGMA); + val = dbeline; + } + val = dbeSession->createIndexObject (idxtype, val); + pregs.append (val); + + curpreg = pregData->getLongValue (PROP_PPRID, idx); + /* + * Update curpreg_loop_control + */ + if (0 != curpreg_loop_control) + { + tval_loop_control.setUINT64 (curpreg_loop_control); + idx = pregData->getIdxByVals + (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curpreg_loop_control = 0; + else + { + curpreg_loop_control = pregData->getLongValue + (PROP_PPRID, idx); + tval_loop_control.setUINT64 (curpreg_loop_control); + idx = pregData->getIdxByVals + (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curpreg_loop_control = 0; + else + curpreg_loop_control = pregData->getLongValue + (PROP_PPRID, idx); + } + } + } + pregs.append (preg0); + void *prstack = cstack->add_stack (&pregs); + mapPReg->put (thrid, tstamp, prstack); + } + theApplication->set_progress (0, NTXT ("")); + delete pregData; +} + +void +Experiment::read_omp_task () +{ + // Task description + DataDescriptor *taskDataDdscr = getDataDescriptor (DATA_OMP5); + if (taskDataDdscr == NULL) + return; + + //7035272: previously, DataView was global; now it's local...is this OK? + DataView *taskData = taskDataDdscr->createView (); + taskData->sort (PROP_TSKID); // omptrace PROP_TSKID + + // OpenMP enter task events + DataDescriptor *dDscr = getDataDescriptor (DATA_OMP3); + if (dDscr == NULL || dDscr->getSize () == 0) + { + delete taskData; + return; + } + + char *idxname = NTXT ("OMP_task"); + // delete a possible error message. Ugly. + delete dbeSession->indxobj_define (idxname, GTXT ("OpenMP Task"), NTXT ("TSKID"), NULL, NULL); + int idxtype = dbeSession->findIndexSpaceByName (idxname); + if (idxtype < 0) + { + delete taskData; + return; + } + ompavail = true; + + // Pre-create task with id == 0 + Histable *task0 = dbeSession->createIndexObject (idxtype, (int64_t) 0); + task0->set_name (dbe_strdup (GTXT ("OpenMP Task from Implicit Parallel Region"))); + + // Take care of the progress bar + char *msg = dbe_sprintf (GTXT ("Processing OpenMP Task Data: %s"), get_basename (expt_name)); + theApplication->set_progress (0, msg); + free (msg); + long deltaReport = 1000; + long nextReport = 0; + + Vector<Histable*> tasks; + long size = dDscr->getSize (); + long errors_found = 0; + for (long i = 0; i < size; ++i) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / size); + if (percent > 0) + theApplication->set_progress (percent, NULL); + nextReport += deltaReport; + } + + uint32_t thrid = dDscr->getIntValue (PROP_THRID, i); + hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i); + uint64_t tskid = dDscr->getLongValue (PROP_TSKID, i); //omptrace TSKID + tasks.reset (); + /* + * We will use 2 pointers to make sure there is no loop. + * First pointer "curtsk" goes to the next element, + * second pointer "curtsk_loop_control" goes to the next->next element. + * If these pointers have the same value - there is a loop. + */ + uint64_t curtsk_loop_control = tskid; + Datum tval_loop_control; + if (curtsk_loop_control != 0) + { + tval_loop_control.setUINT64 (curtsk_loop_control); + long idx = taskData->getIdxByVals (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curtsk_loop_control = 0; + else + curtsk_loop_control = taskData->getLongValue (PROP_PTSKID, idx); + } + for (uint64_t curtsk = tskid; curtsk != 0;) + { + Histable *val = NULL; + + Datum tval; + tval.setUINT64 (curtsk); + long idx = taskData->getIdxByVals (&tval, DataView::REL_EQ); + if (idx < 0) + break; + /* + * Check if there is a loop + */ + if (0 != curtsk_loop_control) + { + if (curtsk == curtsk_loop_control) + { + errors_found++; + if (1 == errors_found) + { + Emsg *m = new Emsg (CMSG_WARN, GTXT ("*** Warning: circular links in OMP tasks; data may not be correct.")); + warnq->append (m); + } + break; + } + } + uint64_t pragmapc = taskData->getLongValue (PROP_PRPC, idx); + DbeInstr *instr = map_Vaddr_to_PC (pragmapc, tstamp); + if (instr == NULL) + break; + val = instr; + DbeLine *dbeline = (DbeLine*) instr->convertto (Histable::LINE); + if (dbeline->lineno > 0) + { + if (instr->func->usrfunc) + dbeline = dbeline->sourceFile->find_dbeline + (instr->func->usrfunc, dbeline->lineno); + dbeline->set_flag (DbeLine::OMPPRAGMA); + val = dbeline; + } + val = dbeSession->createIndexObject (idxtype, val); + tasks.append (val); + + curtsk = taskData->getLongValue (PROP_PTSKID, idx); + /* + * Update curtsk_loop_control + */ + if (0 != curtsk_loop_control) + { + tval_loop_control.setUINT64 (curtsk_loop_control); + idx = taskData->getIdxByVals (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curtsk_loop_control = 0; + else + { + curtsk_loop_control = taskData->getLongValue (PROP_PTSKID, idx); + tval_loop_control.setUINT64 (curtsk_loop_control); + idx = taskData->getIdxByVals (&tval_loop_control, + DataView::REL_EQ); + if (idx < 0) + curtsk_loop_control = 0; + else + curtsk_loop_control = taskData->getLongValue (PROP_PTSKID, + idx); + } + } + } + tasks.append (task0); + void *tskstack = cstack->add_stack (&tasks); + mapTask->put (thrid, tstamp, tskstack); + } + theApplication->set_progress (0, NTXT ("")); + delete taskData; +} + +void +Experiment::read_omp_file () +{ + // DATA_OMP2 table is common between OpenMP 2.5 and 3.0 profiling + DataDescriptor *dDscr = getDataDescriptor (DATA_OMP2); + if (dDscr == NULL) + return; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading OpenMP Data: %s"), base_name); + read_data_file (SP_OMPTRACE_FILE, msg); + free (msg); + + // OpenMP fork events + dDscr = getDataDescriptor (DATA_OMP); + long sz = dDscr->getSize (); + if (sz > 0) + { + // progress bar + msg = dbe_sprintf (GTXT ("Processing OpenMP Parallel Region Data: %s"), + base_name); + theApplication->set_progress (0, msg); + free (msg); + long deltaReport = 5000; + long nextReport = 0; + for (int i = 0; i < sz; ++i) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / sz); + if (percent > 0) + theApplication->set_progress (percent, NULL); + nextReport += deltaReport; + } + uint32_t thrid = dDscr->getIntValue (PROP_THRID, i); + hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i); + uint64_t cprid = dDscr->getLongValue (PROP_CPRID, i); //omptrace + mapPRid->put (thrid, tstamp, cprid); + } + theApplication->set_progress (0, NTXT ("")); + + ompavail = true; + openMPdata = dDscr->createView (); + openMPdata->sort (PROP_CPRID); // omptrace PROP_CPRID + + // thread enters parreg events + dDscr = getDataDescriptor (DATA_OMP2); + sz = dDscr->getSize (); + + // progress bar + msg = dbe_sprintf (GTXT ("Processing OpenMP Parallel Region Data: %s"), + base_name); + theApplication->set_progress (0, msg); + free (msg); + deltaReport = 5000; + nextReport = 0; + + for (int i = 0; i < sz; ++i) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / sz); + if (percent > 0) + theApplication->set_progress (percent, NULL); + nextReport += deltaReport; + } + uint32_t thrid = dDscr->getIntValue (PROP_THRID, i); + hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i); + uint64_t cprid = dDscr->getLongValue (PROP_CPRID, i); //omptrace + mapPRid->put (thrid, tstamp, cprid); + } + theApplication->set_progress (0, NTXT ("")); + } + else + { + read_omp_preg (); + read_omp_task (); + } + if (ompavail && coll_params.profile_mode) + { + dbeSession->status_ompavail = 1; + register_metric (Metric::OMP_WORK); + register_metric (Metric::OMP_WAIT); + register_metric (Metric::OMP_OVHD); + if (coll_params.lms_magic_id == LMS_MAGIC_ID_SOLARIS) + register_metric (Metric::OMP_MASTER_THREAD); + } + } +} + +void +Experiment::read_ifreq_file () +{ + char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_IFREQ_FILE); + FILE *f = fopen (fname, NTXT ("r")); + free (fname); + if (f == NULL) + { + ifreqavail = false; + return; + } + ifreqavail = true; + ifreqq = new Emsgqueue (NTXT ("ifreqq")); + + while (1) + { + Emsg *m; + char str[MAXPATHLEN]; + char *e = fgets (str, ((int) sizeof (str)) - 1, f); + if (e == NULL) + { + // end the list from the experiment + m = new Emsg (CMSG_COMMENT, + GTXT ("============================================================")); + ifreqq->append (m); + break; + } + // get the string + size_t i = strlen (str); + if (i > 0 && str[i - 1] == '\n') + // remove trailing nl + str[i - 1] = 0; + // and append it + m = new Emsg (CMSG_COMMENT, str); + ifreqq->append (m); + } + (void) fclose (f); +} + +Experiment * +Experiment::getBaseFounder () +{ + if (baseFounder) + return baseFounder; + Experiment *founder = this; + Experiment *parent = founder->founder_exp; + while (parent) + { + founder = parent; + parent = founder->founder_exp; + } + baseFounder = founder; + return baseFounder; +} + +hrtime_t +Experiment::getRelativeStartTime () +{ + if (exp_rel_start_time_set) + return exp_rel_start_time; + Experiment *founder = getBaseFounder (); + hrtime_t child_start = this->getStartTime (); + hrtime_t founder_start = founder->getStartTime (); + exp_rel_start_time = child_start - founder_start; + if (child_start == 0 && founder_start) + exp_rel_start_time = 0; // when descendents have incomplete log.xml + exp_rel_start_time_set = true; + return exp_rel_start_time; +} + +DataDescriptor * +Experiment::get_raw_events (int data_id) +{ + DataDescriptor *dDscr; + switch (data_id) + { + case DATA_CLOCK: + dDscr = get_profile_events (); + break; + case DATA_SYNCH: + dDscr = get_sync_events (); + break; + case DATA_HWC: + dDscr = get_hwc_events (); + break; + case DATA_HEAP: + dDscr = get_heap_events (); + break; + case DATA_HEAPSZ: + dDscr = get_heapsz_events (); + break; + case DATA_IOTRACE: + dDscr = get_iotrace_events (); + break; + case DATA_RACE: + dDscr = get_race_events (); + break; + case DATA_DLCK: + dDscr = get_deadlock_events (); + break; + case DATA_SAMPLE: + dDscr = get_sample_events (); + break; + case DATA_GCEVENT: + dDscr = get_gc_events (); + break; + default: + dDscr = NULL; + break; + } + return dDscr; +} + +int +Experiment::base_data_id (int data_id) +{ + switch (data_id) + { + case DATA_HEAPSZ: + return DATA_HEAP; // DATA_HEAPSZ DataView is based on DATA_HEAP's DataView + default: + break; + } + return data_id; +} + +DataView * +Experiment::create_derived_data_view (int data_id, DataView *dview) +{ + // dview contains filtered packets + switch (data_id) + { + case DATA_HEAPSZ: + return create_heapsz_data_view (dview); + default: + break; + } + return NULL; +} + +DataDescriptor * +Experiment::get_profile_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_CLOCK); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Profile Data: %s"), base_name); + read_data_file (SP_PROFILE_FILE, msg); + free (msg); + add_evt_time_to_profile_events (dDscr); + resolve_frame_info (dDscr); + } + else if (!dDscr->isResolveFrInfoDone ()) + resolve_frame_info (dDscr); + return dDscr; +} + +void +Experiment::add_evt_time_to_profile_events (DataDescriptor *dDscr) +{ + if (coll_params.lms_magic_id != LMS_MAGIC_ID_SOLARIS) + return; + + DataView *dview = dDscr->createView (); + dview->sort (PROP_THRID, PROP_TSTAMP); + + // add PROP_EVT_TIME + PropDescr* tmp_propDscr = new PropDescr (PROP_EVT_TIME, "EVT_TIME"); + tmp_propDscr->uname = dbe_strdup (GTXT ("Event duration")); + tmp_propDscr->vtype = TYPE_INT64; + dDscr->addProperty (tmp_propDscr); + + long sz = dview->getSize (); + long long ptimer_usec = get_params ()->ptimer_usec; + for (long i = 0; i < sz; i++) + { + int next_sample; + int jj; + { + hrtime_t this_tstamp = dview->getLongValue (PROP_TSTAMP, i); + long this_thrid = dview->getLongValue (PROP_THRID, i); + for (jj = i + 1; jj < sz; jj++) + { + hrtime_t tmp_tstamp = dview->getLongValue (PROP_TSTAMP, jj); + if (tmp_tstamp != this_tstamp) + break; + long tmp_thrid = dview->getLongValue (PROP_THRID, jj); + if (tmp_thrid != this_thrid) + break; + } + next_sample = jj; + } + + long nticks = 0; + for (jj = i; jj < next_sample; jj++) + nticks += dview->getLongValue (PROP_NTICK, jj); + if (nticks <= 1) + continue; // no duration + + nticks--; + hrtime_t duration = ptimer_usec * 1000LL * nticks; // nanoseconds + for (jj = i; jj < next_sample; jj++) + dview->setValue (PROP_EVT_TIME, jj, duration); + i = jj - 1; + } + delete dview; +} + +DataDescriptor * +Experiment::get_sync_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_SYNCH); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () > 0) + return dDscr; + + // fetch data + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Synctrace Data: %s"), base_name); + read_data_file (SP_SYNCTRACE_FILE, msg); + free (msg); + resolve_frame_info (dDscr); + } + + // check for PROP_EVT_TIME + PropDescr *tmp_propDscr = dDscr->getProp (PROP_EVT_TIME); + if (tmp_propDscr) + return dDscr; + + // add PROP_EVT_TIME + tmp_propDscr = new PropDescr (PROP_EVT_TIME, "EVT_TIME"); + tmp_propDscr->uname = dbe_strdup (GTXT ("Event duration")); + tmp_propDscr->vtype = TYPE_INT64; + dDscr->addProperty (tmp_propDscr); + + long sz = dDscr->getSize (); + for (long i = 0; i < sz; i++) + { + uint64_t event_duration = dDscr->getLongValue (PROP_TSTAMP, i); + event_duration -= dDscr->getLongValue (PROP_SRQST, i); + dDscr->setValue (PROP_EVT_TIME, i, event_duration); + } + return dDscr; +} + +DataDescriptor * +Experiment::get_hwc_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_HWC); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading HW Profile Data: %s"), base_name); + + // clear HWC event stats + dsevents = 0; + dsnoxhwcevents = 0; + read_data_file (SP_HWCNTR_FILE, msg); + free (msg); + resolve_frame_info (dDscr); + + // describe the HW counters in PropDescr + PropDescr *prop = dDscr->getProp (PROP_HWCTAG); + if (prop) + { + Collection_params *cparam = get_params (); + if (cparam->hw_mode != 0) + for (int aux = 0; aux < MAX_HWCOUNT; aux++) + if (cparam->hw_aux_name[aux]) + { + const char* cmdname = cparam->hw_aux_name[aux]; + const char* uname = cparam->hw_username[aux]; + prop->addState (aux, cmdname, uname); + } + } + else + assert (0); + + double dserrrate = 100.0 * ((double) dsnoxhwcevents) / ((double) dsevents); + if ((dsevents > 0) && (dserrrate > 10.0)) + { + // warn the user that rate is high + StringBuilder sb; + if (dbeSession->check_ignore_no_xhwcprof ()) + sb.sprintf ( + GTXT ("Warning: experiment %s has %.1f%%%% (%lld of %lld) dataspace events that were accepted\n without verification; data may be incorrect or misleading\n recompile with -xhwcprof and rerecord to get better data\n"), + base_name, dserrrate, (long long) dsnoxhwcevents, + (long long) dsevents); + else + sb.sprintf ( + GTXT ("Warning: experiment %s has %.1f%%%% (%lld of %lld) dataspace events that could not be verified\n recompile with -xhwcprof and rerecord to get better data\n"), + base_name, dserrrate, (long long) dsnoxhwcevents, + (long long) dsevents); + errorq->append (new Emsg (CMSG_WARN, sb)); + } + + // see if we've scanned the data + if (hwc_scanned == 0) + { + // no, scan the packets to see how many are bogus, or represent lost interrupts + long hwc_cnt = 0; + + // loop over the packets, counting the bad ones + if (hwc_bogus != 0 || hwc_lost_int != 0) + { + // hwc counter data had bogus packets and/or packets reflecting lost interrupts + double bogus_rate = 100. * (double) hwc_bogus / (double) hwc_cnt; + if (bogus_rate > 5.) + { + StringBuilder sb; + sb.sprintf ( + GTXT ("WARNING: Too many invalid HW counter profile events (%ld/%ld = %3.2f%%) in experiment %d (`%s'); data may be unreliable"), + (long) hwc_bogus, (long) hwc_cnt, bogus_rate, + (int) userExpId, base_name); + Emsg *m = new Emsg (CMSG_WARN, sb); + warnq->append (m); + } + hwc_scanned = 1; + } + } + } + return dDscr; +} + +DataDescriptor * +Experiment::get_iotrace_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_IOTRACE); + if (dDscr == NULL) + return NULL; + + if (dDscr->getSize () > 0) + return dDscr; + + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading IO Trace Data: %s"), base_name); + read_data_file (SP_IOTRACE_FILE, msg); + free (msg); + + if (dDscr->getSize () == 0) + return dDscr; + resolve_frame_info (dDscr); + + // check for PROP_EVT_TIME + PropDescr *tmp_propDscr = dDscr->getProp (PROP_EVT_TIME); + if (tmp_propDscr) + return dDscr; + + // add PROP_EVT_TIME + tmp_propDscr = new PropDescr (PROP_EVT_TIME, "EVT_TIME"); + tmp_propDscr->uname = dbe_strdup (GTXT ("Event duration")); + tmp_propDscr->vtype = TYPE_INT64; + dDscr->addProperty (tmp_propDscr); + + // add PROP_IOVFD + tmp_propDscr = new PropDescr (PROP_IOVFD, "IOVFD"); + tmp_propDscr->uname = dbe_strdup (GTXT ("Virtual File Descriptor")); + tmp_propDscr->vtype = TYPE_INT64; + dDscr->addProperty (tmp_propDscr); + + delete fDataMap; + fDataMap = new DefaultMap<int64_t, FileData*>; + + delete vFdMap; + vFdMap = new DefaultMap<int, int64_t>; + + static int64_t virtualFd = 0; + + FileData *fData; + virtualFd += 10; + fData = fDataMap->get (VIRTUAL_FD_STDIN); + if (fData == NULL) + { + fData = new FileData (STDIN_FILENAME); + fData->setVirtualFd (VIRTUAL_FD_STDIN); + fData->id = VIRTUAL_FD_STDIN; + fData->setFileDes (STDIN_FD); + fDataMap->put (VIRTUAL_FD_STDIN, fData); + vFdMap->put (STDIN_FD, VIRTUAL_FD_STDIN); + } + + fData = fDataMap->get (VIRTUAL_FD_STDOUT); + if (fData == NULL) + { + fData = new FileData (STDOUT_FILENAME); + fData->setVirtualFd (VIRTUAL_FD_STDOUT); + fData->id = VIRTUAL_FD_STDOUT; + fData->setFileDes (STDOUT_FD); + fDataMap->put (VIRTUAL_FD_STDOUT, fData); + vFdMap->put (STDOUT_FD, VIRTUAL_FD_STDOUT); + } + + fData = fDataMap->get (VIRTUAL_FD_STDERR); + if (fData == NULL) + { + fData = new FileData (STDERR_FILENAME); + fData->setVirtualFd (VIRTUAL_FD_STDERR); + fData->id = VIRTUAL_FD_STDERR; + fData->setFileDes (STDERR_FD); + fDataMap->put (VIRTUAL_FD_STDERR, fData); + vFdMap->put (STDERR_FD, VIRTUAL_FD_STDERR); + } + + fData = fDataMap->get (VIRTUAL_FD_OTHERIO); + if (fData == NULL) + { + fData = new FileData (OTHERIO_FILENAME); + fData->setVirtualFd (VIRTUAL_FD_OTHERIO); + fData->id = VIRTUAL_FD_OTHERIO; + fData->setFileDes (OTHERIO_FD); + fDataMap->put (VIRTUAL_FD_OTHERIO, fData); + } + + DataView *dview = dDscr->createView (); + dview->sort (PROP_TSTAMP); + long sz = dview->getSize (); + for (long i = 0; i < sz; i++) + { + hrtime_t event_duration = dview->getLongValue (PROP_TSTAMP, i); + hrtime_t event_start = dview->getLongValue (PROP_IORQST, i); + if (event_start > 0) + event_duration -= event_start; + else + event_duration = 0; + dview->setValue (PROP_EVT_TIME, i, event_duration); + + int32_t fd = -1; + int64_t vFd = VIRTUAL_FD_NONE; + char *fName = NULL; + int32_t origFd = -1; + StringBuilder *sb = NULL; + FileData *fDataOrig = NULL; + FileSystem_type fsType; + + IOTrace_type ioType = (IOTrace_type) dview->getIntValue (PROP_IOTYPE, i); + switch (ioType) + { + case READ_TRACE: + case WRITE_TRACE: + case READ_TRACE_ERROR: + case WRITE_TRACE_ERROR: + fd = dview->getIntValue (PROP_IOFD, i); + vFd = vFdMap->get (fd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE + || (fData = fDataMap->get (vFd)) == NULL) + { + fData = new FileData (UNKNOWNFD_FILENAME); + fData->setVirtualFd (virtualFd); + fData->setFsType ("N/A"); + fData->setFileDes (fd); + fDataMap->put (virtualFd, fData); + vFdMap->put (fd, virtualFd); + vFd = virtualFd; + virtualFd++; + } + dview->setValue (PROP_IOVFD, i, vFd); + break; + case OPEN_TRACE: + fName = NULL; + sb = (StringBuilder*) dview->getObjValue (PROP_IOFNAME, i); + if (sb != NULL && sb->length () > 0) + fName = sb->toString (); + fd = dview->getIntValue (PROP_IOFD, i); + origFd = dview->getIntValue (PROP_IOOFD, i); + fsType = (FileSystem_type) dview->getIntValue (PROP_IOFSTYPE, i); + + if (fName != NULL) + { + fData = new FileData (fName); + fDataMap->put (virtualFd, fData); + vFdMap->put (fd, virtualFd); + fData->setFileDes (fd); + fData->setFsType (fsType); + fData->setVirtualFd (virtualFd); + vFd = virtualFd; + virtualFd++; + } + else if (origFd > 0) + { + vFd = vFdMap->get (origFd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE) + { + Dprintf (DEBUG_IO, + "*** Error I/O tracing: (open) cannot get the virtual file descriptor, fd=%d origFd=%d\n", + fd, origFd); + continue; + } + else if ((fDataOrig = fDataMap->get (vFd)) == NULL) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (open) cannot get original FileData object, fd=%d origFd=%d\n", + fd, origFd); + continue; + } + else + { + fName = fDataOrig->getFileName (); + fData = new FileData (fName); + fData->setFileDes (fd); + fData->setFsType (fDataOrig->getFsType ()); + fData->setVirtualFd (virtualFd); + fDataMap->put (virtualFd, fData); + vFdMap->put (fd, virtualFd); + vFd = virtualFd; + virtualFd++; + } + } + else if (fd >= 0) + { + vFd = vFdMap->get (fd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE + || (fData = fDataMap->get (vFd)) == NULL) + { + fData = new FileData (UNKNOWNFD_FILENAME); + fData->setVirtualFd (virtualFd); + fData->setFsType ("N/A"); + fData->setFileDes (fd); + fDataMap->put (virtualFd, fData); + vFdMap->put (fd, virtualFd); + vFd = virtualFd; + virtualFd++; + } + } + else + { + Dprintf (DEBUG_IO, + NTXT ("*** Error IO tracing: (open) unknown open IO type, fd=%d origFd=%d\n"), fd, origFd); + continue; + } + + dview->setValue (PROP_IOVFD, i, vFd); + break; + + case OPEN_TRACE_ERROR: + fName = NULL; + + sb = (StringBuilder*) dview->getObjValue (PROP_IOFNAME, i); + if (sb != NULL && sb->length () > 0) + fName = sb->toString (); + fd = dview->getIntValue (PROP_IOFD, i); + origFd = dview->getIntValue (PROP_IOOFD, i); + fsType = (FileSystem_type) dview->getIntValue (PROP_IOFSTYPE, i); + + if (fName != NULL) + { + fData = new FileData (fName); + fDataMap->put (virtualFd, fData); + fData->setFileDes (fd); + fData->setFsType (fsType); + fData->setVirtualFd (virtualFd); + vFd = virtualFd; + virtualFd++; + } + else if (origFd > 0) + { + vFd = vFdMap->get (origFd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (open error) cannot get the virtual file descriptor, fd=%d origFd=%d\n", + fd, origFd); + continue; + } + else if ((fDataOrig = fDataMap->get (vFd)) == NULL) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (open error) cannot get original FileData object, fd=%d origFd=%d\n", + fd, origFd); + continue; + } + else + { + fName = fDataOrig->getFileName (); + fData = new FileData (fName); + fData->setFileDes (fd); + fData->setFsType (fDataOrig->getFsType ()); + fData->setVirtualFd (virtualFd); + fDataMap->put (virtualFd, fData); + vFd = virtualFd; + virtualFd++; + } + } + + dview->setValue (PROP_IOVFD, i, vFd); + break; + + case CLOSE_TRACE: + case CLOSE_TRACE_ERROR: + fd = dview->getIntValue (PROP_IOFD, i); + vFd = vFdMap->get (fd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (close) cannot get the virtual file descriptor, fd=%d\n", + fd); + continue; + } + fData = fDataMap->get (vFd); + if (fData == NULL) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (close) cannot get the FileData object, fd=%d\n", + fd); + continue; + } + + vFdMap->put (fd, VIRTUAL_FD_NONE); + dview->setValue (PROP_IOVFD, i, vFd); + break; + + case OTHERIO_TRACE: + case OTHERIO_TRACE_ERROR: + vFd = VIRTUAL_FD_OTHERIO; + fData = fDataMap->get (vFd); + if (fData == NULL) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (other IO) cannot get the FileData object\n"); + continue; + } + + dview->setValue (PROP_IOVFD, i, vFd); + break; + case IOTRACETYPE_LAST: + break; + } + } + + delete dview; + + return dDscr; +} + +DataDescriptor * +Experiment::get_heap_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_HEAP); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () > 0) + return dDscr; + + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Heap Trace Data: %s"), base_name); + read_data_file (SP_HEAPTRACE_FILE, msg); + free (msg); + + if (dDscr->getSize () == 0) + return dDscr; + resolve_frame_info (dDscr); + + // Match FREE to MALLOC + PropDescr *prop = new PropDescr (PROP_HLEAKED, NTXT ("HLEAKED")); + prop->uname = dbe_strdup (GTXT ("Bytes Leaked")); + prop->vtype = TYPE_UINT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HMEM_USAGE, NTXT ("HMEM_USAGE")); + prop->uname = dbe_strdup (GTXT ("Heap Memory Usage")); + prop->vtype = TYPE_UINT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HFREED, NTXT ("HFREED")); + prop->uname = dbe_strdup (GTXT ("Bytes Freed")); + prop->vtype = TYPE_UINT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HCUR_ALLOCS, NTXT ("HCUR_ALLOCS")); + prop->uname = dbe_strdup (GTXT ("Net Bytes Allocated")); + prop->vtype = TYPE_INT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HCUR_LEAKS, NTXT ("HCUR_LEAKS")); + prop->uname = dbe_strdup (GTXT ("Net Bytes Leaked")); + prop->vtype = TYPE_UINT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HCUR_NET_ALLOC, NTXT ("HCUR_NET_ALLOC")); + prop->vtype = TYPE_INT64; + prop->flags = DDFLAG_NOSHOW; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_DDSCR_LNK, NTXT ("DDSCR_LNK")); + prop->vtype = TYPE_UINT64; + prop->flags = DDFLAG_NOSHOW; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_VOIDP_OBJ, NTXT ("VOIDP_OBJ")); + prop->vtype = TYPE_OBJ; + prop->flags = DDFLAG_NOSHOW; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_TSTAMP2, NTXT ("TSTAMP2")); + prop->uname = dbe_strdup (GTXT ("End Timestamp (nanoseconds)")); + prop->vtype = TYPE_UINT64; + prop->flags = DDFLAG_NOSHOW; + dDscr->addProperty (prop); + + DataView *dview = dDscr->createView (); + dview->sort (PROP_TSTAMP); + + // Keep track of memory usage + Size memoryUsage = 0; + + HeapMap *heapmap = new HeapMap (); + long sz = dview->getSize (); + for (long i = 0; i < sz; i++) + { + + Heap_type mtype = (Heap_type) dview->getIntValue (PROP_HTYPE, i); + Vaddr vaddr = dview->getULongValue (PROP_HVADDR, i); + Vaddr ovaddr = dview->getULongValue (PROP_HOVADDR, i); + Size hsize = dview->getULongValue (PROP_HSIZE, i); + hrtime_t tstamp = dview->getLongValue (PROP_TSTAMP, i); + + switch (mtype) + { + case MALLOC_TRACE: + dview->setValue (PROP_TSTAMP2, i, (uint64_t) MAX_TIME); + if (vaddr) + { + dview->setValue (PROP_HLEAKED, i, hsize); + heapmap->allocate (vaddr, i + 1); + + // Increase heap size + memoryUsage += hsize; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + } + break; + + case FREE_TRACE: + if (vaddr) + { + long idx = heapmap->deallocate (vaddr) - 1; + if (idx >= 0) + { + // Decrease heap size + Size leaked = dview->getLongValue (PROP_HLEAKED, idx); + memoryUsage -= leaked; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + + Size alloc = dview->getLongValue (PROP_HSIZE, idx); + // update allocation + dview->setValue (PROP_HLEAKED, idx, (uint64_t) 0); + dview->setValue (PROP_TSTAMP2, idx, tstamp); + dview->setValue (PROP_DDSCR_LNK, idx, dview->getIdByIdx (i) + 1); + // update this event + dview->setValue (PROP_HFREED, i, alloc); + } + } + break; + + case REALLOC_TRACE: + dview->setValue (PROP_TSTAMP2, i, (uint64_t) MAX_TIME); + if (ovaddr) + { + long idx = heapmap->deallocate (ovaddr) - 1; + if (idx >= 0) + { + // Decrease heap size + Size leaked = dview->getLongValue (PROP_HLEAKED, idx); + memoryUsage -= leaked; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + + Size alloc = dview->getLongValue (PROP_HSIZE, idx); + // update allocation + dview->setValue (PROP_HLEAKED, idx, (uint64_t) 0); + dview->setValue (PROP_TSTAMP2, idx, tstamp); + dview->setValue (PROP_DDSCR_LNK, idx, dview->getIdByIdx (i) + 1); + // update this event + dview->setValue (PROP_HFREED, i, alloc); + } + } + if (vaddr) + { + dview->setValue (PROP_HLEAKED, i, hsize); + heapmap->allocate (vaddr, i + 1); + + // Increase heap size + memoryUsage += hsize; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + } + break; + case MMAP_TRACE: + case MUNMAP_TRACE: + // Adjust the size to be multiple of page_size + //hsize = (( hsize - 1 ) / page_size + 1 ) * page_size; + if (vaddr) + { + UnmapChunk *list; + if (mtype == MMAP_TRACE) + { + dview->setValue (PROP_TSTAMP2, i, (uint64_t) MAX_TIME); + dview->setValue (PROP_HLEAKED, i, hsize); + list = heapmap->mmap (vaddr, hsize, i); + + // Increase heap size + memoryUsage += hsize; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + } + else + { // MUNMAP_TRACE + list = heapmap->munmap (vaddr, hsize); + + // Set allocation size to zero + // Note: We're currently reusing PROP_HSIZE to mean allocation size + // If we ever need to save the original HSIZE, we'll need to + // create a new PROP_* to represent event allocation size + // + // For now, tuck the original size away as HOVADDR + dview->setValue (PROP_HOVADDR, i, (uint64_t) hsize); + dview->setValue (PROP_HSIZE, i, (uint64_t) 0); + } + Size total_freed = 0; + while (list) + { + long idx = list->val; + total_freed += list->size; + Size leaked = dview->getLongValue (PROP_HLEAKED, idx); + + // Decrease heap size + memoryUsage -= list->size; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + + Size leak_update = leaked - list->size; + // update allocation + dview->setValue (PROP_HLEAKED, idx, leak_update); + // update allocation's list of frees + { + UnmapChunk *copy = new UnmapChunk; + heapUnmapEvents->append (copy); + copy->val = dview->getIdByIdx (i); + copy->size = list->size; + copy->next = (UnmapChunk *) dview->getObjValue (PROP_VOIDP_OBJ, idx); + dview->setObjValue (PROP_VOIDP_OBJ, idx, copy); + } + if (leak_update <= 0) + if (leak_update == 0) + dview->setValue (PROP_TSTAMP2, idx, tstamp); + UnmapChunk *t = list; + list = list->next; + delete t; + } + // update this event + if (total_freed) + // only need to write value if it is non-zero + dview->setValue (PROP_HFREED, i, total_freed); + } + break; + // ignoring HEAPTYPE_LAST, which will never be recorded + case HEAPTYPE_LAST: + break; + } + } + delete heapmap; + delete dview; + + return dDscr; +} + +DataDescriptor * +Experiment::get_heapsz_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_HEAPSZ); + if (dDscr) + return dDscr; + dDscr = get_heap_events (); // derived from DATA_HEAP + if (dDscr == NULL) + return NULL; + dDscr = newDataDescriptor (DATA_HEAPSZ, 0, dDscr); + return dDscr; +} + +static void +update_heapsz_packet (std::set<long> &pkt_id_set, DataView *dview, + long alloc_pkt_id, int64_t net_alloc, uint64_t leaks) +{ + // pkt_id_set: set is updated to include packet + // alloc_pkt_id: data descriptor id (NOT dview idx) + // net_alloc: adjustment to net allocation for this packet (note: signed value) + // leaks: leak bytes to attribute to alloc_pkt_id + std::pair < std::set<long>::iterator, bool> ret; + ret = pkt_id_set.insert (alloc_pkt_id); // add to set + bool new_to_set = ret.second; // was not in set + if (!new_to_set) + { + // Has been seen before, update values + net_alloc += dview->getDataDescriptorValue (PROP_HCUR_NET_ALLOC, alloc_pkt_id); + if (leaks) + { + uint64_t old = dview->getDataDescriptorValue (PROP_HCUR_LEAKS, alloc_pkt_id); + if (old != 0) + leaks = old; + } + } + dview->setDataDescriptorValue (PROP_HCUR_NET_ALLOC, alloc_pkt_id, net_alloc); + dview->setDataDescriptorValue (PROP_HCUR_LEAKS, alloc_pkt_id, leaks); +} + +DataView * +Experiment::create_heapsz_data_view (DataView *heap_dview) +{ + // heap_dview has DATA_HEAP _filtered_ packets. + // This creates, populates, and returns DATA_HEAPSZ DataView + DataDescriptor *dDscr = get_heapsz_events (); + if (dDscr == NULL) + return NULL; + std::set<long> pkt_id_set; + DataView *dview = heap_dview; + long sz = dview->getSize (); + for (long i = 0; i < sz; i++) + { + int64_t hsize = (int64_t) dview->getULongValue (PROP_HSIZE, i); + uint64_t leaks = dview->getULongValue (PROP_HLEAKED, i); + long alloc_pkt_id = dview->getIdByIdx (i); + update_heapsz_packet (pkt_id_set, dview, alloc_pkt_id, hsize, leaks); + + // linked free + UnmapChunk *mmap_frees = (UnmapChunk *) dview->getObjValue (PROP_VOIDP_OBJ, i); // mmap metadata + if (mmap_frees) + { + // mmap: all frees associated with this packet + while (mmap_frees) + { + long free_pkt_id = mmap_frees->val; + int64_t free_sz = mmap_frees->size; + update_heapsz_packet (pkt_id_set, dview, free_pkt_id, -free_sz, 0); + mmap_frees = mmap_frees->next; + } + } + else + { + // malloc: check for associated free + long free_pkt_id = dview->getLongValue (PROP_DDSCR_LNK, i) - 1; + if (free_pkt_id >= 0) + update_heapsz_packet (pkt_id_set, dview, free_pkt_id, -hsize, 0); + } + } + + // create a new DataView based on the filtered-in and associated free events + std::set<long>::iterator it; + DataView *heapsz_dview = dDscr->createExtManagedView (); + for (it = pkt_id_set.begin (); it != pkt_id_set.end (); ++it) + { + long ddscr_pkt_id = *it; + heapsz_dview->appendDataDescriptorId (ddscr_pkt_id); + } + compute_heapsz_data_view (heapsz_dview); + return heapsz_dview; +} + +void +Experiment::compute_heapsz_data_view (DataView *heapsz_dview) +{ + DataView *dview = heapsz_dview; + + // Keep track of memory usage + int64_t currentAllocs = 0; + Size currentLeaks = 0; + dview->sort (PROP_TSTAMP); + long sz = dview->getSize (); + for (long i = 0; i < sz; i++) + { + int64_t net_alloc = dview->getLongValue (PROP_HCUR_NET_ALLOC, i); + currentAllocs += net_alloc; + dview->setValue (PROP_HCUR_ALLOCS, i, currentAllocs); + + Size leaks = dview->getULongValue (PROP_HCUR_LEAKS, i); + currentLeaks += leaks; + dview->setValue (PROP_HCUR_LEAKS, i, currentLeaks); + } +} + +void +Experiment::DBG_memuse (Sample * s) +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_HEAP); + if (dDscr == NULL || dDscr->getSize () == 0) + return; + + DataView *dview = dDscr->createView (); + dview->sort (PROP_TSTAMP); + hrtime_t ts1 = s->get_start_time (); + hrtime_t ts2 = s->get_end_time (); + + HeapMap *heapmap = new HeapMap (); + long sz = dview->getSize (); + Size maxSize = 0; + Size curSize = 0; + hrtime_t maxTime = 0; + for (long i = 0; i < sz; i++) + { + hrtime_t tstamp = dview->getLongValue (PROP_TSTAMP, i); + if (tstamp < ts1) + continue; + if (tstamp >= ts2) + break; + + Heap_type mtype = (Heap_type) dview->getIntValue (PROP_HTYPE, i); + Vaddr vaddr = dview->getULongValue (PROP_HVADDR, i); + Vaddr ovaddr = dview->getULongValue (PROP_HOVADDR, i); + switch (mtype) + { + case REALLOC_TRACE: + break; + case MALLOC_TRACE: + ovaddr = 0; + break; + case FREE_TRACE: + ovaddr = vaddr; + vaddr = 0; + break; + default: + vaddr = 0; + ovaddr = 0; + break; + } + if (ovaddr) + { + long idx = heapmap->deallocate (ovaddr) - 1; + if (idx >= 0) + curSize -= dview->getULongValue (PROP_HSIZE, idx); + } + if (vaddr) + { + heapmap->allocate (vaddr, i + 1); + curSize += dview->getULongValue (PROP_HSIZE, i); + if (curSize > maxSize) + { + maxSize = curSize; + maxTime = tstamp; + } + } + } + printf ("SAMPLE=%s (id=%d) MEMUSE=%lld TSTAMP=%lld\n", s->get_start_label (), + s->get_number (), maxSize, maxTime - getStartTime ()); + delete dview; + delete heapmap; +} + +void +Experiment::DBG_memuse (const char *sname) +{ + for (int i = 0; i < samples->size (); ++i) + { + Sample *sample = samples->fetch (i); + if (streq (sname, sample->get_start_label ())) + { + DBG_memuse (sample); + break; + } + } +} + +DataDescriptor * +Experiment::get_race_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_RACE); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Race Data: %s"), base_name); + read_data_file (SP_RACETRACE_FILE, msg); + free (msg); + resolve_frame_info (dDscr); + } + return dDscr; +} + +DataDescriptor * +Experiment::get_deadlock_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_DLCK); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Deadlocks Data: %s"), base_name); + read_data_file (SP_DEADLOCK_FILE, msg); + free (msg); + resolve_frame_info (dDscr); + } + return dDscr; +} + +DataDescriptor * +Experiment::get_sample_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_SAMPLE); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () > 0) + return dDscr; + + // read_overview_file(); //YXXX do this here at some point instead of: + PropDescr *tmp_propDscr; + tmp_propDscr = new PropDescr (PROP_SMPLOBJ, NTXT ("SMPLOBJ")); + tmp_propDscr->uname = NULL; + tmp_propDscr->vtype = TYPE_OBJ; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_TSTAMP, NTXT ("TSTAMP")); + tmp_propDscr->uname = dbe_strdup ("High resolution timestamp"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_SAMPLE, NTXT ("SAMPLE")); + tmp_propDscr->uname = dbe_strdup ("Sample number"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_EVT_TIME, NTXT ("EVT_TIME")); + tmp_propDscr->uname = dbe_strdup ("Event duration"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + long ssize = samples->size (); + for (long ii = 0; ii < ssize; ii++) + { + Sample * sample = samples->fetch (ii); + long recn = dDscr->addRecord (); + hrtime_t sduration = sample->get_end_time () - sample->get_start_time (); + dDscr->setObjValue (PROP_SMPLOBJ, recn, sample); + dDscr->setValue (PROP_SAMPLE, recn, sample->get_number ()); + dDscr->setValue (PROP_TSTAMP, recn, sample->get_end_time ()); + dDscr->setValue (PROP_EVT_TIME, recn, sduration); + } + return dDscr; +} + +DataDescriptor * +Experiment::get_gc_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_GCEVENT); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () > 0) + return dDscr; + + // read_overview_file(); //YXXX do this here at some point instead of: + PropDescr *tmp_propDscr; + tmp_propDscr = new PropDescr (PROP_GCEVENTOBJ, NTXT ("GCEVENTOBJ")); + tmp_propDscr->uname = NULL; + tmp_propDscr->vtype = TYPE_OBJ; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_TSTAMP, NTXT ("TSTAMP")); + tmp_propDscr->uname = dbe_strdup ("High resolution timestamp"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_GCEVENT, NTXT ("GCEVENT")); + tmp_propDscr->uname = dbe_strdup ("GCEvent number"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_EVT_TIME, NTXT ("EVT_TIME")); + tmp_propDscr->uname = dbe_strdup ("Event duration"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + long ssize = gcevents->size (); + for (long ii = 0; ii < ssize; ii++) + { + GCEvent * gcevent = gcevents->fetch (ii); + long recn = dDscr->addRecord (); + hrtime_t sduration = gcevent->end - gcevent->start; + dDscr->setObjValue (PROP_GCEVENTOBJ, recn, gcevent); + dDscr->setValue (PROP_GCEVENT, recn, gcevent->id); + dDscr->setValue (PROP_TSTAMP, recn, gcevent->end); + dDscr->setValue (PROP_EVT_TIME, recn, sduration); + } + return dDscr; +} + +void +Experiment::update_last_event (hrtime_t ts/*wall_ts*/) +{ + if (last_event == ZERO_TIME) + { + // not yet initialized + last_event = ts; + } + if (last_event - exp_start_time < ts - exp_start_time) + // compare deltas to avoid hrtime_t wrap + last_event = ts; +} + +void +Experiment::write_header () +{ + StringBuilder sb; + + // write commentary to the experiment, describing the parameters + if (dbeSession->ipc_mode || dbeSession->rdt_mode) + { + // In GUI: print start time at the beginning + char *start_time = ctime (&start_sec); + if (start_time != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("Experiment started %s"), start_time); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + } + // write message with target arglist + if (uarglist != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("\nTarget command (%s): '%s'"), + (wsize == W32 ? "32-bit" : "64-bit"), uarglist); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + sb.setLength (0); + sb.sprintf (GTXT ("Process pid %d, ppid %d, pgrp %d, sid %d"), + pid, ppid, pgrp, sid); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + + // add comment for user name, if set + if (username != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("User: `%s'"), username); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for current working directory + if (ucwd != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("Current working directory: %s"), ucwd); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for collector version string + if (cversion != NULL) + { + char *wstring; + switch (wsize) + { + case Wnone: + wstring = NTXT ("?"); + break; + case W32: + wstring = GTXT ("32-bit"); + break; + case W64: + wstring = GTXT ("64-bit"); + break; + default: + wstring = NTXT ("??"); + break; + } + sb.setLength (0); + sb.sprintf (GTXT ("Collector version: `%s'; experiment version %d.%d (%s)"), + cversion, exp_maj_version, exp_min_version, wstring); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for driver version string (er_kernel) + if (dversion != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("Kernel driver version: `%s'"), dversion); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + if (jversion != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("JVM version: `%s'"), jversion); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for hostname, parameters + if (hostname == NULL) + hostname = dbe_strdup (GTXT ("unknown")); + if (os_version == NULL) + os_version = dbe_strdup (GTXT ("unknown")); + if (architecture == NULL) + architecture = dbe_strdup (GTXT ("unknown")); + sb.setLength (0); + sb.sprintf (GTXT ("Host `%s', OS `%s', page size %d, architecture `%s'"), + hostname, os_version, page_size, architecture); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + + sb.setLength (0); + if (maxclock != minclock) + { + clock = maxclock; + sb.sprintf ( + GTXT (" %d CPUs, with clocks ranging from %d to %d MHz.; max of %d MHz. assumed"), + ncpus, minclock, maxclock, clock); + } + else + sb.sprintf (GTXT (" %d CPU%s, clock speed %d MHz."), + ncpus, (ncpus == 1 ? NTXT ("") : "s"), clock); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + + // add comment for machine memory size + if (page_size > 0 && npages > 0) + { + long long memsize = ((long long) npages * page_size) / (1024 * 1024); + sb.setLength (0); + sb.sprintf (GTXT (" Memory: %d pages @ %d = %lld MB."), + npages, page_size, memsize); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for machine memory size + if (machinemodel != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Machine model: %s"), machinemodel); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for start time + char *p = ctime (&start_sec); + sb.setLength (0); + if (p != NULL) + sb.sprintf (GTXT ("Experiment started %s"), p); + else + sb.sprintf (GTXT ("\nExperiment start not recorded")); + write_coll_params (); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + commentq->appendqueue (runlogq); + runlogq->mark_clear (); +} + +void +Experiment::write_coll_params () +{ + StringBuilder sb; + + // now write the various collection parameters as comments + sb.setLength (0); + sb.append (GTXT ("Data collection parameters:")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + if (coll_params.profile_mode == 1) + { + sb.setLength (0); + sb.sprintf (GTXT (" Clock-profiling, interval = %d microsecs."), + (int) (coll_params.ptimer_usec)); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.sync_mode == 1) + { + sb.setLength (0); + char *scope_str = NTXT (""); + switch (coll_params.sync_scope) + { + case 0: + scope_str = GTXT ("Native- and Java-APIs"); + break; + case SYNCSCOPE_JAVA: + scope_str = GTXT ("JAVA-APIs"); + break; + case SYNCSCOPE_NATIVE: + scope_str = GTXT ("Native-APIs"); + break; + case SYNCSCOPE_JAVA | SYNCSCOPE_NATIVE: + scope_str = GTXT ("Native- and Java-APIs"); + break; + } + if (coll_params.sync_threshold < 0) + sb.sprintf (GTXT (" Synchronization tracing, threshold = %d microsecs. (calibrated); %s"), + -coll_params.sync_threshold, scope_str); + else + sb.sprintf (GTXT (" Synchronization tracing, threshold = %d microsecs.; %s"), + coll_params.sync_threshold, scope_str); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.heap_mode == 1) + { + sb.setLength (0); + sb.append (GTXT (" Heap tracing")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.io_mode == 1) + { + sb.setLength (0); + sb.append (GTXT (" IO tracing")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.race_mode == 1) + { + sb.setLength (0); + char *race_stack_name; + switch (coll_params.race_stack) + { + case 0: + race_stack_name = GTXT ("dual-stack"); + break; + case 1: + race_stack_name = GTXT ("single-stack"); + break; + case 2: + race_stack_name = GTXT ("leaf"); + break; + default: + abort (); + } + sb.sprintf (GTXT (" Datarace detection, %s"), race_stack_name); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.deadlock_mode == 1) + { + sb.setLength (0); + sb.append (GTXT (" Deadlock detection")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.hw_mode == 1) + { + sb.setLength (0); + if (hwc_default == true) + sb.append (GTXT (" HW counter-profiling (default); counters:")); + else + sb.append (GTXT (" HW counter-profiling; counters:")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + for (int i = 0; i < MAX_HWCOUNT; i++) + { + if (!coll_params.hw_aux_name[i]) + continue; + sb.setLength (0); + sb.sprintf (GTXT (" %s, tag %d, interval %d, memop %d"), + coll_params.hw_aux_name[i], i, + coll_params.hw_interval[i], coll_params.hw_tpc[i]); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + } + if (coll_params.sample_periodic == 1) + { + sb.setLength (0); + sb.sprintf (GTXT (" Periodic sampling, %d secs."), + coll_params.sample_timer); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.limit != 0) + { + sb.setLength (0); + sb.sprintf (GTXT (" Experiment size limit, %d"), + coll_params.limit); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.linetrace != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Follow descendant processes from: %s"), + coll_params.linetrace); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.pause_sig != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Pause signal %s"), coll_params.pause_sig); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.sample_sig != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Sample signal %s"), coll_params.sample_sig); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.start_delay != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Data collection delay start %s seconds"), coll_params.start_delay); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.terminate != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Data collection termination after %s seconds"), coll_params.terminate); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + // add a blank line after data description + commentq->append (new Emsg (CMSG_COMMENT, NTXT (""))); +} + + +/* + * Raw packet processing + */ +static int +check_mstate (char *ptr, PacketDescriptor *pDscr, int arg) +{ + switch (arg) + { + case PROP_UCPU: + case PROP_SCPU: + case PROP_TRAP: + case PROP_TFLT: + case PROP_DFLT: + case PROP_KFLT: + case PROP_ULCK: + case PROP_TSLP: + case PROP_WCPU: + case PROP_TSTP: + break; + default: + return 0; + } + Vector<FieldDescr*> *fields = pDscr->getFields (); + for (int i = 0, sz = fields->size (); i < sz; i++) + { + FieldDescr *fDscr = fields->fetch (i); + if (fDscr->propID == arg) + return *((int*) (ptr + fDscr->offset)); + } + return 0; +} + +#define PACKET_ALIGNMENT 4 + +uint64_t +Experiment::readPacket (Data_window *dwin, Data_window::Span *span) +{ + Common_packet *rcp = (Common_packet *) dwin->bind (span, + sizeof (CommonHead_packet)); + uint16_t v16; + uint64_t size = 0; + if (rcp) + { + if ((((long) rcp) % PACKET_ALIGNMENT) != 0) + { + invalid_packet++; + size = PROFILE_BUFFER_CHUNK - span->offset % PROFILE_BUFFER_CHUNK; + return size; + } + v16 = (uint16_t) rcp->tsize; + size = dwin->decode (v16); + if (size == 0) + { + size = PROFILE_BUFFER_CHUNK - span->offset % PROFILE_BUFFER_CHUNK; + return size; + } + rcp = (Common_packet *) dwin->bind (span, size); + } + if (rcp == NULL) + return 0; + + if ((((long) rcp) % PACKET_ALIGNMENT) != 0) + { + invalid_packet++; + size = PROFILE_BUFFER_CHUNK - span->offset % PROFILE_BUFFER_CHUNK; + return size; + } + v16 = (uint16_t) rcp->type; + uint32_t rcptype = dwin->decode (v16); + if (rcptype == EMPTY_PCKT) + return size; + if (rcptype == FRAME_PCKT) + { + RawFramePacket *fp = new RawFramePacket; + fp->uid = dwin->decode (((Frame_packet*) rcp)->uid); + fp->uidn = NULL; + fp->uidj = NULL; + fp->omp_uid = NULL; + fp->omp_state = 0; + char *ptr = (char*) rcp + dwin->decode (((Frame_packet*) rcp)->hsize); + if ((((long) ptr) % PACKET_ALIGNMENT) != 0) + { + invalid_packet++; + delete fp; + return size; + } + v16 = (uint16_t) ((Frame_packet*) rcp)->tsize; + char *end = (char*) rcp + dwin->decode (v16); + for (; ptr < end;) + { + Common_info *cinfo = (Common_info*) ptr; + uint32_t hsize = dwin->decode (cinfo->hsize); + if (hsize == 0 || ptr + hsize > end) + break; + int kind = dwin->decode (cinfo->kind); + bool compressed = false; + if (kind & COMPRESSED_INFO) + { + compressed = true; + kind &= ~COMPRESSED_INFO; + } + switch (kind) + { + case STACK_INFO: + { + char *stack = ptr + sizeof (Stack_info); + size_t stack_size = hsize - sizeof (Stack_info); + uint64_t uidn = dwin->decode (((Stack_info*) cinfo)->uid); + if (stack_size <= 0) + { + fp->uidn = get_uid_node (uidn); + break; + } + uint64_t link_uid = (uint64_t) 0; + if (compressed) + { + stack_size -= sizeof (uint64_t); + unsigned char *s = (unsigned char*) (stack + stack_size); + int shift = 0; + for (size_t i = 0; i<sizeof (link_uid); i++) + { + link_uid |= (uint64_t) * s++ << shift; + shift += 8; + } + } + if (wsize == W32) + fp->uidn = add_uid (dwin, uidn, + (int) (stack_size / sizeof (uint32_t)), + (uint32_t*) stack, link_uid); + else + fp->uidn = add_uid (dwin, uidn, + (int) (stack_size / sizeof (uint64_t)), + (uint64_t*) stack, link_uid); + break; + } + case JAVA_INFO: + { + char *stack = ptr + sizeof (Java_info); + size_t stack_size = hsize - sizeof (Java_info); + uint64_t uidj = dwin->decode (((Java_info*) cinfo)->uid); + if (stack_size <= 0) + { + fp->uidj = get_uid_node (uidj); + break; + } + + uint64_t link_uid = (uint64_t) 0; + if (compressed) + { + stack_size -= sizeof (uint64_t); + unsigned char *s = (unsigned char*) (stack + stack_size); + int shift = 0; + for (size_t i = 0; i<sizeof (link_uid); i++) + { + link_uid |= (uint64_t) * s++ << shift; + shift += 8; + } + } + if (wsize == W32) + fp->uidj = add_uid (dwin, uidj, + (int) (stack_size / sizeof (uint32_t)), + (uint32_t*) stack, link_uid); + else + { + // bug 6909545: garbage in 64-bit JAVA_INFO + char *nstack = (char*) malloc (stack_size); + char *dst = nstack; + char *srcmax = stack + stack_size - sizeof (uint64_t); + for (char *src = stack; src <= srcmax;) + { + int64_t val = dwin->decode (*(int32_t*) src); + *(uint64_t*) dst = dwin->decode (val); + src += sizeof (uint64_t); + dst += sizeof (uint64_t); + if (src > srcmax) + { + fprintf (stderr, "er_print: Experiment::readPacket: Error in data: src=%llx greater than %llx\n", + (long long) src, (long long) srcmax); + break; + } + *(uint64_t*) dst = *(uint64_t*) src; + src += sizeof (uint64_t); + dst += sizeof (uint64_t); + } + fp->uidj = add_uid (dwin, uidj, + (int) (stack_size / sizeof (uint64_t)), + (uint64_t*) nstack, link_uid); + free (nstack); + } + break; + } + case OMP_INFO: + fp->omp_state = dwin->decode (((OMP_info*) ptr)->omp_state); + break; + case OMP2_INFO: + { + uint64_t omp_uid = dwin->decode (((OMP2_info*) ptr)->uid); + fp->omp_uid = get_uid_node (omp_uid); + fp->omp_state = dwin->decode (((OMP2_info*) ptr)->omp_state); + break; + } + default: + break; + } + ptr += hsize; + } + frmpckts->append (fp); + return size; + } + else if (rcptype == UID_PCKT) + { + Uid_packet *uidp = (Uid_packet*) rcp; + uint64_t uid = dwin->decode (uidp->uid); + char *arr_bytes = (char*) (uidp + 1); + v16 = (uint16_t) rcp->tsize; + size_t arr_length = dwin->decode (v16) - sizeof (Uid_packet); + if (arr_length <= 0) + return size; + uint64_t link_uid = (uint64_t) 0; + if (dwin->decode (uidp->flags) & COMPRESSED_INFO) + { + arr_length -= sizeof (uint64_t); + unsigned char *s = (unsigned char*) (arr_bytes + arr_length); + int shift = 0; + for (size_t i = 0; i<sizeof (link_uid); i++) + { + link_uid |= (uint64_t) * s++ << shift; + shift += 8; + } + } + if (wsize == W32) + add_uid (dwin, uid, (int) (arr_length / sizeof (uint32_t)), + (uint32_t*) arr_bytes, link_uid); + else + add_uid (dwin, uid, (int) (arr_length / sizeof (uint64_t)), + (uint64_t*) arr_bytes, link_uid); + return size; + } + + PacketDescriptor *pcktDescr = getPacketDescriptor (rcptype); + if (pcktDescr == NULL) + return size; + DataDescriptor *dataDescr = pcktDescr->getDataDescriptor (); + if (dataDescr == NULL) + return size; + + /* omazur: TBR START -- old experiment */ + if (rcptype == PROF_PCKT) + { + // For backward compatibility with older SS12 experiments + int numstates = get_params ()->lms_magic_id; // ugly, for old experiments + if (numstates > LMS_NUM_SOLARIS_MSTATES) + numstates = LMS_NUM_SOLARIS_MSTATES; + for (int i = 0; i < numstates; i++) + if (check_mstate ((char*) rcp, pcktDescr, PROP_UCPU + i)) + readPacket (dwin, (char*) rcp, pcktDescr, dataDescr, PROP_UCPU + i, + size); + } + else + readPacket (dwin, (char*) rcp, pcktDescr, dataDescr, 0, size); + return size; +} + +void +Experiment::readPacket (Data_window *dwin, char *ptr, PacketDescriptor *pDscr, + DataDescriptor *dDscr, int arg, uint64_t pktsz) +{ + union Value + { + uint32_t val32; + uint64_t val64; + } *v; + + long recn = dDscr->addRecord (); + Vector<FieldDescr*> *fields = pDscr->getFields (); + int sz = fields->size (); + for (int i = 0; i < sz; i++) + { + FieldDescr *field = fields->fetch (i); + v = (Value*) (ptr + field->offset); + if (field->propID == arg) + { + dDscr->setValue (PROP_NTICK, recn, dwin->decode (v->val32)); + dDscr->setValue (PROP_MSTATE, recn, (uint32_t) (field->propID - PROP_UCPU)); + } + if (field->propID == PROP_THRID || field->propID == PROP_LWPID + || field->propID == PROP_CPUID) + { + uint64_t tmp64 = 0; + switch (field->vtype) + { + case TYPE_INT32: + case TYPE_UINT32: + tmp64 = dwin->decode (v->val32); + break; + case TYPE_INT64: + case TYPE_UINT64: + tmp64 = dwin->decode (v->val64); + break; + case TYPE_STRING: + case TYPE_DOUBLE: + case TYPE_OBJ: + case TYPE_DATE: + case TYPE_BOOL: + case TYPE_ENUM: + case TYPE_LAST: + case TYPE_NONE: + break; + } + uint32_t tag = mapTagValue ((Prop_type) field->propID, tmp64); + dDscr->setValue (field->propID, recn, tag); + } + else + { + switch (field->vtype) + { + case TYPE_INT32: + case TYPE_UINT32: + dDscr->setValue (field->propID, recn, dwin->decode (v->val32)); + break; + case TYPE_INT64: + case TYPE_UINT64: + dDscr->setValue (field->propID, recn, dwin->decode (v->val64)); + break; + case TYPE_STRING: + { + int len = (int) (pktsz - field->offset); + if ((len > 0) && (ptr[field->offset] != 0)) + { + StringBuilder *sb = new StringBuilder (); + sb->append (ptr + field->offset, 0, len); + dDscr->setObjValue (field->propID, recn, sb); + } + break; + } + // ignoring the following cases (why?) + case TYPE_DOUBLE: + case TYPE_OBJ: + case TYPE_DATE: + case TYPE_BOOL: + case TYPE_ENUM: + case TYPE_LAST: + case TYPE_NONE: + break; + } + } + } +} + +#define PROG_BYTE 102400 // update progress bar every PROG_BYTE bytes + +void +Experiment::read_data_file (const char *fname, const char *msg) +{ + Data_window::Span span; + off64_t total_len, remain_len; + char *progress_bar_msg; + int progress_bar_percent = -1; + + char *data_file_name = dbe_sprintf (NTXT ("%s/%s"), expt_name, fname); + Data_window *dwin = new Data_window (data_file_name); + // Here we can call stat(data_file_name) to get file size, + // and call a function to reallocate vectors for clock profiling data + free (data_file_name); + if (dwin->not_opened ()) + { + delete dwin; + return; + } + dwin->need_swap_endian = need_swap_endian; + + span.offset = 0; + span.length = dwin->get_fsize (); + total_len = remain_len = span.length; + progress_bar_msg = dbe_sprintf (NTXT ("%s %s"), NTXT (" "), msg); + invalid_packet = 0; + for (;;) + { + uint64_t pcktsz = readPacket (dwin, &span); + if (pcktsz == 0) + break; + // Update progress bar + if ((span.length <= remain_len) && (remain_len > 0)) + { + int percent = (int) (100 * (total_len - remain_len) / total_len); + if (percent > progress_bar_percent) + { + progress_bar_percent += 10; + theApplication->set_progress (percent, progress_bar_msg); + } + remain_len -= PROG_BYTE; + } + span.length -= pcktsz; + span.offset += pcktsz; + } + delete dwin; + + if (invalid_packet) + { + StringBuilder sb; + sb.sprintf (GTXT ("WARNING: There are %d invalid packet(s) in the %s file"), + invalid_packet, fname); + Emsg *m = new Emsg (CMSG_WARN, sb); + warnq->append (m); + } + + theApplication->set_progress (0, NTXT ("")); + free (progress_bar_msg); +} + +int +Experiment::read_overview_file () +{ + char *data_file_name = dbe_sprintf ("%s/%s", expt_name, SP_OVERVIEW_FILE); + Data_window *dwin = new Data_window (data_file_name); + free (data_file_name); + if (dwin->not_opened ()) + { + delete dwin; + return 0; + } + dwin->need_swap_endian = need_swap_endian; + newDataDescriptor (DATA_SAMPLE); + + Data_window::Span span; + span.offset = 0; + span.length = dwin->get_fsize (); + + PrUsage *data = NULL, *data_prev = NULL; + Sample *sample; + int sample_number = 1; + + int64_t prDataSize; + if (wsize == W32) + prDataSize = PrUsage::bind32Size (); + else + prDataSize = PrUsage::bind64Size (); + + while (span.length > 0) + { + data_prev = data; + data = new PrUsage (); + + void *dw = dwin->bind (&span, prDataSize); + if ((dw == NULL) || (prDataSize > span.length)) + { + Emsg *m = new Emsg (CMSG_ERROR, GTXT ("Warning: overview data file can't be read")); + warnq->append (m); + status = FAILURE; + delete dwin; + return status; + } + + if (wsize == W32) + data->bind32 (dw, need_swap_endian); + else + data->bind64 (dw, need_swap_endian); + span.length -= prDataSize; + span.offset += prDataSize; + + // Skip the first packet + if (data_prev == NULL) + continue; + if (sample_number > samples->size ()) + { // inconsistent log/overview + sample = new Sample (sample_number); + char * label = GTXT ("<unknown>"); + sample->start_label = dbe_strdup (label); + sample->end_label = dbe_strdup (label); + samples->append (sample); + } + else + sample = samples->fetch (sample_number - 1); + sample_number++; + sample->start_time = data_prev->pr_tstamp + 1; + sample->end_time = data->pr_tstamp; + sample->prusage = data_prev; + + data_prev->pr_rtime = data->pr_rtime - data_prev->pr_rtime; + data_prev->pr_utime = data->pr_utime - data_prev->pr_utime; + data_prev->pr_stime = data->pr_stime - data_prev->pr_stime; + data_prev->pr_ttime = data->pr_ttime - data_prev->pr_ttime; + data_prev->pr_tftime = data->pr_tftime - data_prev->pr_tftime; + data_prev->pr_dftime = data->pr_dftime - data_prev->pr_dftime; + data_prev->pr_kftime = data->pr_kftime - data_prev->pr_kftime; + data_prev->pr_ltime = data->pr_ltime - data_prev->pr_ltime; + data_prev->pr_slptime = data->pr_slptime - data_prev->pr_slptime; + data_prev->pr_wtime = data->pr_wtime - data_prev->pr_wtime; + data_prev->pr_stoptime = data->pr_stoptime - data_prev->pr_stoptime; + data_prev->pr_minf = data->pr_minf - data_prev->pr_minf; + data_prev->pr_majf = data->pr_majf - data_prev->pr_majf; + data_prev->pr_nswap = data->pr_nswap - data_prev->pr_nswap; + data_prev->pr_inblk = data->pr_inblk - data_prev->pr_inblk; + data_prev->pr_oublk = data->pr_oublk - data_prev->pr_oublk; + data_prev->pr_msnd = data->pr_msnd - data_prev->pr_msnd; + data_prev->pr_mrcv = data->pr_mrcv - data_prev->pr_mrcv; + data_prev->pr_sigs = data->pr_sigs - data_prev->pr_sigs; + data_prev->pr_vctx = data->pr_vctx - data_prev->pr_vctx; + data_prev->pr_ictx = data->pr_ictx - data_prev->pr_ictx; + data_prev->pr_sysc = data->pr_sysc - data_prev->pr_sysc; + data_prev->pr_ioch = data->pr_ioch - data_prev->pr_ioch; + sample->get_usage (); // force validation + } + + for (long smpNum = samples->size (); smpNum >= sample_number; smpNum--) + { + // overview file was truncated + sample = samples->remove (smpNum - 1); + delete sample; + } + + if (data) + { + // Update last_event so that getEndTime() covers + // all loadobjects, too. + update_last_event (data->pr_tstamp); + delete data; + } + delete dwin; + return SUCCESS; +} + +int +Experiment::uidNodeCmp (const void *a, const void *b) +{ + UIDnode *nd1 = *(UIDnode**) a; + UIDnode *nd2 = *(UIDnode**) b; + if (nd1->uid == nd2->uid) + return 0; + return nd1->uid < nd2->uid ? -1 : 1; +} + +static uint64_t +funcAddr (uint32_t val) +{ + if (val == (uint32_t) SP_LEAF_CHECK_MARKER) + return (uint64_t) SP_LEAF_CHECK_MARKER; + if (val == (uint32_t) SP_TRUNC_STACK_MARKER) + return (uint64_t) SP_TRUNC_STACK_MARKER; + if (val == (uint32_t) SP_FAILED_UNWIND_MARKER) + return (uint64_t) SP_FAILED_UNWIND_MARKER; + return val; +} + +Experiment::UIDnode * +Experiment::add_uid (Data_window *dwin, uint64_t uid, int size, + uint32_t *array, uint64_t link_uid) +{ + if (uid == (uint64_t) 0) + return NULL; + uint64_t val = funcAddr (dwin->decode (array[0])); + UIDnode *node = NULL; + UIDnode *res = get_uid_node (uid, val); + UIDnode *next = res; + for (int i = 0; i < size; i++) + { + val = funcAddr (dwin->decode (array[i])); + if (next == NULL) + { + next = get_uid_node ((uint64_t) 0, val); + if (node != NULL) + node->next = next; + } + node = next; + next = node->next; + if (node->val == 0) + node->val = val; + else if (node->val != val) // Algorithmic error (should never happen) + node->val = (uint64_t) SP_LEAF_CHECK_MARKER; + } + if (next == NULL && link_uid != (uint64_t) 0 && node != NULL) + node->next = get_uid_node (link_uid); + return res; +} + +Experiment::UIDnode * +Experiment::add_uid (Data_window *dwin, uint64_t uid, int size, uint64_t *array, uint64_t link_uid) +{ + if (uid == (uint64_t) 0) + return NULL; + UIDnode *node = NULL; + uint64_t val = dwin->decode (array[0]); + UIDnode *res = get_uid_node (uid, val); + UIDnode *next = res; + for (int i = 0; i < size; i++) + { + val = dwin->decode (array[i]); + if (next == NULL) + { + next = get_uid_node ((uint64_t) 0, val); + if (node != NULL) + node->next = next; + } + node = next; + next = node->next; + if (node->val == (uint64_t) 0) + node->val = val; + else if (node->val != val) // Algorithmic error (should never happen) + node->val = (uint64_t) - 1; + } + if (next == NULL && link_uid != (uint64_t) 0 && node != NULL) + node->next = get_uid_node (link_uid); + return res; +} + +Experiment::UIDnode * +Experiment::new_uid_node (uint64_t uid, uint64_t val) +{ +#define NCHUNKSTEP 1024 + if (nnodes >= nchunks * CHUNKSZ) + { + // Reallocate Node chunk array + UIDnode** old_chunks = chunks; + chunks = new UIDnode*[nchunks + NCHUNKSTEP]; + memcpy (chunks, old_chunks, nchunks * sizeof (UIDnode*)); + nchunks += NCHUNKSTEP; + delete[] old_chunks; + // Clean future pointers + memset (&chunks[nchunks - NCHUNKSTEP], 0, NCHUNKSTEP * sizeof (UIDnode*)); + } + + if (NULL == chunks[nnodes / CHUNKSZ]) // Allocate new chunk for nodes. + chunks[nnodes / CHUNKSZ] = new UIDnode[CHUNKSZ]; + UIDnode *node = &chunks[nnodes / CHUNKSZ][nnodes % CHUNKSZ]; + node->uid = uid; + node->val = val; + node->next = NULL; + nnodes++; + return node; +} + +Experiment::UIDnode * +Experiment::get_uid_node (uint64_t uid, uint64_t val) +{ + int hash = (((int) uid) >> 4) & (HTableSize - 1); + if (uid != (uint64_t) 0) + { + UIDnode *node = uidHTable[hash]; + if (node && node->uid == uid) + return node; + } + UIDnode *node = new_uid_node (uid, val); + if (uid != (uint64_t) 0) + { + uidHTable[hash] = node; + uidnodes->append (node); + } + return node; +} + +Experiment::UIDnode * +Experiment::get_uid_node (uint64_t uid) +{ + if (uid == (uint64_t) 0) + return NULL; + int hash = (((int) uid) >> 4) & (HTableSize - 1); + UIDnode *node = uidHTable[hash]; + if (node && node->uid == uid) + return node; + node = new_uid_node (uid, (uint64_t) 0); + node->next = node; + return node; +} + +Experiment::UIDnode * +Experiment::find_uid_node (uint64_t uid) +{ + int hash = (((int) uid) >> 4) & (HTableSize - 1); + UIDnode *node = uidHTable[hash]; + if (node && node->uid == uid) + return node; + int lt = 0; + int rt = uidnodes->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + node = uidnodes->fetch (md); + if (node->uid < uid) + lt = md + 1; + else if (node->uid > uid) + rt = md - 1; + else + { + uidHTable[hash] = node; + return node; + } + } + return NULL; +} + +int +Experiment::frUidCmp (const void *a, const void *b) +{ + RawFramePacket *fp1 = *(RawFramePacket**) a; + RawFramePacket *fp2 = *(RawFramePacket**) b; + if (fp1->uid == fp2->uid) + return 0; + return fp1->uid < fp2->uid ? -1 : 1; +} + +Experiment::RawFramePacket * +Experiment::find_frame_packet (uint64_t uid) +{ + int lt = 0; + int rt = frmpckts->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + RawFramePacket *fp = frmpckts->fetch (md); + if (fp->uid < uid) + lt = md + 1; + else if (fp->uid > uid) + rt = md - 1; + else + return fp; + } + + return NULL; +} + +#define FRINFO_CACHEOPT_SIZE_LIMIT 4000000 +#define FRINFO_PIPELINE_SIZE_LIMIT 500000 +#define FRINFO_PIPELINE_NUM_STAGES 3 + +// Pipelined execution of resolve_frame_info() and add_stack(). +// Since this is the largest time consuming part of loading an experiment (especially +// so for large java experiments) - executing this part as a 3 stage pipeline can +// give significant performance gain - and this concept can be aggressively applied +// to enhance the gain further in future. The three stages are: +// Phase 1: resolve_frame_info() +// Phase 2: first part of add_stack() where the native stack is built +// Phase 3: second part og add_stack() where the java stack is built +// Phase 4: insert the native and java stacks into the stack map +// The main thread operates in the first Phase and the other stages are +// operated by a ssplib sequential queue - The threads working on the queues run concurrently +// with each other and with the main thread. But within a particular queue, jobs are +// executed sequentially + + +// This is the second phase of the pipeline of resolve_frame_info and add_stack +// It works on a chunk of iterations (size CSTCTX_CHUNK_SZ) and invokes add_stack() +// for each one of them + +void +Experiment::resolve_frame_info (DataDescriptor *dDscr) +{ + if (!resolveFrameInfo) + return; + if (NULL == cstack) + return; + dDscr->setResolveFrInfoDone (); + + // Check for TSTAMP + int propID = dbeSession->getPropIdByName (NTXT ("TSTAMP")); + Data *dataTStamp = dDscr->getData (propID); + if (dataTStamp == NULL) + return; + + propID = dbeSession->getPropIdByName (NTXT ("FRINFO")); + Data *dataFrinfo = dDscr->getData (propID); + + propID = dbeSession->getPropIdByName (NTXT ("THRID")); + Data *dataThrId = dDscr->getData (propID); + + // We can get frame info either by FRINFO or by [THRID,STKIDX] + if (dataFrinfo == NULL) + return; + + char *propName = NTXT ("MSTACK"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prMStack = new PropDescr (propID, propName); + prMStack->uname = dbe_strdup (GTXT ("Machine Call Stack")); + prMStack->vtype = TYPE_OBJ; + dDscr->addProperty (prMStack); + + propName = NTXT ("USTACK"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prUStack = new PropDescr (propID, propName); + prUStack->uname = dbe_strdup (GTXT ("User Call Stack")); + prUStack->vtype = TYPE_OBJ; + dDscr->addProperty (prUStack); + + propName = NTXT ("XSTACK"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prXStack = new PropDescr (propID, propName); + prXStack->uname = dbe_strdup (GTXT ("Expert Call Stack")); + prXStack->vtype = TYPE_OBJ; + dDscr->addProperty (prXStack); + + propName = NTXT ("HSTACK"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prHStack = new PropDescr (propID, propName); + prHStack->uname = dbe_strdup (GTXT ("ShowHide Call Stack")); + prHStack->vtype = TYPE_OBJ; + dDscr->addProperty (prHStack); + + if (has_java) + { + propName = NTXT ("JTHREAD"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prJThread = new PropDescr (propID, propName); + prJThread->uname = dbe_strdup (GTXT ("Java Thread")); + prJThread->vtype = TYPE_OBJ; + dDscr->addProperty (prJThread); + } + + if (ompavail) + { + PropDescr *prop = new PropDescr (PROP_OMPSTATE, NTXT ("OMPSTATE")); + prop->uname = dbe_strdup (GTXT ("OpenMP state")); + prop->vtype = TYPE_UINT32; + char * stateNames [OMP_LAST_STATE] = OMP_THR_STATE_STRINGS; + char * stateUNames[OMP_LAST_STATE] = OMP_THR_STATE_USTRINGS; + for (int ii = 0; ii < OMP_LAST_STATE; ii++) + prop->addState (ii, stateNames[ii], stateUNames[ii]); + dDscr->addProperty (prop); + + // add PROP_CPRID to profiling data (not same as omptrace's PROP_CPRID) + prop = dDscr->getProp (PROP_CPRID); + if (prop) + { + VType_type type = prop->vtype; + assert (type == TYPE_OBJ); //see 7040526 + } + prop = new PropDescr (PROP_CPRID, NTXT ("CPRID")); //profiling PROP_CPRID + prop->uname = dbe_strdup (GTXT ("OpenMP parallel region")); + prop->vtype = TYPE_OBJ; + dDscr->addProperty (prop); + + // add PROP_TSKID to profiling data (not same as omptrace's PROP_TSKID) + prop = dDscr->getProp (PROP_TSKID); + if (prop) + { + VType_type type = prop->vtype; + assert (type == TYPE_OBJ); //see 7040526 + } + prop = new PropDescr (PROP_TSKID, NTXT ("TSKID")); //profiling PROP_TSKID + prop->uname = dbe_strdup (GTXT ("OpenMP task")); + prop->vtype = TYPE_OBJ; + dDscr->addProperty (prop); + } + char *progress_bar_msg = dbe_sprintf (NTXT ("%s %s: %s"), NTXT (" "), + GTXT ("Processing CallStack Data"), + get_basename (expt_name)); + int progress_bar_percent = -1; + long deltaReport = 5000; + long nextReport = 0; + + long size = dDscr->getSize (); + // bool resolve_frinfo_pipelined = size > FRINFO_PIPELINE_SIZE_LIMIT && !ompavail; + bool resolve_frinfo_pipelined = false; + + Map<uint64_t, uint64_t> *nodeCache = NULL; + Map<uint64_t, uint64_t> *frameInfoCache = NULL; + if (size > FRINFO_CACHEOPT_SIZE_LIMIT && dversion == NULL) + { + frameInfoCache = new CacheMap<uint64_t, uint64_t>; + nodeCache = new CacheMap<uint64_t, uint64_t>; + } + + pushCnt = popCnt = pushCnt3 = popCnt3 = 0; + nPush = nPop = 0; + + FramePacket *fp = NULL; + // DbeThreadPool * threadPool = new DbeThreadPool(5); + fp = new FramePacket; + fp->stack = new Vector<Vaddr>; + fp->jstack = new Vector<Vaddr>; + fp->ompstack = new Vector<Vaddr>; + fp->omp_state = 0; + fp->mpi_state = 0; + + // piggyback on post-processing to calculate exp->last_event + const hrtime_t _exp_start_time = getStartTime (); // wall clock time + hrtime_t exp_duration = getLastEvent () == ZERO_TIME ? 0 + : getLastEvent () - _exp_start_time; // zero-based + + int missed_fi = 0; + int total_fi = 0; + + for (long i = 0; i < size; i++) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / size); + if (percent > progress_bar_percent) + { + progress_bar_percent += 10; + theApplication->set_progress (percent, progress_bar_msg); + } + nextReport += deltaReport; + } + + uint32_t thrid = (uint32_t) dataThrId->fetchInt (i); + hrtime_t tstamp = (hrtime_t) dataTStamp->fetchLong (i); + + // piggyback on post-processing to calculate exp->last_event + { + hrtime_t relative_timestamp = tstamp - _exp_start_time; + if (exp_duration < relative_timestamp) + exp_duration = relative_timestamp; + } + uint64_t frinfo = (uint64_t) dataFrinfo->fetchLong (i); + + RawFramePacket *rfp = NULL; + if (frinfo) + { + // CacheMap does not work with NULL key + if (frameInfoCache != NULL) + rfp = (RawFramePacket *) frameInfoCache->get (frinfo); + } + if (rfp == 0) + { + rfp = find_frame_packet (frinfo); + if (rfp != 0) + { + if (frameInfoCache != NULL) + frameInfoCache->put (frinfo, (uint64_t) rfp); + } + else + missed_fi++; + total_fi++; + } + + // Process OpenMP properties + if (ompavail) + { + fp->omp_state = rfp ? rfp->omp_state : 0; + dDscr->setValue (PROP_OMPSTATE, i, fp->omp_state); + + fp->omp_cprid = mapPRid->get (thrid, tstamp, mapPRid->REL_EQLE); + void *omp_preg = mapPReg->get (thrid, tstamp, mapPReg->REL_EQLE); + if (!omp_preg) + { + char *idxname = NTXT ("OMP_preg"); + int idxtype = dbeSession->findIndexSpaceByName (idxname); + if (idxtype != -1) + { + Histable *preg0 = dbeSession->findObjectById (Histable::INDEXOBJ, idxtype, (int64_t) 0); + if (preg0) + { + Vector<Histable*> pregs; + pregs.append (preg0); + omp_preg = cstack->add_stack (&pregs); + mapPReg->put (thrid, tstamp, omp_preg); + } + } + } + dDscr->setObjValue (PROP_CPRID, i, omp_preg); //profiling PROP_CPRID + void *omp_task = mapTask->get (thrid, tstamp, mapTask->REL_EQLE); + if (!omp_task) + { + char *idxname = NTXT ("OMP_task"); + int idxtype = dbeSession->findIndexSpaceByName (idxname); + if (idxtype != -1) + { + Histable *task0 = dbeSession->findObjectById (Histable::INDEXOBJ, idxtype, (int64_t) 0); + if (task0) + { + Vector<Histable*> tasks; + tasks.append (task0); + omp_task = cstack->add_stack (&tasks); + mapTask->put (thrid, tstamp, omp_task); + } + } + } + dDscr->setObjValue (PROP_TSKID, i, omp_task); //profiling PROP_TSKID + } + else + { + fp->omp_state = 0; + fp->omp_cprid = 0; + } + + // Construct the native stack + fp->stack->reset (); + Vaddr leafpc = dDscr->getULongValue (PROP_LEAFPC, i); + if (leafpc) + fp->stack->append (leafpc); + UIDnode *node = rfp ? rfp->uidn : NULL; + while (node) + { + if (node->next == node) + // this node contains link_uid + node = find_uid_node (node->uid); + else + { + fp->stack->append (node->val); + node = node->next; + } + } + fp->truncated = 0; + int last = fp->stack->size () - 1; + if (last >= 0) + { + switch (fp->stack->fetch (last)) + { + case SP_TRUNC_STACK_MARKER: + fp->truncated = (Vaddr) SP_TRUNC_STACK_MARKER; + fp->stack->remove (last); + break; + case SP_FAILED_UNWIND_MARKER: + fp->truncated = (Vaddr) SP_FAILED_UNWIND_MARKER; + fp->stack->remove (last); + break; + } + } + + // Construct the Java stack + fp->jstack->reset (); + node = rfp ? rfp->uidj : NULL; + while (node) + { + if (node->next == node) + { + // this node contains link_uid + UIDnode *n = NULL; + if (node->uid) + { + // CacheMap does not work with NULL key + if (nodeCache != NULL) + n = (UIDnode *) nodeCache->get (node->uid); + } + if (n == NULL) + { + n = find_uid_node (node->uid); + if (n != NULL) + { + if (nodeCache != NULL) + nodeCache->put (node->uid, (uint64_t) n); + } + } + node = n; + } + else + { + fp->jstack->append (node->val); + node = node->next; + } + } + fp->jtruncated = false; + last = fp->jstack->size () - 1; + if (last >= 1 && fp->jstack->fetch (last) == SP_TRUNC_STACK_MARKER) + { + fp->jtruncated = true; + fp->jstack->remove (last); + fp->jstack->remove (last - 1); + } + + // Construct the OpenMP stack + if (ompavail) + { + fp->ompstack->reset (); + if (rfp && rfp->omp_uid) + { + if (leafpc) + fp->ompstack->append (leafpc); + node = rfp->omp_uid; + while (node) + { + if (node->next == node) + // this node contains link_uid + node = find_uid_node (node->uid); + else + { + fp->ompstack->append (node->val); + node = node->next; + } + } + fp->omptruncated = false; + last = fp->ompstack->size () - 1; + if (last >= 0) + { + switch (fp->ompstack->fetch (last)) + { + case SP_TRUNC_STACK_MARKER: + fp->omptruncated = (Vaddr) SP_TRUNC_STACK_MARKER; + fp->ompstack->remove (last); + break; + case SP_FAILED_UNWIND_MARKER: + fp->omptruncated = (Vaddr) SP_FAILED_UNWIND_MARKER; + fp->ompstack->remove (last); + break; + } + } + } + } + cstack->add_stack (dDscr, i, fp, NULL); + } + + // piggyback on post-processing to calculate exp->last_event + { + hrtime_t exp_end_time = _exp_start_time + exp_duration; + update_last_event (exp_end_time); + } + + if (missed_fi > 0) + { + StringBuilder sb; + sb.sprintf ( + GTXT ("*** Warning: %d frameinfo packets are missing from total of %d when resolving %s."), + missed_fi, total_fi, dDscr->getName ()); + warnq->append (new Emsg (CMSG_WARN, sb)); + } + + // threadPool->wait_group(); + // delete threadPool; + theApplication->set_progress (0, NTXT ("")); + free (progress_bar_msg); + if (!resolve_frinfo_pipelined && fp != NULL) + { + delete fp->ompstack; + delete fp->jstack; + delete fp->stack; + delete fp; + } + delete frameInfoCache; + frameInfoCache = NULL; + delete nodeCache; + nodeCache = NULL; +} + +void +Experiment::post_process () +{ + // update non_paused_time after final update to "last_event" + if (resume_ts != MAX_TIME && last_event) + { + hrtime_t ts = last_event - exp_start_time; + hrtime_t delta = ts - resume_ts; + non_paused_time += delta; + resume_ts = MAX_TIME; // collection is paused + } + + // GC: prune events outside of experiment duration, calculate GC duration, update indices + int index; + GCEvent * gcevent; + gc_duration = ZERO_TIME; + if (gcevents != NULL) + { + // delete events that finish before exp_start_time or start after last_event + for (int ii = 0; ii < gcevents->size ();) + { + gcevent = gcevents->fetch (ii); + if (gcevent->end - exp_start_time < 0 + || last_event - gcevent->start < 0) + delete gcevents->remove (ii); + else + ii++; + } + } + Vec_loop (GCEvent*, gcevents, index, gcevent) + { + gcevent->id = index + 1; // renumber to account for any deleted events + if (gcevent->start - exp_start_time < 0 || gcevent->start == ZERO_TIME) + // truncate events that start before experiment start + gcevent->start = exp_start_time; + if (last_event - gcevent->end < 0) + // truncate events that end after experiment end + gcevent->end = last_event; + gc_duration += gcevent->end - gcevent->start; + } +} + +Experiment::Exp_status +Experiment::find_expdir (char *path) +{ + // This function checks that the experiment directory + // is of the proper form, and accessible + struct stat64 sbuf; + + // Save the name + expt_name = dbe_strdup (path); + + // Check that the name ends in .er + size_t i = strlen (path); + if (i > 0 && path[i - 1] == '/') + path[--i] = '\0'; + + if (i < 4 || strcmp (&path[i - 3], NTXT (".er")) != 0) + { + Emsg *m = new Emsg (CMSG_FATAL, + GTXT ("*** Error: not a valid experiment name")); + errorq->append (m); + status = FAILURE; + return FAILURE; + } + + // Check if new directory structure (i.e., no pointer file) + if (dbe_stat (path, &sbuf)) + { + Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: experiment not found")); + errorq->append (m); + status = FAILURE; + return FAILURE; + } + if (S_ISDIR (sbuf.st_mode) == 0) + { + // ignore pointer-file experiments + Emsg *m = new Emsg (CMSG_FATAL, + GTXT ("*** Error: experiment was recorded with an earlier version, and can not be read")); + errorq->append (m); + obsolete = 1; + status = FAILURE; + return FAILURE; + } + return SUCCESS; +} + +void +Experiment::purge () +{ + // This routine will purge all of the caches of releasable storage. + for (int i = 0; i < dataDscrs->size (); ++i) + { + DataDescriptor *dataDscr = dataDscrs->fetch (i); + if (dataDscr == NULL) + continue; + dataDscr->reset (); + } + delete cstack; + delete cstackShowHide; + cstack = CallStack::getInstance (this); + cstackShowHide = CallStack::getInstance (this); +} + +void +Experiment::resetShowHideStack () +{ + delete cstackShowHide; + cstackShowHide = CallStack::getInstance (this); +} + +#define GET_INT_VAL(v, s, len) \ + for (v = len = 0; isdigit(*s); s++, len++) { v = v * 10 + (*s -'0'); } + +static int +dir_name_cmp (const void *a, const void *b) +{ + char *s1 = *((char **) a); + char *s2 = *((char **) b); + while (*s1) + { + if (isdigit (*s1) && isdigit (*s2)) + { + int v1, v2, len1, len2; + GET_INT_VAL (v1, s1, len1); + GET_INT_VAL (v2, s2, len2); + if (v1 != v2) + return v1 - v2; + if (len1 != len2) + return len2 - len1; + continue; + } + if (*s1 != *s2) + break; + s1++; + s2++; + } + return *s1 - *s2; +} + +Vector<char*> * +Experiment::get_descendants_names () +{ + char *dir_name = get_expt_name (); + if (dir_name == NULL) + return NULL; + DIR *exp_dir = opendir (dir_name); + if (exp_dir == NULL) + return NULL; + Vector<char*> *exp_names = new Vector<char*>(); + for (struct dirent *entry = readdir (exp_dir); entry; + entry = readdir (exp_dir)) + { + if (entry->d_name[0] == '_' || strncmp (entry->d_name, "M_r", 3) == 0) + { + char *dpath = dbe_sprintf (NTXT ("%s/%s"), dir_name, entry->d_name); + struct stat64 sbuf; + if (dbe_stat (dpath, &sbuf) == 0 && S_ISDIR (sbuf.st_mode)) + exp_names->append (dpath); + else + free (dpath); + } + } + closedir (exp_dir); + if (exp_names->size () == 0) + { + delete exp_names; + return NULL; + } + exp_names->sort (dir_name_cmp); + return exp_names; +} + +bool +Experiment::create_dir (char *dname) +{ + if (mkdir (dname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) + { + return true; + } + struct stat64 sbuf; + if (dbe_stat (dname, &sbuf) != 0 || S_ISDIR (sbuf.st_mode) == 0) + { + char *buf = dbe_sprintf (GTXT ("Unable to create directory `%s'\n"), + dname); + errorq->append (new Emsg (CMSG_ERROR, buf)); + free (buf); + return false; + } + return true; +} + +char * +Experiment::get_arch_name () +{ + if (arch_name == NULL) + { + // Determine the master experiment directory. + // omazur: should do it in a less hacky way. XXXX + char *ptr = strstr_r (expt_name, DESCENDANT_EXPT_KEY); + ptr = ptr ? ptr + 3 : expt_name + strlen (expt_name); + arch_name = dbe_sprintf (NTXT ("%.*s/%s"), (int) (ptr - expt_name), + expt_name, SP_ARCHIVES_DIR); + } + return arch_name; +} + +char * +Experiment::get_fndr_arch_name () +{ + if (fndr_arch_name == NULL) + // Determine the founder experiment directory. + fndr_arch_name = dbe_strdup (get_arch_name ()); + return fndr_arch_name; +} + +enum +{ + HASH_NAME_LEN = 11 // (64 / 6 + 1) = 11 +}; + +static char * +get_hash_string (char buf[HASH_NAME_LEN + 1], uint64_t hash) +{ + static const char *har = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"; + for (int i = 0; i < HASH_NAME_LEN; i++) + { + buf[i] = har[hash & 0x3f]; + hash = hash >> 6; + } + buf[HASH_NAME_LEN] = 0; + return buf; +} + +char * +Experiment::getNameInArchive (const char *fname, bool archiveFile) +{ + char *aname = get_archived_name (fname, archiveFile); + char *ret = dbe_sprintf (NTXT ("%s/%s"), get_arch_name (), aname); + free (aname); + return ret; +} + +#define MAX_ARCHIVE_FILENAME_LEN (256 - HASH_NAME_LEN - 2) + +char * +Experiment::get_archived_name (const char *fname, bool archiveFile) +{ + char *bname = get_basename (fname); + + // dirname_hash: + char dirnameHash[HASH_NAME_LEN + 1]; + // Treat "a.out" and "./a.out" equally + uint64_t hash = bname != fname ? crc64 (fname, bname - fname) + : crc64 (NTXT ("./"), 2); + get_hash_string (dirnameHash, hash); + + char *ret; + long bname_len = dbe_sstrlen (bname); + if (bname_len > MAX_ARCHIVE_FILENAME_LEN) + { + char basenameHash[HASH_NAME_LEN + 1]; + hash = crc64 (bname, bname_len); + get_hash_string (basenameHash, hash); + ret = dbe_sprintf ("%.*s%c%s_%s", + MAX_ARCHIVE_FILENAME_LEN - HASH_NAME_LEN - 1, + bname, archiveFile ? '.' : '_', + dirnameHash, basenameHash); + } + else + ret = dbe_sprintf ("%s%c%s", bname, archiveFile ? '.' : '_', dirnameHash); + return ret; +} + +char * +Experiment::checkFileInArchive (const char *fname, bool archiveFile) +{ + if (archiveMap) + { + char *aname = get_archived_name (fname, archiveFile); + DbeFile *df = archiveMap->get (aname); + free (aname); + if (df) + return strdup (df->get_location ()); + return NULL; + } + if (founder_exp) + return founder_exp->checkFileInArchive (fname, archiveFile); + return NULL; +} + + +// Comparing SegMem + +static int +SegMemCmp (const void *a, const void *b) +{ + SegMem *item1 = *((SegMem **) a); + SegMem *item2 = *((SegMem **) b); + return item1->unload_time > item2->unload_time ? 1 : + item1->unload_time == item2->unload_time ? 0 : -1; +} + +SegMem* +Experiment::update_ts_in_maps (Vaddr addr, hrtime_t ts) +{ + Vector<SegMem *> *segMems = (Vector<SegMem *> *) maps->values (); + if (!segMems->is_sorted ()) + { + Dprintf (DEBUG_MAPS, NTXT ("update_ts_in_maps: segMems.size=%lld\n"), (long long) segMems->size ()); + segMems->sort (SegMemCmp); + } + for (int i = 0, sz = segMems ? segMems->size () : 0; i < sz; i++) + { + SegMem *sm = segMems->fetch (i); + if (ts < sm->unload_time) + { + for (; i < sz; i++) + { + sm = segMems->fetch (i); + if ((addr >= sm->base) && (addr < sm->base + sm->size)) + { + Dprintf (DEBUG_MAPS, + "update_ts_in_maps: old:%u.%09u -> %u.%09u addr=0x%08llx size=%lld\n", + (unsigned) (sm->load_time / NANOSEC), + (unsigned) (sm->load_time % NANOSEC), + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + (unsigned long long) sm->base, (long long) sm->size); + maps->remove (sm->base, sm->load_time); + sm->load_time = ts; + maps->insert (sm->base, ts, sm); + return sm; + } + } + } + } + Dprintf (DEBUG_MAPS, "update_ts_in_maps: NOT FOUND %u.%09u addr=0x%08llx\n", + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + (unsigned long long) addr); + return NULL; +} + +DbeInstr* +Experiment::map_Vaddr_to_PC (Vaddr addr, hrtime_t ts) +{ + // Look up in the hash table first + int hash = (((int) addr) >> 8) & (HTableSize - 1); + SegMem *si = smemHTable[hash]; + if (si == NULL || addr < si->base || addr >= si->base + si->size + || ts < si->load_time || ts >= si->unload_time) + { + // Not in the hash table + si = (SegMem*) maps->locate (addr, ts); + if (si == NULL || addr < si->base || addr >= si->base + si->size + || ts < si->load_time || ts >= si->unload_time) + { + si = update_ts_in_maps (addr, ts); + if (si == NULL) + return dbeSession->get_Unknown_Function ()->find_dbeinstr (PCInvlFlag, addr); + } + smemHTable[hash] = si; + } + + // Calculate the file offset of 'addr' + uint64_t f_offset = si->get_file_offset () + (addr - si->base); + + DbeInstr *instr; + if (si->obj->get_type () == Histable::LOADOBJECT) + { + LoadObject *lo = (LoadObject*) si->obj; + lo->sync_read_stabs (); + instr = lo->find_dbeinstr (f_offset); + } + else + { + int hash2 = ((((int) addr) & 0xFFFC00) | (((int) f_offset) >> 2)) + & (HTableSize - 1); + instr = instHTable[hash2]; + if (instr == NULL || instr->func != si->obj || instr->addr != f_offset) + { + // Not in the hash table + Function *fp = (Function *) si->obj; + instr = fp->find_dbeinstr (0, f_offset); + instHTable[hash2] = instr; + } + } + if (!instr->func->isUsed) + { + instr->func->isUsed = true; + instr->func->module->isUsed = true; + instr->func->module->loadobject->isUsed = true; + } + return instr; +} + +Sample * +Experiment::map_event_to_Sample (hrtime_t ts) +{ + Sample *sample; + int index; + + // Check if the last used sample is the right one, + // if not then find it. + if (sample_last_used && ts >= sample_last_used->start_time + && ts <= sample_last_used->end_time) + return sample_last_used; + + Vec_loop (Sample*, samples, index, sample) + { + if ((ts >= sample->start_time) && + (ts <= sample->end_time)) + { + sample_last_used = sample; + return sample; + } + } + return (Sample*) NULL; +} + +GCEvent * +Experiment::map_event_to_GCEvent (hrtime_t ts) +{ + GCEvent *gcevent; + int index; + + // Check if the last used sample is the right one, + // if not then find it. + if (gcevent_last_used && ts >= gcevent_last_used->start + && ts <= gcevent_last_used->end) + return gcevent_last_used; + Vec_loop (GCEvent*, gcevents, index, gcevent) + { + if ((ts >= gcevent->start) && + (ts <= gcevent->end)) + { + gcevent_last_used = gcevent; + return gcevent; + } + } + return (GCEvent*) NULL; +} + +DbeInstr* +Experiment::map_jmid_to_PC (Vaddr mid, int bci, hrtime_t ts) +{ + if (mid == 0 || jmaps == NULL) + // special case: no Java stack was recorded, bci - error code + return dbeSession->get_JUnknown_Function ()->find_dbeinstr (0, bci); + + JMethod *jmthd = jmidHTable->get (mid); + if (jmthd == NULL) + { + jmthd = (JMethod *) jmaps->locate_exact_match (mid, ts); + if (jmthd) + jmidHTable->put (mid, jmthd); + } + if (jmthd == NULL || jmthd->get_type () != Histable::FUNCTION) + return dbeSession->get_JUnknown_Function ()->find_dbeinstr (0, (uint64_t) mid); + return jmthd->find_dbeinstr (0, bci); +} + +Emsg * +Experiment::fetch_comments () +{ + return commentq->fetch (); +} + +Emsg * +Experiment::fetch_runlogq () +{ + return runlogq->fetch (); +} + +Emsg * +Experiment::fetch_errors () +{ + return errorq->fetch (); +} + +Emsg * +Experiment::fetch_warnings () +{ + return warnq->fetch (); +} + +Emsg * +Experiment::fetch_notes () +{ + return notesq->fetch (); +} + +Emsg * +Experiment::fetch_ifreq () +{ + return ifreqq->fetch (); +} + +Emsg * +Experiment::fetch_pprocq () +{ + return pprocq->fetch (); +} + +int +Experiment::read_dyntext_file () +{ + char *data_file_name = dbe_sprintf ("%s/%s", expt_name, SP_DYNTEXT_FILE); + Data_window *dwin = new Data_window (data_file_name); + if (dwin->not_opened ()) + { + free (data_file_name); + delete dwin; + return 1; + } + dwin->need_swap_endian = need_swap_endian; + + Function *fp = NULL; + char *progress_msg = NULL; // Message for the progress bar + for (int64_t offset = 0;;) + { + DT_common *cpckt = (DT_common *) dwin->bind (offset, sizeof (DT_common)); + if (cpckt == NULL) + break; + size_t cpcktsize = dwin->decode (cpckt->size); + cpckt = (DT_common *) dwin->bind (offset, cpcktsize); + if (cpckt == NULL) + break; + switch (dwin->decode (cpckt->type)) + { + case DT_HEADER: + { + DT_header *hdr = (DT_header*) cpckt; + hrtime_t ts = dwin->decode (hdr->time) + exp_start_time; + SegMem *si = (SegMem*) maps->locate (dwin->decode (hdr->vaddr), ts); + fp = si ? (Function *) si->obj : NULL; + if (fp && (fp->get_type () != Histable::FUNCTION + || !(fp->flags & FUNC_FLAG_DYNAMIC))) + fp = NULL; + break; + } + case DT_CODE: + if (fp) + { + fp->img_fname = data_file_name; + fp->img_offset = offset + sizeof (DT_common); + if ((platform != Intel) && (platform != Amd64)) + { //ARCH(SPARC) + // Find out 'save' instruction address for SPARC + char *ptr = ((char*) cpckt) + sizeof (DT_common); + size_t img_size = cpcktsize - sizeof (DT_common); + for (size_t i = 0; i < img_size; i += 4) + if (ptr[i] == (char) 0x9d && ptr[i + 1] == (char) 0xe3) + { + fp->save_addr = i; + break; + } + } + } + break; + case DT_SRCFILE: + if (fp) + { + char *srcname = dbe_strndup (((char*) cpckt) + sizeof (DT_common), + cpcktsize - sizeof (DT_common)); + LoadObject *ds = fp->module ? fp->module->loadobject : NULL; + assert (ds != NULL); + Module *mod = dbeSession->createModule (ds, NULL); + mod->set_file_name (srcname); + //} + if (fp->module) + { + // It's most likely (unknown). Remove fp from it. + long idx = fp->module->functions->find (fp); + if (idx >= 0) + fp->module->functions->remove (idx); + } + fp->module = mod; + mod->functions->append (fp); + } + break; + case DT_LTABLE: + if (fp) + { + DT_lineno *ltab = (DT_lineno*) ((char*) cpckt + sizeof (DT_common)); + size_t sz = (cpcktsize - sizeof (DT_common)) / sizeof (DT_lineno); + if (sz <= 0) + break; + // Take care of the progress bar + static int percent = 0; + static long deltaReport = sz / 100; // 1000; + static long nextReport = 0; + static long progress_count = 0; + fp->pushSrcFile (fp->getDefSrc (), 0); + for (size_t i = 0; i < sz; i++) + { + int lineno = dwin->decode (ltab[i].lineno); + if (fp->usrfunc != NULL) + { + // Update progress bar + if (dbeSession->is_interactive ()) + { + if (progress_count == nextReport) + { + if (percent < 99) + { + percent++; + if (NULL == progress_msg) + { + progress_msg = dbe_sprintf (GTXT ("Processing Dynamic Text: %s"), + get_basename (expt_name)); + } + theApplication->set_progress (percent, progress_msg); + nextReport += deltaReport; + } + } + progress_count++; + } + DbeLine *dbeline = fp->usrfunc->mapPCtoLine (lineno, NULL); + lineno = dbeline != NULL ? dbeline->lineno : -1; + } + fp->add_PC_info (dwin->decode (ltab[i].offset), lineno); + } + fp->popSrcFile (); + } + break; + default: + // skip unknown records + break; + } + offset += cpcktsize; + } + free (progress_msg); + free (data_file_name); + delete dwin; + return 0; +} + +uint32_t +Experiment::mapTagValue (Prop_type prop, uint64_t value) +{ + Vector<Histable*> *objs = tagObjs->fetch (prop); + int lt = 0; + int rt = objs->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + Other *obj = (Other*) objs->fetch (md); + if (obj->value64 < value) + lt = md + 1; + else if (obj->value64 > value) + rt = md - 1; + else + return obj->tag; + } + + uint32_t tag; + if (sparse_threads && (prop == PROP_THRID || prop == PROP_LWPID)) + tag = objs->size () + 1; // "+ 1" related to 7038295 + else + tag = (int) value; // truncation; See 6788767 + + Other *obj = new Other (); + obj->value64 = value; + obj->tag = tag; + if (lt == objs->size ()) + objs->append (obj); + else + objs->insert (lt, obj); + + // Update min and max tags + if (prop == PROP_LWPID) + { + if ((uint64_t) tag < min_lwp) + min_lwp = (uint64_t) tag; + if ((uint64_t) tag > max_lwp) + max_lwp = (uint64_t) tag; + lwp_cnt++; + } + else if (prop == PROP_THRID) + { + if ((uint64_t) tag < min_thread) + min_thread = (uint64_t) tag; + if ((uint64_t) tag > max_thread) + max_thread = (uint64_t) tag; + thread_cnt++; + } + else if (prop == PROP_CPUID) + { + // On Solaris 8, we don't get CPU id -- don't change + if (value != (uint64_t) - 1) + {//YXXX is this related only to solaris 8? + if ((uint64_t) tag < min_cpu) + min_cpu = (uint64_t) tag; + if ((uint64_t) tag > max_cpu) + max_cpu = (uint64_t) tag; + } + cpu_cnt++; + } + return obj->tag; +} + +Vector<Histable*> * +Experiment::getTagObjs (Prop_type prop) +{ + return tagObjs->fetch (prop); +} + +Histable * +Experiment::getTagObj (Prop_type prop, uint32_t tag) +{ + Vector<Histable*> *objs = tagObjs->fetch (prop); + if (objs == NULL) + return NULL; + for (int i = 0; i < objs->size (); i++) + { + Other *obj = (Other*) objs->fetch (i); + if (obj->tag == tag) + return obj; + } + return NULL; +} + +JThread * +Experiment::map_pckt_to_Jthread (uint32_t tid, hrtime_t tstamp) +{ + if (!has_java) + return JTHREAD_DEFAULT; + int lt = 0; + int rt = jthreads_idx->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jthread = jthreads_idx->fetch (md); + if (jthread->tid < tid) + lt = md + 1; + else if (jthread->tid > tid) + rt = md - 1; + else + { + for (; jthread; jthread = jthread->next) + if (tstamp >= jthread->start && tstamp < jthread->end) + return jthread; + break; + } + } + + return JTHREAD_NONE; +} + +JThread* +Experiment::get_jthread (uint32_t tid) +{ + if (!has_java) + return JTHREAD_DEFAULT; + int lt = 0; + int rt = jthreads_idx->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jthread = jthreads_idx->fetch (md); + if (jthread->tid < tid) + lt = md + 1; + else if (jthread->tid > tid) + rt = md - 1; + else + { + JThread *jthread_first = jthread; + while ((jthread = jthread->next) != NULL) + if (!jthread->is_system () && + jthread->jthr_id < jthread_first->jthr_id) + jthread_first = jthread; + return jthread_first; + } + } + + return JTHREAD_NONE; +} + +// SS12 experiment +DataDescriptor * +Experiment::newDataDescriptor (int data_id, int flags, + DataDescriptor *master_dDscr) +{ + DataDescriptor *dataDscr = NULL; + if (data_id >= 0 && data_id < dataDscrs->size ()) + { + dataDscr = dataDscrs->fetch (data_id); + if (dataDscr != NULL) + return dataDscr; + } + + assert (data_id >= 0 && data_id < DATA_LAST); + const char *nm = get_prof_data_type_name (data_id); + const char *uname = get_prof_data_type_uname (data_id); + + if (master_dDscr) + dataDscr = new DataDescriptor (data_id, nm, uname, master_dDscr); + else + dataDscr = new DataDescriptor (data_id, nm, uname, flags); + dataDscrs->store (data_id, dataDscr); + return dataDscr; +} + +Vector<DataDescriptor*> * +Experiment::getDataDescriptors () +{ + Vector<DataDescriptor*> *result = new Vector<DataDescriptor*>; + for (int i = 0; i < dataDscrs->size (); ++i) + { + DataDescriptor *dd; + dd = get_raw_events (i); // force data fetch + if (dd != NULL) + result->append (dd); + } + return result; +} + +DataDescriptor * +Experiment::getDataDescriptor (int data_id) +{ + if (data_id < 0 || data_id >= dataDscrs->size ()) + return NULL; + return dataDscrs->fetch (data_id); +} + +PacketDescriptor * +Experiment::newPacketDescriptor (int kind, DataDescriptor *dDscr) +{ + PacketDescriptor *pDscr = new PacketDescriptor (dDscr); + pcktDscrs->store (kind, pDscr); + return pDscr; +} + +PacketDescriptor * +Experiment::getPacketDescriptor (int kind) +{ + if (kind < 0 || kind >= pcktDscrs->size ()) + return NULL; + return pcktDscrs->fetch (kind); +} + +void +Experiment::set_clock (int clk) +{ + if (clk > 0) + { + if (maxclock < clk) + { + maxclock = clk; + clock = maxclock; + } + if (minclock == 0 || minclock > clk) + minclock = clk; + } +} + +bool +JThread::is_system () +{ + if (group_name == NULL) + return false; + return strcmp (group_name, NTXT ("system")) == 0; +} + +void +Experiment::dump_stacks (FILE *outfile) +{ + cstack->print (outfile); +} + +void +Experiment::dump_map (FILE *outfile) +{ + int index; + SegMem *s; + fprintf (outfile, GTXT ("Experiment %s\n"), get_expt_name ()); + fprintf (outfile, GTXT ("Address Size (hex) Load time Unload time Checksum Name\n")); + Vec_loop (SegMem*, seg_items, index, s) + { + timestruc_t load; + timestruc_t unload; + hr2timestruc (&load, (s->load_time - exp_start_time)); + if (load.tv_nsec < 0) + { + load.tv_sec--; + load.tv_nsec += NANOSEC; + } + if (s->unload_time == MAX_TIME) + { + unload.tv_sec = 0; + unload.tv_nsec = 0; + } + else + hr2timestruc (&unload, (s->unload_time - exp_start_time)); + if (load.tv_nsec < 0) + { + load.tv_sec--; + load.tv_nsec += NANOSEC; + } + fprintf (outfile, + "0x%08llx %8lld (0x%08llx) %5ld.%09ld %5ld.%09ld \"%s\"\n", + s->base, s->size, s->size, load.tv_sec, load.tv_nsec, + unload.tv_sec, unload.tv_nsec, s->obj->get_name ()); + } + fprintf (outfile, NTXT ("\n")); +} + +/** + * Copy file to archive + * @param name + * @param aname + * @param hide_msg + * @return 0 - success, 1 - error + */ +int +Experiment::copy_file_to_archive (const char *name, const char *aname, int hide_msg) +{ + errno = 0; + int fd_w = open64 (aname, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (fd_w == -1) + { + if (errno == EEXIST) + return 0; + fprintf (stderr, GTXT ("er_archive: unable to copy `%s': %s\n"), + name, STR (strerror (errno))); + return 1; + } + + if (dbe_stat_file (name, NULL) != 0) + { + fprintf (stderr, GTXT ("er_archive: cannot access file `%s': %s\n"), + name, STR (strerror (errno))); + close (fd_w); + return 1; + } + + int fd_r = open64 (name, O_RDONLY); + if (fd_r == -1) + { + fprintf (stderr, GTXT ("er_archive: unable to open `%s': %s\n"), + name, strerror (errno)); + close (fd_w); + unlink (aname); + return 1; + } + + if (!hide_msg) + fprintf (stderr, GTXT ("Copying `%s' to `%s'\n"), name, aname); + bool do_unlink = false; + for (;;) + { + unsigned char buf[65536]; + int n, n1; + n = (int) read (fd_r, (void *) buf, sizeof (buf)); + if (n <= 0) + break; + n1 = (int) write (fd_w, buf, n); + if (n != n1) + { + fprintf (stderr, GTXT ("er_archive: unable to write %d bytes to `%s': %s\n"), + n, aname, STR (strerror (errno))); + do_unlink = true; + break; + } + } + close (fd_w); + + struct stat64 s_buf; + if (fstat64 (fd_r, &s_buf) == 0) + { + struct utimbuf u_buf; + u_buf.actime = s_buf.st_atime; + u_buf.modtime = s_buf.st_mtime; + utime (aname, &u_buf); + } + close (fd_r); + if (do_unlink) + { + if (!hide_msg) + fprintf (stderr, GTXT ("er_archive: remove %s\n"), aname); + unlink (aname); + return 1; + } + return 0; +} + +/** + * Copy file to common archive + * Algorithm: + * Calculate checksum + * Generate file name to be created in common archive + * Check if it is not in common archive yet + * Copy file to the common archive directory if it is not there yet + * Create symbolic link: "aname" -> "caname", where "caname" is the name in common archive + * @param name - original file name + * @param aname - file name in experiment archive + * @param common_archive - common archive directory + * @return 0 - success, 1 - error + */ +int +Experiment::copy_file_to_common_archive (const char *name, const char *aname, + int hide_msg, + const char *common_archive, + int relative_path) +{ + if (!name || !aname || !common_archive) + { + if (!name) + fprintf (stderr, GTXT ("er_archive: Internal error: file name is NULL\n")); + if (!aname) + fprintf (stderr, GTXT ("er_archive: Internal error: file name in archive is NULL\n")); + if (!common_archive) + fprintf (stderr, GTXT ("er_archive: Internal error: path to common archive is NULL\n")); + return 1; + } + // Check if file is already archived + if (dbe_stat (aname, NULL) == 0) + return 0; // File is already archived + // Generate full path to common archive directory + char *cad = NULL; + char *abs_aname = NULL; + if ((common_archive[0] != '/') || (aname[0] != '/')) + { + long size = pathconf (NTXT ("."), _PC_PATH_MAX); + if (size < 0) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: pathconf(\".\", _PC_PATH_MAX) failed\n")); + return 1; + } + char *buf = (char *) malloc ((size_t) size); + if (buf == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + char *ptr = getcwd (buf, (size_t) size); + if (ptr == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: cannot determine current directory\n")); + free (buf); + return 1; + } + if (common_archive[0] != '/') + cad = dbe_sprintf (NTXT ("%s/%s"), ptr, common_archive); + else + cad = dbe_strdup (common_archive); + if (aname[0] != '/') + abs_aname = dbe_sprintf (NTXT ("%s/%s"), ptr, aname); + else + abs_aname = dbe_strdup (aname); + free (buf); + } + else + { + cad = dbe_strdup (common_archive); + abs_aname = dbe_strdup (aname); + } + // Calculate checksum + char * errmsg = NULL; + uint32_t crcval = get_cksum (name, &errmsg); + if (0 == crcval) + { // error + free (cad); + free (abs_aname); + if (NULL != errmsg) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: %s\n"), errmsg); + free (errmsg); + return 1; + } + fprintf (stderr, + GTXT ("er_archive: Fatal error: get_cksum(%s) returned %d\n"), + name, crcval); + return 1; + } + // Generate file name to be created in common archive + char *fname = get_basename (name); + char *abs_caname = dbe_sprintf (NTXT ("%s/%u_%s"), cad, crcval, fname); + if (abs_caname == NULL) + { + free (cad); + free (abs_aname); + fprintf (stderr, + GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + // Check if full name is not too long + long len = dbe_sstrlen (abs_caname); + long max = pathconf (cad, _PC_PATH_MAX); + if ((max < 0) || (len <= 0)) + { // unknown error + fprintf (stderr, GTXT ("er_archive: Fatal error: pathconf(%s, _PC_PATH_MAX) failed\n"), + cad); + free (abs_caname); + free (cad); + free (abs_aname); + return 1; + } + if (len >= max) + { + // Try to truncate the name + if ((len - max) <= dbe_sstrlen (fname)) + { + // Yes, we can do it + abs_caname[max - 1] = 0; + if (!hide_msg) + fprintf (stderr, GTXT ("er_archive: file path is too long - truncated:%s\n"), + abs_caname); + } + } + // Check if file name is not too long + char *cafname = get_basename (abs_caname); + len = dbe_sstrlen (cafname); + max = pathconf (cad, _PC_NAME_MAX); + if ((max < 0) || (len <= 0)) + { // unknown error + fprintf (stderr, GTXT ("er_archive: Fatal error: pathconf(%s, _PC_NAME_MAX) failed\n"), + cad); + free (abs_caname); + free (cad); + free (abs_aname); + return 1; + } + if (len >= max) + { + // Try to truncate the name + if ((len - max) <= dbe_sstrlen (fname)) + { + // Yes, we can do it + cafname[max - 1] = 0; + if (!hide_msg) + fprintf (stderr, GTXT ("er_archive: file name is too long - truncated:%s\n"), + abs_caname); + } + } + // Copy file to the common archive directory if it is not there yet + int res = 0; + if (dbe_stat_file (abs_caname, NULL) != 0) + { + // Use temporary file to avoid synchronization problems + char *t = dbe_sprintf ("%s/archive_%llx", cad, (unsigned long long) gethrtime()); + free (cad); + // Copy file to temporary file + res = copy_file_to_archive (name, t, hide_msg); // hide messages + if (res != 0) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: cannot copy file %s to temporary file: %s\n"), + name, t); + unlink (t); + free (t); + free (abs_caname); + free (abs_aname); + return 1; + } + // Set read-only permissions + struct stat64 statbuf; + if (0 == dbe_stat_file (name, &statbuf)) + { + mode_t mask = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + mode_t mode = statbuf.st_mode & mask; + chmod (t, mode); + } + // Try to rename temporary file "t" to "abs_caname" + // res = link(t, abs_caname); // link() fails on some f/s - use rename() + res = rename (t, abs_caname); + if (res != 0) + { + if (errno != EEXIST) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: rename(%s, %s) returned error: %d\n"), + t, abs_caname, res); + unlink (t); + free (t); + free (abs_caname); + free (abs_aname); + return 1; + } + // File "abs_caname" is already there - continue + } + unlink (t); + free (t); + } + else + free (cad); + char *lname = NULL; + if (relative_path) + { + if (common_archive[0] != '/' && aname[0] != '/') + { + // compare one relative path to another and find common beginning + char *rel_caname = dbe_sprintf ("%s/%s", common_archive, cafname); + if (rel_caname == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + lname = get_relative_link (rel_caname, aname); + free (rel_caname); + } + else + { + if (abs_aname == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + lname = get_relative_link (abs_caname, abs_aname); + } + } + else // absolute path + lname = dbe_strdup (abs_caname); + free (abs_aname); + if (lname == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + // Create symbolic link: aname -> lname + if (dbe_stat_file (abs_caname, NULL) == 0) + { + res = symlink (lname, aname); + if (res != 0) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: symlink(%s, %s) returned error: %d (errno=%s)\n"), + lname, aname, res, strerror (errno)); + free (abs_caname); + free (lname); + return 1; + } + if (!hide_msg) + fprintf (stderr, GTXT ("Created symbolic link %s to file in common archive: %s\n"), + aname, lname); + } + else + { + fprintf (stderr, GTXT ("er_archive: Internal error: file does not exist in common archive: %s\n"), + abs_caname); + res = 1; + } + free (abs_caname); + free (lname); + return res; +} + +/** + * Copy file to archive + * @param name + * @param aname + * @param hide_msg + * @param common_archive + * @return 0 - success + */ +int +Experiment::copy_file (char *name, char *aname, int hide_msg, char *common_archive, int relative_path) +{ + if (common_archive) + { + if (0 == copy_file_to_common_archive (name, aname, hide_msg, + common_archive, relative_path)) + return 0; + // Error. For now - fatal error. Message is already printed. + fprintf (stderr, GTXT ("er_archive: Fatal error: cannot copy file %s to common archive %s\n"), + name, common_archive); + return 1; + } + return (copy_file_to_archive (name, aname, hide_msg)); +} + +LoadObject * +Experiment::createLoadObject (const char *path, uint64_t chksum) +{ + LoadObject *lo = dbeSession->createLoadObject (path, chksum); + if (lo->firstExp == NULL) + lo->firstExp = this; + return lo; +} + +LoadObject * +Experiment::createLoadObject (const char *path, const char *runTimePath) +{ + DbeFile *df = findFileInArchive (path, runTimePath); + if (df && (df->get_stat () == NULL)) + df = NULL; // No access to file + LoadObject *lo = dbeSession->createLoadObject (path, runTimePath, df); + if (df && (lo->dbeFile->get_location (false) == NULL)) + { + lo->dbeFile->set_location (df->get_location ()); + lo->dbeFile->inArchive = df->inArchive; + lo->dbeFile->sbuf = df->sbuf; + lo->dbeFile->experiment = df->experiment; + lo->firstExp = df->experiment; + } + if (lo->firstExp == NULL) + { + lo->firstExp = this; + lo->dbeFile->experiment = this; + } + return lo; +} + +SourceFile * +Experiment::get_source (const char *path) +{ + if (founder_exp && (founder_exp != this)) + return founder_exp->get_source (path); + if (sourcesMap == NULL) + sourcesMap = new StringMap<SourceFile*>(1024, 1024); + if (strncmp (path, NTXT ("./"), 2) == 0) + path += 2; + SourceFile *sf = sourcesMap->get (path); + if (sf) + return sf; + char *fnm = checkFileInArchive (path, false); + if (fnm) + { + sf = new SourceFile (path); + dbeSession->append (sf); + DbeFile *df = sf->dbeFile; + df->set_location (fnm); + df->inArchive = true; + df->check_access (fnm); // init 'sbuf' + df->sbuf.st_mtime = 0; // Don't check timestamps + free (fnm); + } + else + sf = dbeSession->createSourceFile (path); + sourcesMap->put (path, sf); + return sf; +} + +Vector<Histable*> * +Experiment::get_comparable_objs () +{ + update_comparable_objs (); + if (comparable_objs || dbeSession->expGroups->size () <= 1) + return comparable_objs; + comparable_objs = new Vector<Histable*>(dbeSession->expGroups->size ()); + for (long i = 0, sz = dbeSession->expGroups->size (); i < sz; i++) + { + ExpGroup *gr = dbeSession->expGroups->get (i); + if (groupId == gr->groupId) + { + comparable_objs->append (this); + continue; + } + Histable *h = NULL; + for (long i1 = 0, sz1 = gr->exps ? gr->exps->size () : 0; i1 < sz1; i1++) + { + Experiment *exp = gr->exps->get (i1); + if ((exp->comparable_objs == NULL) && (dbe_strcmp (utargname, exp->utargname) == 0)) + { + exp->phaseCompareIdx = phaseCompareIdx; + h = exp; + h->comparable_objs = comparable_objs; + break; + } + } + comparable_objs->append (h); + } + dump_comparable_objs (); + return comparable_objs; +} + +DbeFile * +Experiment::findFileInArchive (const char *fname) +{ + if (archiveMap) + { + char *aname = get_archived_name (fname); + DbeFile *df = archiveMap->get (aname); + free (aname); + return df; + } + if (founder_exp) + return founder_exp->findFileInArchive (fname); + return NULL; +} + +DbeFile * +Experiment::findFileInArchive (const char *className, const char *runTimePath) +{ + DbeFile *df = NULL; + if (runTimePath) + { + const char *fnm = NULL; + if (strncmp (runTimePath, NTXT ("zip:"), 4) == 0) + fnm = runTimePath + 4; + else if (strncmp (runTimePath, NTXT ("jar:file:"), 9) == 0) + fnm = runTimePath + 9; + if (fnm) + { + const char *s = strchr (fnm, '!'); + if (s) + { + char *s1 = dbe_strndup (fnm, s - fnm); + df = findFileInArchive (s1); + free (s1); + } + else + df = findFileInArchive (fnm); + if (df) + df->filetype |= DbeFile::F_JAR_FILE; + } + else if (strncmp (runTimePath, NTXT ("file:"), 5) == 0) + { + fnm = runTimePath + 5; + df = findFileInArchive (fnm); + } + else + df = findFileInArchive (runTimePath); + } + if (df == NULL) + df = findFileInArchive (className); + return df; +} diff --git a/gprofng/src/Experiment.h b/gprofng/src/Experiment.h new file mode 100644 index 0000000..41c44e4 --- /dev/null +++ b/gprofng/src/Experiment.h @@ -0,0 +1,689 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _EEXPERIMENT_H +#define _EEXPERIMENT_H + +// The experiment class is responsible for managing all the data +// for an individual experiment + +#include "Metric.h" +#include "Histable.h" +#include "Stats_data.h" +#include "DefaultMap.h" +#include "HeapMap.h" + +class Data_window; +class DbeFile; +class CallStack; +class JMethod; +class Sample; +class SegMem; +class LoadObject; +class SourceFile; +class UserLabel; +class PRBTree; +class Emsg; +class Emsgqueue; +struct JThread; +struct GCEvent; +class FileData; +class Module; +class Experiment; +template <class ITEM> class Vector; + +#define JTHREAD_DEFAULT ((JThread*)0) +#define JTHREAD_NONE ((JThread*)-1) + +// When we perform the pipelined optimization on resolve_frame_info() and add_stack() +// this is the number of iterations one phase works on before passing on the work to +// the next phase + +#define CSTCTX_CHUNK_SZ 10000 +#define PIPELINE_QUEUE_SZ_HI 8 +#define PIPELINE_QUEUE_SZ_LOW 2 + +// the add_stack_ctx structure contains the intermediate state (context) after +// CSTCTX_CHUNK_SZ number of iterations to pass on the work to another thread to +// operate on the next stage +typedef struct +{ + Vector<DbeInstr*> *natpcs; + Vector<Histable*> *jpcs; + long idx; + FramePacket *frp; + hrtime_t tstamp; + uint32_t thrid; + bool last_ctx; +} cstk_ctx; + +// To minimize threadpool overhead, the granularity of a job submitted is made larger: +// containing a chunk of iterations (of size CSTCTX_CHUNK_SZ) +typedef struct +{ + cstk_ctx* cstCtxAr[CSTCTX_CHUNK_SZ]; + int last_idx; + long idx_begin; + long idx_end; + DataDescriptor *dDscr; + Experiment *exp; + void *cstk; +} cstk_ctx_chunk; + +class Experiment : public Histable, public DbeMessages +{ +public: + + enum Exp_status + { + SUCCESS, + INCOMPLETE, + FAILURE + }; + + Experiment (); + virtual ~Experiment (); + + virtual Histable_type + get_type () + { + return EXPERIMENT; + }; + virtual Vector<Histable*> *get_comparable_objs (); + + int groupId; + Experiment *founder_exp; // parent of this experiment + Vector<Experiment*> *children_exps; // children of this experiment + + // Configuration Information + char *hostname; // Hosthame (e.g. mymachine) + long start_sec; // Starting timeval secs. + char *username; // name of person performing the test + char *architecture; // Architecture name ("sun4") + Platform_t platform; // Sparc,Sparcv9,Intel + WSize_t wsize; // word size: may be w32 or w64 + int clock; // CPU clock frequency, Mhz + int varclock; // Set if CPU clock frequency can change: turbo-mode + int maxclock; // max. CPU clock frequency on MP machine + int minclock; // min. CPU clock frequency on MP machine + int ncpus; // count of CPUs where expt was recorded + int hw_cpuver; // CPU version from libcpc + char *machinemodel; // machine model of machine on which experiment was recorded + char *os_version; // Operating system name + int page_size; // Page size (bytes) + int npages; // Number of page size + int exp_maj_version; // major version number of current experiment + int exp_min_version; // minor version number of current experiment + int hex_field_width; // number of digits in hex form of address + // for current experiment, i.e. 8 for 32bit addresses + int broken; // If SP_JCMD_RUN line not seen + int obsolete; // If pointer file experiment detected + bool hwc_default; // True if HW counters were enabled by default + int hwc_bogus; // Count of bogus HWC packets + int hwc_lost_int; // Count of packets reflecting lost interrupt + int hwc_scanned; // If the HWC packets have been scanned + int invalid_packet; // Count of invalid packets + bool exec_started; // True if exec was called, and exec error not yet seen + bool dataspaceavail; // True if dataspace data is in the experiment + bool leaklistavail; // True if leaklist data is in the experiment + bool heapdataavail; // True if heap data is in the experiment + bool racelistavail; // true if there are race events in the experiment + bool iodataavail; // true if there are io events in the experiment + bool deadlocklistavail; // true if there are deadlock events in the experiment + bool timelineavail; // true if there are valid timestamps in the experiment + bool ifreqavail; // True if instruction-frequency data is in the experiment + bool ompavail; // true if there is OpenMP data in the experiment + bool has_java; + char *uarglist; // argv[] array, as a string + char *utargname; // basename of argv[0] extracted from uarglist + char *ucwd; // working directory + char *cversion; // collector version string + char *dversion; // driver version string (er_kernel) + char *jversion; // Java version string (java profiling) + + // Open the named experiment record and process log file + Exp_status open (char *directory_name); + + // Update experiment (read and process new data) + Exp_status update (); + + // Returns collector parameters for the current sample selection + Collection_params * + get_params () + { + return &coll_params; + } + + Exp_status + get_status () + { + return status; + } + + // Returns the number of samples. For use by FilterNumeric + int + nsamples () + { + return samples->size (); + } + + // Release any releasable memory. + void purge (); + + void resetShowHideStack (); + int save_notes (char*, bool); + int delete_notes (bool); + Experiment *getBaseFounder (); // returns topmost founder or this if no descendents + + hrtime_t + getStartTime () + { + return exp_start_time; + } + hrtime_t getRelativeStartTime (); // delta between start and founder's start + + hrtime_t + getWallStartSec () + { + return start_sec; + } + + hrtime_t + getLastEvent () + { + if (last_event != ZERO_TIME) + return last_event; + return exp_start_time; + } + + hrtime_t + getGCDuration () + { + return gc_duration; + } + + int + getPID () + { + return pid; + } + + int + getUserExpId () + { + return userExpId; + } + + int + getExpIdx () + { + return expIdx; + } + + void + setExpIdx (int idx) + { + expIdx = idx; + } + + void + setUserExpId (int idx) + { + userExpId = idx; + } + + void + setTinyThreshold (int limit) + { + tiny_threshold = limit; + } + + bool + isDiscardedTinyExperiment () + { + return discardTiny; + } + + Exp_status open_epilogue (); + void read_experiment_data (bool read_ahead); + static int copy_file_to_archive (const char *name, const char *aname, int hide_msg); + static int copy_file_to_common_archive (const char *name, const char *aname, + int hide_msg, const char *common_archive, int relative_path = 0); + static int copy_file (char *name, char *aname, int hide_msg, + char *common_archive = NULL, int relative_path = 0); + + // get_raw_events() + // action: get unfiltered packets, loading them if required + // parameters: data_id (see ProfData_type) + DataDescriptor *get_raw_events (int data_id); + Vector<DataDescriptor*> *getDataDescriptors (); + + // Some DATA_* types are derived from others, e.g. DATA_HEAPSZ is derived from DATA_HEAP + // The following hooks support derived DataViews + int base_data_id (int data_id); // returns base data_id type (ProfData_type DATA_*) + DataView *create_derived_data_view (int data_id, DataView *dview); + + Vector<BaseMetric*>* + get_metric_list () + { + return metrics; + } + + char * + get_expt_name () + { + return expt_name; // Return the pathname to the experiment + }; + + Vector<char*> *get_descendants_names (); + char *get_fndr_arch_name (); + char *get_arch_name (); + char *getNameInArchive (const char *fname, bool archiveFile = false); + char *checkFileInArchive (const char *fname, bool archiveFile = false); + DbeFile *findFileInArchive (const char *className, const char *runTimePath); + DbeFile *findFileInArchive (const char *fname); + bool create_dir (char *dname); + + Vaddr + ret_stack_base () + { + return stack_base; + }; + + // Map a virtual address to a PC pair + DbeInstr *map_Vaddr_to_PC (Vaddr addr, hrtime_t ts); + DbeInstr *map_jmid_to_PC (Vaddr mid, int lineno, hrtime_t ts); + Sample *map_event_to_Sample (hrtime_t ts); + GCEvent *map_event_to_GCEvent (hrtime_t ts); + + DataView * + getOpenMPdata () + { + return openMPdata; + } + + time_t + get_mtime () + { + return mtime; + } + + Emsg *fetch_comments (void); // fetch the queue of comment messages + Emsg *fetch_runlogq (void); // fetch the queue of run log messages + Emsg *fetch_errors (void); // fetch the queue of error messages + Emsg *fetch_warnings (void); // fetch the queue of warning messages + Emsg *fetch_notes (void); // fetch the queue of notes messages + Emsg *fetch_ifreq (void); // fetch the queue of ifreq messages + Emsg *fetch_pprocq (void); // fetch the queue of post-processing messages + + // message queues + Emsgqueue *commentq; // comments for the experiment header + Emsgqueue *runlogq; // used temporarily; after log file processing, + // messages are appended to the commentq + Emsgqueue *errorq; // error messages + Emsgqueue *warnq; // warning messages + Emsgqueue *notesq; // user-written notes messages + Emsgqueue *pprocq; // postprocessing messages + Emsgqueue *ifreqq; // Instruction frequency data, from count experiment + Map<const char*, LoadObject*> *loadObjMap; + Vector<LoadObject*> *loadObjs; + void append (LoadObject *lo); + LoadObject *createLoadObject (const char *path, uint64_t chksum = 0); + LoadObject *createLoadObject (const char *path, const char *runTimePath); + SourceFile *get_source (const char *path); + void set_clock (int clk); + + CallStack * + callTree () + { + return cstack; + } + + CallStack * + callTreeShowHide () + { + return cstackShowHide; + } + + uint32_t mapTagValue (Prop_type, uint64_t value); + Histable *getTagObj (Prop_type, uint32_t idx); + Vector<Histable*> *getTagObjs (Prop_type); + + JThread *map_pckt_to_Jthread (uint32_t tid, hrtime_t tstamp); + JThread *get_jthread (uint32_t tid); + + Vector<JThread*> * + get_jthreads () + { + return jthreads; + } + + Vector<GCEvent*> * + get_gcevents () + { + return gcevents; + } + + bool need_swap_endian; + Collection_params coll_params; // Collection params + + // Ranges for threads, lwps, cpu + uint64_t min_thread; + uint64_t max_thread; + uint64_t thread_cnt; + uint64_t min_lwp; + uint64_t max_lwp; + uint64_t lwp_cnt; + uint64_t min_cpu; + uint64_t max_cpu; + uint64_t cpu_cnt; + uint64_t dsevents; // count of dataspace events + uint64_t dsnoxhwcevents; /* count of ds events that could be be validated + * because of no branch target info */ + + PacketDescriptor *newPacketDescriptor (int kind, DataDescriptor *dDscr); + PacketDescriptor *getPacketDescriptor (int kind); + + // debugging aids -- dump_stacks, dump_map + void dump_stacks (FILE *); + void dump_map (FILE *); + + // These methods are used in nightly performance regression testing + void DBG_memuse (Sample *); + void DBG_memuse (const char *sname); + void init_cache (); + + DefaultMap<int64_t, FileData*> * + getFDataMap () + { + return fDataMap; + } + CallStack *cstack; + +protected: + + Exp_status status; // Error status + Vector<SegMem*> *seg_items; // Master list of seg_items + CallStack *cstackShowHide; + PRBTree *maps; // All maps in (Vaddr,time) + + hrtime_t gc_duration; // wall-clock hrtime of total GC intervals + hrtime_t exp_start_time; // wall-clock hrtime at exp start + hrtime_t last_event; // wall-clock hrtime of last known sample or log.xml entry + hrtime_t non_paused_time; // sum of periods where data collection is active (not paused) + hrtime_t resume_ts; // tracks log.xml start/resume times + void update_last_event (hrtime_t ts /*wall time (not 0-based)*/); + + char *expt_name; // name of experiment + char *arch_name; // <experiment>/archive + char *fndr_arch_name; // <founder_experiment>/archive + //TBR? hrtime_t sample_time; // total of sample durations + int yyparse (); // Allow yyparse actions to access + Vaddr stack_base; // Stack base + + // Write experiment header to comment queue + void write_header (); + void write_coll_params (); + + Exp_status find_expdir (char *directory_name); + + // Invoke the parser to process a file. + void read_data_file (const char*, const char*); + int read_log_file (); + void read_labels_file (); + void read_notes_file (); + void read_archives (); + int read_java_classes_file (); + void read_map_file (); + int read_overview_file (); + int read_dyntext_file (); + void read_omp_file (); + void read_omp_preg (); + void read_omp_task (); + void read_ifreq_file (); + void read_frameinfo_file (); + + // Functions to process the log and loadobjects file entries + // They are deliberately made virtual to overload them + // in er_export. + virtual int process_arglist_cmd (char *, char *); + virtual int process_desc_start_cmd (char *, hrtime_t, char *, char *, int, char *); + virtual int process_desc_started_cmd (char *, hrtime_t, char *, char *, int, char *); + virtual int process_fn_load_cmd (Module *mod, char *fname, Vaddr vaddr, int fsize, hrtime_t ts); + virtual int process_fn_unload_cmd (char *, Vaddr, hrtime_t); + virtual int process_hwcounter_cmd (char *, int, char *, char *, int, int, int, char *); + virtual int process_hwsimctr_cmd (char *, int, char *, char *, char*, int, int, int, int, int); + virtual int process_jcm_load_cmd (char*, Vaddr, Vaddr, int, hrtime_t); + virtual int process_jcm_unload_cmd (char*, Vaddr, hrtime_t); + virtual int process_Linux_kernel_cmd (hrtime_t); + virtual int process_jthr_end_cmd (char *, uint64_t, Vaddr, Vaddr, hrtime_t); + virtual int process_jthr_start_cmd (char *, char *, char *, char *, uint64_t, Vaddr, Vaddr, hrtime_t); + virtual int process_gc_end_cmd (hrtime_t); + virtual int process_gc_start_cmd (hrtime_t); + virtual int process_sample_cmd (char *, hrtime_t, int id, char *lbl); + virtual int process_sample_sig_cmd (char *, int); + virtual int process_seg_map_cmd (char *, hrtime_t, Vaddr, int, int, int64_t, int64_t, int64_t, char *); + virtual int process_seg_unmap_cmd (char *, hrtime_t, Vaddr); + + // creation time for experiment + time_t mtime; + hrtime_t exp_rel_start_time; // start of exp. relative to founder + bool exp_rel_start_time_set; + Vector<UserLabel*> *userLabels; // List of er_labels + int userExpId; // user value for EXPID + int expIdx; // DbeSession exp identifier + PRBTree *jmaps; // JAVA_CLASSES: (id,time)->Histable + Experiment* baseFounder; // outermost experiment (null until lazily computed) + + // Represents a file in experiment + class ExperimentFile; + + // XML handler to parse various experiment files + class ExperimentHandler; + class ExperimentLabelsHandler; + + uint64_t readPacket (Data_window *dwin, Data_window::Span *span); + void readPacket (Data_window *dwin, char *ptr, PacketDescriptor *pDscr, + DataDescriptor *dDscr, int arg, uint64_t pktsz); + + // read data + DataDescriptor *get_profile_events (); + DataDescriptor *get_sync_events (); + DataDescriptor *get_hwc_events (); + DataDescriptor *get_heap_events (); + DataDescriptor *get_heapsz_events (); + DataDescriptor *get_iotrace_events (); + DataDescriptor *get_race_events (); + DataDescriptor *get_deadlock_events (); + DataDescriptor *get_sample_events (); + DataDescriptor *get_gc_events (); + DataDescriptor *getDataDescriptor (int data_id); + DataDescriptor *newDataDescriptor (int data_id, int flags = 0, + DataDescriptor *master_dDscr = NULL); + + // Frame info data structures and methods + struct UIDnode; + struct RawFramePacket; + + Vector<RawFramePacket*>*frmpckts; // frame info data + static int frUidCmp (const void*, const void*); + RawFramePacket *find_frame_packet (uint64_t uid); + + static const int CHUNKSZ = 16384; + long nnodes; + long nchunks; + UIDnode **chunks; + UIDnode **uidHTable; + Vector<UIDnode*> *uidnodes; + bool resolveFrameInfo; + bool discardTiny; + int tiny_threshold; /* optimize away tiny experiments which ran + * for less than specified time (ms): default 0 */ + + static int uidNodeCmp (const void *a, const void *b); + UIDnode *add_uid (Data_window *dwin, uint64_t uid, int size, uint32_t *array, uint64_t link_uid); + UIDnode *add_uid (Data_window *dwin, uint64_t uid, int size, uint64_t *array, uint64_t link_uid); + UIDnode *new_uid_node (uint64_t uid, uint64_t val); + UIDnode *get_uid_node (uint64_t uid, uint64_t val); + UIDnode *get_uid_node (uint64_t uid); + UIDnode *find_uid_node (uint64_t uid); + + ExperimentFile *logFile; + + // Data descriptors + Vector<DataDescriptor*> *dataDscrs; + Vector<PacketDescriptor*> *pcktDscrs; + long blksz; // binary data file block size + + // Processed data packets + DataView *openMPdata; // OMP fork events + + // Map events to OpenMP parallel regions and tasks + Map2D<uint32_t, hrtime_t, uint64_t> *mapPRid; + Map2D<uint32_t, hrtime_t, void*> *mapPReg; + Map2D<uint32_t, hrtime_t, void*> *mapTask; + + // Archive content + Map<const char*, DbeFile *> *archiveMap; + Map<const char*, SourceFile*>*sourcesMap; + + void init (); + void fini (); + void post_process (); + void constructJavaStack (FramePacket *, UIDnode *, Map<uint64_t, uint64_t> *); + void resolve_frame_info (DataDescriptor*); + void cleanup_cstk_ctx_chunk (); + void register_metric (Metric::Type type); + void register_metric (Hwcentry *ctr, const char* aux, const char* username); + + Sample *sample_last_used; + GCEvent *gcevent_last_used; + char *first_sample_label; + Module *get_jclass (const char *className, const char *fileName); + LoadObject *get_j_lo (const char *className, const char *fileName); + + Vector<BaseMetric*> *metrics; + Vector<JThread*> *jthreads; // master list of Java threads + Vector<JThread*> *jthreads_idx; // index in the master list + Vector<GCEvent*> *gcevents; + Vector<UnmapChunk*> *heapUnmapEvents; + Vector<Sample*> *samples; // Array of Sample pointers + + DefaultMap<int64_t, FileData*> *fDataMap; // list of FileData objects using the virtual File descriptor as the key + DefaultMap<int, int64_t> *vFdMap; // list of virtual file descrptors using the file descriptor as the key + + Vector<Vector<Histable*>*> *tagObjs; // tag objects + bool sparse_threads; + + SegMem **smemHTable; // hash table for SegMem's + DbeInstr **instHTable; // hash table for DbeInstr + Map<unsigned long long, JMethod*> *jmidHTable; // hash table for jmid + + // identity of target process + int pid; + int ppid; + int pgrp; + int sid; + + // Map file processing related data + struct MapRecord + { + + enum + { + LOAD, UNLOAD + } kind; + Histable *obj; + Vaddr base; + Size size; + hrtime_t ts; + uint64_t foff; + }; + + void mrec_insert (MapRecord *mrec); + SegMem *update_ts_in_maps (Vaddr addr, hrtime_t ts); + int read_warn_file (); + LoadObject *get_dynfunc_lo (const char *loName); + Function *create_dynfunc (Module *mod, char *fname, int64_t vaddr, int64_t fsize); + char *get_archived_name (const char *fname, bool archiveFile = false); + + Vector<MapRecord*> *mrecs; + +private: + void add_evt_time_to_profile_events (DataDescriptor *dDscr); + DataView *create_heapsz_data_view (DataView *heap_dview); + void compute_heapsz_data_view (DataView *heapsz_dview); +}; + +struct JThread +{ + JThread *next; + char *name; + char *group_name; + char *parent_name; + uint32_t tid; // system thread id + Vaddr jthr; // recorded Java thread id + Vaddr jenv; // recorded JNIEnv id + uint32_t jthr_id; // internal JThread object id + hrtime_t start; + hrtime_t end; + + JThread () + { + name = NULL; + group_name = NULL; + parent_name = NULL; + } + + ~JThread () + { + free (name); + free (group_name); + free (parent_name); + } + bool is_system (); +}; + +struct GCEvent +{ + + GCEvent () + { + id = -1; + } + + ~GCEvent () { } + + hrtime_t start; + hrtime_t end; + int id; +}; + +class ExperimentLoadCancelException +{ +public: + + ExperimentLoadCancelException () { }; + + ~ExperimentLoadCancelException () { }; +}; + + +#endif /* _EEXPERIMENT_H */ diff --git a/gprofng/src/Expression.cc b/gprofng/src/Expression.cc new file mode 100644 index 0000000..49c94a8 --- /dev/null +++ b/gprofng/src/Expression.cc @@ -0,0 +1,1279 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <assert.h> +#include "CallStack.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "DataObject.h" +#include "Exp_Layout.h" +#include "Experiment.h" +#include "Module.h" +#include "LoadObject.h" +#include "Expression.h" +#include "Function.h" +#include "Histable.h" +#include "Sample.h" +#include "Table.h" + +////////////////////////////////////////////////////////// +// class Expression::Context + +static const uint64_t INDXOBJ_EXPGRID_SHIFT = 60; +static const uint64_t INDXOBJ_EXPID_SHIFT = 32; + +Expression::Context::Context (DbeView *_dbev, Experiment *_exp) +{ + dbev = _dbev; + exp = _exp; + dview = NULL; + eventId = 0; +} + +Expression::Context::Context (DbeView *_dbev, Experiment *_exp, + DataView *_dview, long _eventId) +{ + dbev = _dbev; + exp = _exp; + dview = _dview; + eventId = _eventId; +} + +////////////////////////////////////////////////////////// +// class Expression +Expression::Expression (OpCode _op, uint64_t _v) +{ + op = _op; + v = Value (_v); + arg0 = NULL; + arg1 = NULL; +} + +Expression::Expression (OpCode _op, const Expression *_arg0, + const Expression *_arg1) +{ + op = _op; + v = Value (); + arg0 = NULL; + arg1 = NULL; + if (_arg0) + arg0 = _arg0->copy (); + if (_arg1) + arg1 = _arg1->copy (); +} + +Expression::~Expression () +{ + delete arg0; + delete arg1; +} + +Expression::Expression (const Expression &rhs) +{ + op = rhs.op; + arg0 = NULL; + arg1 = NULL; + if (rhs.arg0) + arg0 = rhs.arg0->copy (); + if (rhs.arg1) + arg1 = rhs.arg1->copy (); + v = Value (rhs.v); + fixupValues (); +} + +Expression::Expression (const Expression *rhs) +{ + arg0 = NULL; + arg1 = NULL; + copy (rhs); +} + +void +Expression::copy (const Expression *rhs) +{ + op = rhs->op; + delete arg0; + delete arg1; + arg0 = NULL; + arg1 = NULL; + if (rhs->arg0) + arg0 = rhs->arg0->copy (); + if (rhs->arg1) + arg1 = rhs->arg1->copy (); + v = Value (rhs->v); + fixupValues (); +} + +Expression & +Expression::operator= (const Expression &rhs) +{ + if (this == &rhs) + return *this; + copy (&rhs); + return *this; +} + +void +Expression::fixupValues () +{ + if (v.next) + { + assert (arg0 && v.next == &(arg0->v)); + v.next = &(arg0->v); + } +} + +bool +Expression::getVal (int propId, Context *ctx) +{ + v.val = 0; + v.next = NULL; + int origPropId = propId; + switch (propId) + { + default: + { + if (!ctx->dview) + return false; + PropDescr *propDscr = ctx->dview->getProp (propId); + if (!propDscr) + return false; + switch (propDscr->vtype) + { + case TYPE_INT32: + v.val = ctx->dview->getIntValue (propId, ctx->eventId); + break; + case TYPE_UINT32: + v.val = (uint32_t) ctx->dview->getIntValue (propId, ctx->eventId); //prevent sign extension + break; + case TYPE_INT64: + case TYPE_UINT64: + v.val = ctx->dview->getLongValue (propId, ctx->eventId); + break; + case TYPE_OBJ: + // YM: not sure if we should allow this + v.val = (long long) ctx->dview->getObjValue (propId, ctx->eventId); + break; + case TYPE_STRING: + case TYPE_DOUBLE: + default: + return false; // Weird, programming error? + } + break; + } + case PROP_FREQ_MHZ: + if (ctx->exp && ctx->exp->clock) + v.val = ctx->exp->clock; + else + return false; + break; + case PROP_PID: + if (ctx->exp == NULL) + return false; + v.val = ctx->exp->getPID (); + break; + case PROP_EXPID: + if (ctx->exp == NULL) + return false; + v.val = ctx->exp->getUserExpId (); + break; + case PROP_EXPID_CMP: + if (ctx->exp == NULL) + return false; + else + { + Experiment *exp = ctx->exp; + if (ctx->dbev && ctx->dbev->comparingExperiments ()) + exp = (Experiment *) exp->get_compare_obj (); + v.val = exp->getUserExpId (); + } + break; + case PROP_EXPGRID: + if (ctx->exp == NULL) + return false; + v.val = ctx->exp->groupId; + break; + case PROP_NTICK_USEC: + if (ctx->exp == NULL) + return false; + if (ctx->dview && ctx->dview->getProp (PROP_NTICK)) + v.val = ctx->dview->getIntValue (PROP_NTICK, ctx->eventId) + * ctx->exp->get_params ()->ptimer_usec; + else + return false; + break; + case PROP_ATSTAMP: + case PROP_ETSTAMP: + if (ctx->exp == NULL) + return false; + if (ctx->dview && ctx->dview->getProp (PROP_TSTAMP)) + v.val = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + else + return false; + if (propId == PROP_ATSTAMP) + break; // absolute time, no adjustments + // propId==PROP_ETSTAMP + // calculate relative time from start of this experiment + v.val -= ctx->exp->getStartTime (); + break; + case PROP_TSTAMP: + case PROP_TSTAMP_LO: + case PROP_TSTAMP_HI: + { + if (ctx->exp == NULL) + return false; + if (!(ctx->dview && ctx->dview->getProp (PROP_TSTAMP))) + return false; + hrtime_t tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + // compute relative time from start of founder experiment + v.val = tstamp - ctx->exp->getStartTime () + + ctx->exp->getRelativeStartTime (); + if (propId == PROP_TSTAMP) + break; + if (ctx->dview->getProp (PROP_EVT_TIME)) + { + hrtime_t delta = ctx->dview->getLongValue (PROP_EVT_TIME, ctx->eventId); + if (propId == PROP_TSTAMP_LO) + { + if (delta > 0) + { // positive delta means TSTAMP is at end + // TSTAMP_LO = TSTAMP-delta + v.val -= delta; + break; + } + break; + } + else + { // PROP_TSTAMP_HI + if (delta < 0) + { // negative delta means TSTAMP is at start + // TSTAMP_HI = TSTAMP+(-delta) + v.val -= delta; + break; + } + break; + } + } + else if (ctx->dview->getProp (PROP_TSTAMP2)) + { + if (propId == PROP_TSTAMP_HI) + { + hrtime_t tstamp2 = ctx->dview->getLongValue (PROP_TSTAMP2, + ctx->eventId); + if (tstamp2 == 0) + break; // if not initialized, event does not have duration + if (tstamp2 == MAX_TIME) + tstamp2 = ctx->exp->getLastEvent (); + hrtime_t delta = tstamp2 - tstamp; + if (delta >= 0) + { + v.val += delta; + break; + } + break; // weird, delta should not be negative + } + break; // PROP_TSTAMP_LO, no modification needed + } + break; // should never be hit + } + case PROP_IOHEAPBYTES: + { + propId = PROP_IONBYTE; + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + { // has property? + propId = PROP_HSIZE; + if (!ctx->dview->getProp (propId)) + return false; + } + v.val = ctx->dview->getLongValue (propId, ctx->eventId); + break; + } + case PROP_SAMPLE_MAP: + { + if (ctx->exp == NULL) + return false; + if (ctx->dview == NULL) + return false; + if (ctx->dview->getProp (PROP_SAMPLE)) + v.val = ctx->dview->getIntValue (PROP_SAMPLE, ctx->eventId); + else + { // does not have property, convert to time. + uint64_t tstamp; + tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + Sample *sample = ctx->exp->map_event_to_Sample (tstamp); + v.val = sample ? sample->get_number () : -1; + } + break; + } + case PROP_GCEVENT_MAP: + { + if (ctx->exp == NULL) + return false; + if (ctx->dview == NULL) + return false; + if (ctx->dview->getProp (PROP_GCEVENT)) + v.val = ctx->dview->getIntValue (PROP_GCEVENT, ctx->eventId); + else + { // does not have property, convert to time. + uint64_t tstamp; + tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + GCEvent *gcevent = ctx->exp->map_event_to_GCEvent (tstamp); + v.val = gcevent ? gcevent->id : 0; + } + break; + } + case PROP_LEAF: + { + if (ctx->dview == NULL) + return false; + VMode vmode = ctx->dbev ? ctx->dbev->get_view_mode () : VMODE_USER; + int prop_id; + if (vmode == VMODE_MACHINE) + prop_id = PROP_MSTACK; + else if (vmode == VMODE_EXPERT) + prop_id = PROP_XSTACK; + else + prop_id = PROP_USTACK; + if (!ctx->dview->getProp (prop_id)) + return false; + Histable *obj = CallStack::getStackPC (ctx->dview->getObjValue (prop_id, ctx->eventId), 0); + Function *func = (Function*) obj->convertto (Histable::FUNCTION); + v.val = func->id; // LEAF + break; + } + case PROP_STACKID: + { + VMode vmode = ctx->dbev ? ctx->dbev->get_view_mode () : VMODE_USER; + if (vmode == VMODE_MACHINE) + propId = PROP_MSTACK; + else if (vmode == VMODE_EXPERT) + propId = PROP_XSTACK; + else + propId = PROP_USTACK; + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + return false; + v.val = (long) ctx->dview->getObjValue (propId, ctx->eventId); + break; + } + case PROP_STACKL: + case PROP_STACKI: + case PROP_STACK: + { + VMode vmode = ctx->dbev ? ctx->dbev->get_view_mode () : VMODE_USER; + if (vmode == VMODE_MACHINE) + propId = PROP_MSTACK; + else if (vmode == VMODE_EXPERT) + propId = PROP_XSTACK; + else + propId = PROP_USTACK; + } + // no break; + case PROP_MSTACKL: + case PROP_XSTACKL: + case PROP_USTACKL: + case PROP_MSTACKI: + case PROP_XSTACKI: + case PROP_USTACKI: + switch (propId) + { + case PROP_MSTACKL: + case PROP_MSTACKI: + propId = PROP_MSTACK; + break; + case PROP_XSTACKL: + case PROP_XSTACKI: + propId = PROP_XSTACK; + break; + case PROP_USTACKL: + case PROP_USTACKI: + propId = PROP_USTACK; + break; + default: + break; + } + // no break; + case PROP_MSTACK: + case PROP_XSTACK: + case PROP_USTACK: + { + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + return false; + bool hide_mode = !ctx->dbev->isShowAll () + || ctx->dbev->isFilterHideMode (); + Expression *cur = this; + for (CallStackNode *stack = (CallStackNode *) + ctx->dview->getObjValue (propId, ctx->eventId); + stack; stack = stack->get_ancestor ()) + { + Histable *hist = stack->get_instr (); + if (origPropId == PROP_STACK || origPropId == PROP_MSTACK + || origPropId == PROP_XSTACK || origPropId == PROP_USTACK) + { + cur->v.val = hist->convertto (Histable::FUNCTION)->id; + cur->v.fn = cur->v.val; + } + else if (origPropId == PROP_STACKL || origPropId == PROP_MSTACKL + || origPropId == PROP_XSTACKL || origPropId == PROP_USTACKL) + { + cur->v.val = hist->convertto (Histable::LINE)->id; + if (hide_mode) + cur->v.fn = hist->convertto (Histable::FUNCTION)->id; + else + cur->v.fn = 0; + } + else if (origPropId == PROP_STACKI || origPropId == PROP_MSTACKI + || origPropId == PROP_XSTACKI || origPropId == PROP_USTACKI) + { + cur->v.val = hist->convertto (Histable::INSTR)->id; + if (hide_mode) + cur->v.fn = hist->convertto (Histable::FUNCTION)->id; + else + cur->v.fn = 0; + } + if (cur->arg1 == NULL) + cur->arg1 = new Expression (OP_NONE, (uint64_t) 0); + if (stack->get_ancestor () == NULL) + { + if (origPropId == PROP_STACKL || origPropId == PROP_MSTACKL + || origPropId == PROP_XSTACKL || origPropId == PROP_USTACKL + || origPropId == PROP_STACKI || origPropId == PROP_MSTACKI + || origPropId == PROP_XSTACKI || origPropId == PROP_USTACKI) + { + cur->v.next = NULL; + continue; + } + } + cur->v.next = &cur->arg1->v; + cur = cur->arg1; + } + if (origPropId == PROP_STACK || origPropId == PROP_MSTACK + || origPropId == PROP_XSTACK || origPropId == PROP_USTACK) + { + cur->v.val = dbeSession->get_Total_Function ()->id; + cur->v.fn = cur->v.val; + cur->v.next = NULL; + } + break; + } + case PROP_DOBJ: + { + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (PROP_DOBJ)) + return false; + DataObject *dobj = (DataObject*) + ctx->dview->getObjValue (PROP_DOBJ, ctx->eventId); + if (dobj != NULL) + { + Expression *cur = this; + for (;;) + { + cur->v.val = dobj->id; + dobj = dobj->parent; + if (dobj == NULL) + break; + if (cur->arg1 == NULL) + cur->arg1 = new Expression (OP_NONE, (uint64_t) 0); + cur->v.next = &cur->arg1->v; + cur = cur->arg1; + } + cur->v.next = NULL; + } + break; + } + case PROP_CPRID: + case PROP_TSKID: + { + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + return false; + CallStackNode *ompstack = (CallStackNode *) + ctx->dview->getObjValue (propId, ctx->eventId); + Histable *hobj = ompstack->get_instr (); + if (hobj != NULL) + v.val = hobj->id; + break; + } + case PROP_JTHREAD: + { + if (ctx->exp == NULL) + return false; + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + return false; + uint64_t tstamp; + tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + uint32_t thrid; + uint64_t jthr_id = 0; + thrid = ctx->dview->getIntValue (PROP_THRID, ctx->eventId); + JThread *jthread = ctx->exp->map_pckt_to_Jthread (thrid, tstamp); + if (jthread != JTHREAD_NONE && jthread != JTHREAD_DEFAULT) + { + jthr_id = jthread->jthr_id; + uint64_t grid = ctx->exp->groupId; + uint64_t expid = ctx->exp->getUserExpId (); + v.val = (grid << INDXOBJ_EXPGRID_SHIFT) | + (expid << INDXOBJ_EXPID_SHIFT) | jthr_id; + } + break; + } + } + return true; +} + +bool +Expression::bEval (Context *ctx) +{ + uint64_t v0, v1; + switch (op) + { + case OP_DEG: + if (!arg1->bEval (ctx)) + return false; + if (arg1->v.val < 0) + { + v.val = 0; + return true; + } + if (!arg0->bEval (ctx)) + { + return false; + } + v0 = arg0->v.val; + v1 = arg1->v.val; + for (v.val = 1; v1 > 0; v1--) + v.val *= v0; + return true; + case OP_MUL: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val * arg1->v.val; + return true; + } + return false; + case OP_DIV: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v1 = arg1->v.val; + v.val = (v1 == 0) ? 0 : (arg0->v.val / v1); + return true; + } + return false; + case OP_REM: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v1 = arg1->v.val; + v.val = (v1 == 0) ? 0 : (arg0->v.val % v1); + return true; + } + return false; + case OP_ADD: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val + arg1->v.val; + // DBFIXME LIBRARY VISIBILITY + // hack to pass v.fn value to new expression for leaf filters USTACK+0 + v.fn = arg0->v.fn + arg1->v.fn; + return true; + } + return false; + case OP_MINUS: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val - arg1->v.val; + return true; + } + return false; + case OP_LS: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val << arg1->v.val; + return true; + } + return false; + case OP_RS: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val >> arg1->v.val; + return true; + } + return false; + case OP_LT: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val < arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_LE: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val <= arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_GT: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val > arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_GE: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val >= arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_EQ: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val == arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_NE: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val != arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_BITAND: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val & arg1->v.val; + return true; + } + return false; + case OP_BITXOR: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val ^ arg1->v.val; + return true; + } + return false; + case OP_BITOR: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val | arg1->v.val; + return true; + } + return false; + case OP_AND: + if (arg0->bEval (ctx)) + { + if (arg0->v.val == 0) + { + v.val = 0; + return true; + } + if (arg1->bEval (ctx)) + { + v.val = arg1->v.val == 0 ? 0 : 1; + return true; + } + return false; + } + if (arg1->bEval (ctx) && arg1->v.val == 0) + { + v.val = 0; + return true; + } + return false; + case OP_OR: + if (arg0->bEval (ctx)) + { + if (arg0->v.val != 0) + { + v.val = 1; + return true; + } + if (arg1->bEval (ctx)) + { + v.val = arg1->v.val == 0 ? 0 : 1; + return true; + } + return false; + } + if (arg1->bEval (ctx) && arg1->v.val != 0) + { + v.val = 1; + return true; + } + return false; + case OP_NEQV: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v0 = arg0->v.val; + v1 = arg1->v.val; + v.val = (v0 == 0 && v1 != 0) || (v0 != 0 && v1 == 0) ? 1 : 0; + return true; + } + return false; + case OP_EQV: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v0 = arg0->v.val; + v1 = arg1->v.val; + v.val = (v0 == 0 && v1 == 0) || (v0 != 0 && v1 != 0) ? 1 : 0; + return true; + } + return false; + case OP_QWE: + if (arg0->bEval (ctx)) + { + if (arg0->v.val != 0) + { + if (arg1->arg0->bEval (ctx)) + { + v.val = arg1->arg0->v.val; + return true; + } + } + else + { + if (arg1->arg1->bEval (ctx)) + { + v.val = arg1->arg1->v.val; + return true; + } + } + } + return false; + case OP_COMMA: + if (arg0->bEval (ctx)) + { + v.next = &arg0->v; + if (arg1->bEval (ctx)) + { + v.val = arg1->v.val; + return true; + } + } + return false; + case OP_IN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *s = &arg0->v; s; s = s->next) + { + bool found = false; + for (Value *t = &arg1->v; t; t = t->next) + { + if (t->val == s->val) + { + found = true; + break; + } + } + if (!found) + { + v.val = 0; + return true; + } + } + v.val = 1; + return true; + } + return false; + case OP_SOMEIN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *s = &arg0->v; s; s = s->next) + { + for (Value *t = &arg1->v; t; t = t->next) + { + if (t->val == s->val) + { + v.val = 1; + return true; + } + } + } + v.val = 0; + return true; + } + return false; + case OP_ORDRIN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *t0 = &arg1->v; t0; t0 = t0->next) + { + bool found = true; + for (Value *s = &arg0->v, *t = t0; s; s = s->next, t = t->next) + { + if (t == NULL || t->val != s->val) + { + found = false; + break; + } + } + if (found) + { + v.val = 1; + return true; + } + } + v.val = 0; + return true; + } + return false; + // LIBRARY_VISIBILITY + case OP_LIBRARY_IN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *s = &arg0->v; s; s = s->next) + { + bool found = false; + uint64_t objId = s->val; + Histable *obj = dbeSession->findObjectById (objId); + bool libraryFound = false; + Function *fn; + if (obj != NULL && obj->get_type () == Histable::FUNCTION) + { + fn = (Function *) obj; + if (fn->isHideFunc) + // this belongss to a loadobject in hide/library mode + libraryFound = true; + } + + if (libraryFound) + { + uint64_t lo_id = fn->module->loadobject->id; + for (Value *t = &arg1->v; t; t = t->next) + { + uint64_t t_id = t->fn; + Histable *obj2 = dbeSession->findObjectById (t_id); + if (obj2 != NULL + && obj2->get_type () == Histable::FUNCTION) + { + Function *func2 = (Function *) obj2; + uint64_t lo_id2 = func2->module->loadobject->id; + if (lo_id2 == lo_id) + { + found = true; + break; + } + } + } + } + else + { + // Not a loadobject + for (Value *t = &arg1->v; t; t = t->next) + { + if (t->val == s->val) + { + found = true; + break; + } + } + } + if (!found) + { + v.val = 0; + return true; + } + } + v.val = 1; + return true; + } + return false; + case OP_LIBRARY_SOMEIN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *s = &arg0->v; s; s = s->next) + { + uint64_t objId = s->val; + Histable *obj = dbeSession->findObjectById (objId); + bool libraryFound = false; + Function *fn; + if (obj != NULL && obj->get_type () == Histable::FUNCTION) + { + fn = (Function *) obj; + if (fn->isHideFunc) + // this belongs to a loadobject in hide/library mode + libraryFound = true; + } + + if (libraryFound) + { + uint64_t lo_id = fn->module->loadobject->id; + for (Value *t = &arg1->v; t; t = t->next) + { + uint64_t t_id = t->fn; + Histable *obj2 = dbeSession->findObjectById (t_id); + if (obj2 != NULL && obj2->get_type () == Histable::FUNCTION) + { + Function *func2 = (Function *) obj2; + uint64_t lo_id2 = func2->module->loadobject->id; + if (lo_id2 == lo_id) + { + v.val = 1; + return true; + } + } + } + } + else + { + for (Value *t = &arg1->v; t; t = t->next) + if (t->val == s->val) + { + v.val = 1; + return true; + } + } + } + v.val = 0; + return true; + } + return false; + case OP_LIBRARY_ORDRIN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *t0 = &arg1->v; t0; t0 = t0->next) + { + bool found = true; + Value *t = t0; + for (Value *s = &arg0->v; s; s = s->next) + { + // start comparing s->val with t->val + // if matches move on to s->next and t->next + uint64_t objId = s->val; + Histable *obj = dbeSession->findObjectById (objId); + bool libraryFound = false; + Function *fn; + if (obj != NULL && obj->get_type () == Histable::FUNCTION) + { + fn = (Function *) obj; + if (fn->isHideFunc) + libraryFound = true; + } + if (libraryFound) + { + // s->val is from a loadobject + // check if t->val is a func whose loadobject matches s->val + uint64_t lo_id = fn->module->loadobject->id; + uint64_t t_id = t->fn; + Histable *obj2 = dbeSession->findObjectById (t_id); + if (obj2 != NULL + && obj2->get_type () == Histable::FUNCTION) + { + Function *func2 = (Function *) obj2; + uint64_t lo_id2 = func2->module->loadobject->id; + if (lo_id2 != lo_id) + { + // no match + found = false; + break; + } + else + { + // t->val is a func whose loadobject matches s->val + while (t != NULL && lo_id2 == lo_id) + { + // skip frames with same load object + t = t->next; + t_id = t->fn; + obj2 = dbeSession->findObjectById (t_id); + if (obj2 != NULL + && obj2->get_type () == Histable::FUNCTION) + { + func2 = (Function *) obj2; + lo_id2 = func2->module->loadobject->id; + } + } + } + } + } + else + { + if (t == NULL || t->val != s->val) + { + found = false; + break; + } + t = t->next; + } + } + if (found) + { + v.val = 1; + return true; + } + } + v.val = 0; + return true; + } + return false; + case OP_BITNOT: + if (arg0->bEval (ctx)) + { + v.val = ~arg0->v.val; + return true; + } + return false; + case OP_NOT: + if (arg0->bEval (ctx)) + { + v.val = !arg0->v.val; + return true; + } + return false; + case OP_NUM: + return true; + case OP_NAME: + if (ctx && arg0->bEval (ctx) && getVal ((int) arg0->v.val, ctx)) + return true; + return false; + case OP_FUNC: + // FNAME is completely processed by pEval for now + v.val = 0; + return true; + case OP_HASPROP: + if (!ctx || !ctx->dview) + return false; // can't be resolved (occurs during pEval() ) + else if (arg0->op != OP_NAME || !arg0->arg0) + return false; // weird, wrong arg type + else + { + int propId = (int) arg0->arg0->v.val; + if (ctx->dview->getProp (propId)) + v.val = 1; + else + v.val = 0; + return true; + } + case OP_FILE: + // FILENAME is completely processed by pEval for now + v.val = 0; + return true; + case OP_JAVA: + //JGROUP & JPARENT is completely processed by pEval for now + v.val = 0; + return true; + case OP_COLON: + return false; // OK for arg1 of OP_QWE + default: +#ifdef IPC_LOG + fprintf (stderr, "INTERNAL ERROR: Expression::eval op=%d\n", op); +#endif + return false; + } + return false; +} + +Expression * +Expression::pEval (Context *ctx) // partial evaluation (dview may be NULL) +{ + Expression *res = NULL; + switch (op) + { + case OP_FUNC: + { + Vector<Histable*> *objs = NULL; + if (arg0->v.val == FUNC_FNAME) + { + Histable::NameFormat nfmt = ctx ? ctx->dbev->get_name_format () : Histable::NA; + objs = (Vector<Histable*>*)dbeSession->match_func_names ((char*) arg1->v.val, nfmt); + } + else if (arg0->v.val == FUNC_DNAME) + objs = (Vector<Histable*>*)dbeSession->match_dobj_names ((char*) arg1->v.val); + Expression *cur = new Expression (Expression::OP_NUM, (uint64_t) 0); + res = cur; + int i = objs ? objs->size () - 1 : -1; + for (; i >= 0; i--) + { + cur->v.val = objs->fetch (i)->id; + if (i == 0) + break; + cur->arg0 = new Expression (OP_NONE, (uint64_t) 0); + cur->v.next = &cur->arg0->v; + cur = cur->arg0; + } + cur->v.next = NULL; + if (objs) + delete objs; + break; + } + case OP_JAVA: + { + Vector<JThread*> *objs = NULL; + Vector<uint64_t> *grids = NULL; + Vector<uint64_t> *expids = NULL; + if (arg0->v.val == JAVA_JGROUP) + objs = dbeSession->match_java_threads ((char*) arg1->v.val, 0, grids, + expids); + else if (arg0->v.val == JAVA_JPARENT) + objs = dbeSession->match_java_threads ((char*) arg1->v.val, 1, grids, + expids); + Expression *cur = new Expression (Expression::OP_NUM, (uint64_t) 0); + res = cur; + int i = objs ? objs->size () - 1 : -1; + for (; i >= 0; i--) + { + uint64_t jthr_id = 0; + JThread *jthread = (JThread *) (objs->fetch (i)); + jthr_id = jthread->jthr_id; + uint64_t grid = grids->fetch (i); + uint64_t expid = expids->fetch (i); + cur->v.val = (grid << INDXOBJ_EXPGRID_SHIFT) | + (expid << INDXOBJ_EXPID_SHIFT) | jthr_id; + if (i == 0) + break; + cur->arg0 = new Expression (OP_NONE, (uint64_t) 0); + cur->v.next = &cur->arg0->v; + cur = cur->arg0; + } + cur->v.next = NULL; + delete objs; + delete grids; + delete expids; + break; + } + case OP_FILE: + { + Vector<Histable*> *objs = NULL; + Histable::NameFormat nfmt = ctx ? ctx->dbev->get_name_format () : Histable::NA; + if (ctx) + objs = (Vector<Histable*>*)dbeSession->match_file_names ((char*) arg1->v.val, nfmt); + Expression *cur = new Expression (Expression::OP_NUM, (uint64_t) 0); + res = cur; + int i = objs ? objs->size () - 1 : -1; + for (; i >= 0; i--) + { + cur->v.val = objs->fetch (i)->id; + if (i == 0) + break; + cur->arg0 = new Expression (OP_NONE, (uint64_t) 0); + cur->v.next = &cur->arg0->v; + cur = cur->arg0; + } + cur->v.next = NULL; + if (objs) + delete objs; + break; + } + case OP_NUM: + case OP_COMMA: + res = copy (); + break; + case OP_IN: + case OP_SOMEIN: + case OP_ORDRIN: + { + // LIBRARY_VISIBILITY: + // Evaluate the arg0 of OP_IN, OP_SOMEIN, OP_ORDRIN to see if it has any library/loadobject + // Change it to OP_LIBRARY_IN, OP_LIBRARY_SOMEIN or OP_LIBRARY_ORDRIN respectively + if (dbeSession->is_lib_visibility_used () && (arg0->hasLoadObject () + || arg1->hasLoadObject ())) + { + OpCode new_op; + switch (op) + { + case OP_IN: + new_op = OP_LIBRARY_IN; + break; + case OP_SOMEIN: + new_op = OP_LIBRARY_SOMEIN; + break; + case OP_ORDRIN: + new_op = OP_LIBRARY_ORDRIN; + break; + default: + new_op = op; // Should never reach here + break; + } + if (arg1->hasLoadObject ()) + res = new Expression (new_op, arg1 ? arg1->pEval (ctx) : NULL, + arg0 ? arg0->pEval (ctx) : NULL); + else + res = new Expression (new_op, arg0 ? arg0->pEval (ctx) : NULL, + arg1 ? arg1->pEval (ctx) : NULL); + res->v = v; + ctx->dbev->setFilterHideMode (); + return res; + } + } + // no break; if no loadobjects found fall thru to the default case + default: + if (bEval (ctx)) + { + res = new Expression (OP_NUM, v.val); + break; + } + res = new Expression (op, arg0 ? arg0->pEval (ctx) : NULL, + arg1 ? arg1->pEval (ctx) : NULL); + res->v = v; + break; + } + return res; +} + +bool +Expression::verifyObjectInExpr (Histable *obj) +{ + uint64_t id = ((uint64_t) obj->id); + if (op == OP_NUM && v.val == id) + return true; + bool inArg0 = false; + bool inArg1 = false; + if (arg0 != NULL) + inArg0 = arg0->verifyObjectInExpr (obj); + if (inArg0) + return true; + if (arg1 != NULL) + inArg1 = arg1->verifyObjectInExpr (obj); + if (inArg1) + return true; + return false; +} + +bool +Expression::hasLoadObject () +{ + if (op == OP_NUM) + { + uint64_t id = v.val; + Histable *obj = dbeSession->findObjectById (id); + if (obj != NULL && obj->get_type () == Histable::FUNCTION) + { + Function *func = (Function *) obj; + if (func->isHideFunc) + return true; + } + } + if (arg0 && arg0->hasLoadObject ()) + return true; + if (arg1 && arg1->hasLoadObject ()) + return true; + return false; +} diff --git a/gprofng/src/Expression.h b/gprofng/src/Expression.h new file mode 100644 index 0000000..7542853 --- /dev/null +++ b/gprofng/src/Expression.h @@ -0,0 +1,180 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _EXPRESSION_H +#define _EXPRESSION_H + +#include <inttypes.h> + +class Experiment; +class DataView; +class DbeView; +class Histable; + +class Expression +{ +public: + + class Context + { + public: + Context (DbeView *_dbev, Experiment *_exp = 0); + Context (DbeView *_dbev, Experiment *_exp, DataView *_dview, long _eventId); + + ~Context () { }; + + void + put (DataView *d, long id) + { + dview = d; + eventId = id; + }; + + void + put (Experiment *_exp) + { + exp = _exp; + }; + + Experiment *exp; + DataView *dview; + DbeView *dbev; + long eventId; + }; + + enum OpCode + { + OP_NONE, + OP_QWE, + OP_COLON, + OP_OR, + OP_AND, + OP_NOT, + OP_EQV, + OP_NEQV, + OP_BITOR, + OP_BITAND, + OP_BITXOR, + OP_BITNOT, + OP_EQ, + OP_NE, + OP_LT, + OP_GT, + OP_LE, + OP_GE, + OP_LS, + OP_RS, + OP_ADD, + OP_MINUS, + OP_MUL, + OP_DIV, + OP_REM, + OP_DEG, + OP_COMMA, + OP_IN, + OP_SOMEIN, + OP_ORDRIN, + OP_NUM, + OP_NAME, + OP_FUNC, + OP_FILE, + OP_JAVA, + OP_HASPROP, + OP_LIBRARY_IN, + OP_LIBRARY_SOMEIN, + OP_LIBRARY_ORDRIN + }; + + enum FuncCode + { + FUNC_FNAME, + FUNC_DNAME + }; + + enum JavaCode + { + JAVA_JGROUP, + JAVA_JPARENT + }; + + Expression (OpCode, const Expression*, const Expression* = 0); + Expression (OpCode, uint64_t); + Expression (const Expression &rhs); + Expression (const Expression *rhs); + Expression &operator= (const Expression &rhs); + ~Expression (); + + Expression * + copy () const + { + return new Expression (this); + } + void copy (const Expression *rhs); + + uint64_t + eval (Context *ctx) + { + return bEval (ctx) ? v.val : 0; + }; + + bool + passes (Context *ctx) + { + return bEval (ctx) ? v.val != 0 : true; + }; + + bool + complete () + { + return op == OP_NUM; + }; + + bool verifyObjectInExpr (Histable *obj); + Expression * + pEval (Context *ctx); // Partial evaluation to simplify expression + +private: + + struct Value + { + + Value (uint64_t _val = 0, Value *_next = 0) : val (_val), next (_next) + { + fn = 0; + } + uint64_t val; + uint64_t fn; + Value *next; + }; + + bool getVal (int propId, Context *ctx); + bool bEval (Context *ctx); + + bool hasLoadObject (); + void fixupValues (); + + OpCode op; + Value v; + Expression *arg0; + Expression *arg1; +}; + + +#endif /* _EXPRESSION_H */ diff --git a/gprofng/src/FileData.cc b/gprofng/src/FileData.cc new file mode 100644 index 0000000..7a941f5 --- /dev/null +++ b/gprofng/src/FileData.cc @@ -0,0 +1,400 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <assert.h> +#include <string.h> + +#include "util.h" +#include "FileData.h" + +void +FileData::init () +{ + readTime = 0; + writeTime = 0; + otherTime = 0; + errorTime = 0; + readBytes = 0; + writeBytes = 0; + readCnt = 0; + writeCnt = 0; + otherCnt = 0; + errorCnt = 0; + wSlowestBytes = 0; + wSmallestBytes = _10TB; + wLargestBytes = 0; + w0KB1KBCnt = 0; + w1KB8KBCnt = 0; + w8KB32KBCnt = 0; + w32KB128KBCnt = 0; + w128KB256KBCnt = 0; + w256KB512KBCnt = 0; + w512KB1000KBCnt = 0; + w1000KB10MBCnt = 0; + w10MB100MBCnt = 0; + w100MB1GBCnt = 0; + w1GB10GBCnt = 0; + w10GB100GBCnt = 0; + w100GB1TBCnt = 0; + w1TB10TBCnt = 0; + rSlowestBytes = 0; + rSmallestBytes = _10TB; + rLargestBytes = 0; + r0KB1KBCnt = 0; + r1KB8KBCnt = 0; + r8KB32KBCnt = 0; + r32KB128KBCnt = 0; + r128KB256KBCnt = 0; + r256KB512KBCnt = 0; + r512KB1000KBCnt = 0; + r1000KB10MBCnt = 0; + r10MB100MBCnt = 0; + r100MB1GBCnt = 0; + r1GB10GBCnt = 0; + r10GB100GBCnt = 0; + r100GB1TBCnt = 0; + r1TB10TBCnt = 0; +} + +FileData::FileData (const char *fName) +{ + fileName = dbe_strdup (fName); + fileDesList = new Vector<int>; + virtualFds = new Vector<int64_t>; + virtualFd = -1; + fileDes = -1; + fsType[0] = '\0'; + histType = Histable::IOACTVFD; + init (); +} + +FileData::FileData (FileData *fData) +{ + fileName = dbe_strdup (fData->fileName); + fileDesList = new Vector<int>; + Vector<int> *fdList = fData->fileDesList; + int fd; + if (fdList != NULL) + for (int i = 0; i < fdList->size (); i++) + if ((fd = fdList->fetch (i)) == -1) + fileDesList->append (fd); + + virtualFds = new Vector<int64_t>; + Vector<int64_t> *vfds = fData->virtualFds; + int64_t vfd; + if (vfds != NULL) + for (int i = 0; i < vfds->size (); i++) + if ((vfd = vfds->fetch (i)) == -1) + virtualFds->append (vfd); + virtualFd = fData->virtualFd; + fileDes = fData->fileDes; + histType = fData->histType; + + for (int i = 0; i < FSTYPESZ; i++) + fsType[i] = fData->fsType[i]; + + readTime = fData->readTime; + writeTime = fData->writeTime; + otherTime = fData->otherTime; + errorTime = fData->errorTime; + readBytes = fData->readBytes; + writeBytes = fData->writeBytes; + readCnt = fData->readCnt; + writeCnt = fData->writeCnt; + otherCnt = fData->otherCnt; + errorCnt = fData->errorCnt; + wSlowestBytes = fData->wSlowestBytes; + wSmallestBytes = fData->wSmallestBytes; + wLargestBytes = fData->wLargestBytes; + w0KB1KBCnt = fData->w0KB1KBCnt; + w1KB8KBCnt = fData->w1KB8KBCnt; + w8KB32KBCnt = fData->w8KB32KBCnt; + w32KB128KBCnt = fData->w32KB128KBCnt; + w128KB256KBCnt = fData->w128KB256KBCnt; + w256KB512KBCnt = fData->w256KB512KBCnt; + w512KB1000KBCnt = fData->w512KB1000KBCnt; + w1000KB10MBCnt = fData->w1000KB10MBCnt; + w10MB100MBCnt = fData->w10MB100MBCnt; + w100MB1GBCnt = fData->w100MB1GBCnt; + w1GB10GBCnt = fData->w1GB10GBCnt; + w10GB100GBCnt = fData->w10GB100GBCnt; + w100GB1TBCnt = fData->w100GB1TBCnt; + w1TB10TBCnt = fData->w1TB10TBCnt; + rSlowestBytes = fData->rSlowestBytes; + rSmallestBytes = fData->rSmallestBytes; + rLargestBytes = fData->rLargestBytes; + r0KB1KBCnt = fData->r0KB1KBCnt; + r1KB8KBCnt = fData->r1KB8KBCnt; + r8KB32KBCnt = fData->r8KB32KBCnt; + r32KB128KBCnt = fData->r32KB128KBCnt; + r128KB256KBCnt = fData->r128KB256KBCnt; + r256KB512KBCnt = fData->r256KB512KBCnt; + r512KB1000KBCnt = fData->r512KB1000KBCnt; + r1000KB10MBCnt = fData->r1000KB10MBCnt; + r10MB100MBCnt = fData->r10MB100MBCnt; + r100MB1GBCnt = fData->r100MB1GBCnt; + r1GB10GBCnt = fData->r1GB10GBCnt; + r10GB100GBCnt = fData->r10GB100GBCnt; + r100GB1TBCnt = fData->r100GB1TBCnt; + r1TB10TBCnt = fData->r1TB10TBCnt; +} + +FileData::~FileData () +{ + free (fileName); + delete fileDesList; + delete virtualFds; +} + +void +FileData::setVirtualFds (int64_t vfd) +{ + for (int i = 0; i < virtualFds->size (); i++) + if (vfd == virtualFds->fetch (i)) + return; + virtualFds->append (vfd); +} + +void +FileData::setFileDesList (int fd) +{ + for (int i = 0; i < fileDesList->size (); i++) + if (fd == fileDesList->fetch (i)) + return; + fileDesList->append (fd); +} + +void +FileData::setFsType (const char* fst) +{ + size_t len = strlen (fst); + if (len > 0 && len < FSTYPESZ) + snprintf (fsType, sizeof (fsType), NTXT ("%s"), fst); + else + snprintf (fsType, sizeof (fsType), GTXT ("error")); +} + +Histable* +FileData::convertto (Histable_type type, Histable*) +{ + return (type == histType ? this : NULL); +} + +char* +FileData::get_name (Histable::NameFormat /*_nfmt*/) +{ + if (histType == Histable::IOACTVFD) + { + if (!streq (fileName, NTXT ("<Total>"))) + { + if (fileDes >= 0) + return dbe_sprintf (GTXT ("%s (IOVFD=%lld, FD=%d)"), fileName, + (long long) virtualFd, (int) fileDes); + return dbe_sprintf (GTXT ("%s (IOVFD=%lld)"), fileName, + (long long) virtualFd); + } + else + return fileName; + } + else if (histType == Histable::IOACTFILE) + { + if (!streq (fileName, NTXT ("<Total>"))) + { + if (!streq (fsType, NTXT ("N/A"))) + return dbe_sprintf (GTXT ("%s (FS=%s)"), fileName, fsType); + return fileName; + } + return fileName; + } + return fileName; +} + +char* +FileData::get_raw_name (Histable::NameFormat /*_nfmt*/) +{ + return fileName; +} + +void +FileData::setFsType (FileSystem_type fst) +{ + const char *fsName; + switch (fst) + { + case ZFS_TYPE: + fsName = "zfs"; + break; + case NFS_TYPE: + fsName = "nfs"; + break; + case UFS_TYPE: + fsName = "ufs"; + break; + case UDFS_TYPE: + fsName = "udfs"; + break; + case LOFS_TYPE: + fsName = "lofs"; + break; + case VXFS_TYPE: + fsName = "vxfs"; + break; + case TMPFS_TYPE: + fsName = "tmpfs"; + break; + case PCFS_TYPE: + fsName = "pcfs"; + break; + case HSFS_TYPE: + fsName = "hsfs"; + break; + case PROCFS_TYPE: + fsName = "procfs"; + break; + case FIFOFS_TYPE: + fsName = "fifofs"; + break; + case SWAPFS_TYPE: + fsName = "swapfs"; + break; + case CACHEFS_TYPE: + fsName = "cachefs"; + break; + case AUTOFS_TYPE: + fsName = "autofs"; + break; + case SPECFS_TYPE: + fsName = "specfs"; + break; + case SOCKFS_TYPE: + fsName = "sockfs"; + break; + case FDFS_TYPE: + fsName = "fdfs"; + break; + case MNTFS_TYPE: + fsName = "mntfs"; + break; + case NAMEFS_TYPE: + fsName = "namefs"; + break; + case OBJFS_TYPE: + fsName = "objfs"; + break; + case SHAREFS_TYPE: + fsName = "sharefs"; + break; + case EXT2FS_TYPE: + fsName = "ext2"; + break; + case EXT3FS_TYPE: + fsName = "ext3"; + break; + case EXT4FS_TYPE: + fsName = "ext4"; + break; + case UNKNOWNFS_TYPE: + fsName = "N/A"; + break; + default: + fsName = "N/A"; + break; + } + setFsType (fsName); +} + +void +FileData::setWriteStat (hrtime_t wt, int64_t nb) +{ + if (wSlowestBytes < wt) + wSlowestBytes = wt; + if (nb != 0 && wSmallestBytes > nb) + wSmallestBytes = nb; + if (wLargestBytes < nb) + wLargestBytes = nb; + if (nb >= 0 && nb <= _1KB) + w0KB1KBCnt++; + else if (nb <= _8KB) + w1KB8KBCnt++; + else if (nb <= _32KB) + w8KB32KBCnt++; + else if (nb <= _128KB) + w32KB128KBCnt++; + else if (nb <= _256KB) + w128KB256KBCnt++; + else if (nb <= _512KB) + w256KB512KBCnt++; + else if (nb <= _1000KB) + w512KB1000KBCnt++; + else if (nb <= _10MB) + w1000KB10MBCnt++; + else if (nb <= _100MB) + w10MB100MBCnt++; + else if (nb <= _1GB) + w100MB1GBCnt++; + else if (nb <= _10GB) + w1GB10GBCnt++; + else if (nb <= _100GB) + w10GB100GBCnt++; + else if (nb <= _1TB) + w100GB1TBCnt++; + else if (nb <= _10TB) + w1TB10TBCnt++; +} + +void +FileData::setReadStat (hrtime_t rt, int64_t nb) +{ + if (rSlowestBytes < rt) + rSlowestBytes = rt; + if (nb != 0 && rSmallestBytes > nb) + rSmallestBytes = nb; + if (rLargestBytes < nb) + rLargestBytes = nb; + if (nb >= 0 && nb <= _1KB) + r0KB1KBCnt++; + else if (nb <= _8KB) + r1KB8KBCnt++; + else if (nb <= _32KB) + r8KB32KBCnt++; + else if (nb <= _128KB) + r32KB128KBCnt++; + else if (nb <= _256KB) + r128KB256KBCnt++; + else if (nb <= _512KB) + r256KB512KBCnt++; + else if (nb <= _1000KB) + r512KB1000KBCnt++; + else if (nb <= _10MB) + r1000KB10MBCnt++; + else if (nb <= _100MB) + r10MB100MBCnt++; + else if (nb <= _1GB) + r100MB1GBCnt++; + else if (nb <= _10GB) + r1GB10GBCnt++; + else if (nb <= _100GB) + r10GB100GBCnt++; + else if (nb <= _1TB) + r100GB1TBCnt++; + else if (nb <= _10TB) + r1TB10TBCnt++; +} diff --git a/gprofng/src/FileData.h b/gprofng/src/FileData.h new file mode 100644 index 0000000..67de689 --- /dev/null +++ b/gprofng/src/FileData.h @@ -0,0 +1,522 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _FILEDATA_H +#define _FILEDATA_H + +#include "gp-defs.h" +#include "gp-time.h" + +#include "vec.h" +#include "data_pckts.h" +#include "Histable.h" + +#define FSTYPESZ 16 + +#define VIRTUAL_FD_TOTAL 0 +#define VIRTUAL_FD_STDIN 1 +#define VIRTUAL_FD_STDOUT 2 +#define VIRTUAL_FD_STDERR 3 +#define VIRTUAL_FD_OTHERIO 4 +#define VIRTUAL_FD_NONE -1 + +#define STDIN_FD 0 +#define STDOUT_FD 1 +#define STDERR_FD 2 +#define OTHERIO_FD -1 + +#define OTHERIO_FILENAME "<Other IO activity>" +#define STDIN_FILENAME "<stdin>" +#define STDOUT_FILENAME "<stdout>" +#define STDERR_FILENAME "<stderr>" +#define TOTAL_FILENAME NTXT("<Total>") +#define UNKNOWNFD_FILENAME "<pipe(), socket(), or other fds>" + +#define _1KB 1024 +#define _8KB 8192 +#define _32KB 32768 +#define _128KB 131072 +#define _256KB 262144 +#define _512KB 524288 +#define _1000KB 1048576 +#define _10MB 10485760 +#define _100MB 104857600 +#define _1GB 1073741824 +#define _10GB 10737418240 +#define _100GB 107374182400 +#define _1TB 1099511627776 +#define _10TB 10995116277760 + +class FileData : public Histable +{ + friend class IOActivity; +public: + FileData (const char *fName); + FileData (FileData *fData); + ~FileData (); + + virtual char *get_name (Histable::NameFormat nfmt); + virtual Histable *convertto (Histable_type, Histable* = NULL); + + char *get_raw_name (Histable::NameFormat nfmt); + void setFsType (FileSystem_type fst); + void setFsType (const char* fst); + + virtual Histable_type + get_type () + { + return histType; + }; + + virtual uint64_t + get_addr () + { + return virtualFd; + }; + + uint64_t + get_index () + { + return virtualFd; + }; + + void init (); + + char * + getFileName () + { + return fileName; + } + + void + addReadEvent (hrtime_t rt, int64_t nb) + { + readTime += rt; + readBytes += nb; + readCnt++; + } + + hrtime_t + getReadTime () + { + return readTime; + } + + int64_t + getReadBytes () + { + return readBytes; + } + + int32_t + getReadCnt () + { + return readCnt; + } + + void + addWriteEvent (hrtime_t wt, int64_t nb) + { + writeTime += wt; + writeBytes += nb; + writeCnt++; + } + + hrtime_t + getWriteTime () + { + return writeTime; + } + + int64_t + getWriteBytes () + { + return writeBytes; + } + + int32_t + getWriteCnt () + { + return writeCnt; + } + + void + addOtherEvent (hrtime_t ot) + { + otherTime += ot; + otherCnt++; + } + + hrtime_t + getOtherTime () + { + return otherTime; + } + + int32_t + getOtherCnt () + { + return otherCnt; + } + + void + addErrorEvent (hrtime_t er) + { + errorTime += er; + errorCnt++; + } + + hrtime_t + getErrorTime () + { + return errorTime; + } + + int32_t + getErrorCnt () + { + return errorCnt; + } + + void setFileDesList (int fd); + + Vector<int> * + getFileDesList () + { + return fileDesList; + } + + void + setFileDes (int fd) + { + fileDes = fd; + } + + int32_t + getFileDes () + { + return fileDes; + } + + void setVirtualFds (int64_t vfd); + + Vector<int64_t> * + getVirtualFds () + { + return virtualFds; + } + + char * + getFsType () + { + return fsType; + } + + void + setVirtualFd (int64_t vFd) + { + virtualFd = vFd; + } + + int64_t + getVirtualFd () + { + return virtualFd; + } + + void + setHistType (Histable::Type hType) + { + histType = hType; + } + + Histable::Type + getHistType () + { + return histType; + } + + void setWriteStat (hrtime_t wt, int64_t nb); + + hrtime_t + getWSlowestBytes () + { + return wSlowestBytes; + } + + int64_t + getWSmallestBytes () + { + return wSmallestBytes; + } + + int64_t + getWLargestBytes () + { + return wLargestBytes; + } + + int32_t + getW0KB1KBCnt () + { + return w0KB1KBCnt; + } + + int32_t + getW1KB8KBCnt () + { + return w1KB8KBCnt; + } + + int32_t + getW8KB32KBCnt () + { + return w8KB32KBCnt; + } + + int32_t + getW32KB128KBCnt () + { + return w32KB128KBCnt; + } + + int32_t + getW128KB256KBCnt () + { + return w128KB256KBCnt; + } + + int32_t + getW256KB512KBCnt () + { + return w256KB512KBCnt; + } + + int32_t + getW512KB1000KBCnt () + { + return w512KB1000KBCnt; + } + + int32_t + getW1000KB10MBCnt () + { + return w1000KB10MBCnt; + } + + int32_t + getW10MB100MBCnt () + { + return w10MB100MBCnt; + } + + int32_t + getW100MB1GBCnt () + { + return w100MB1GBCnt; + } + + int32_t + getW1GB10GBCnt () + { + return w1GB10GBCnt; + } + + int32_t + getW10GB100GBCnt () + { + return w10GB100GBCnt; + } + + int32_t + getW100GB1TBCnt () + { + return w100GB1TBCnt; + } + + int32_t + getW1TB10TBCnt () + { + return w1TB10TBCnt; + } + + void setReadStat (hrtime_t rt, int64_t nb); + + hrtime_t + getRSlowestBytes () + { + return rSlowestBytes; + } + + int64_t + getRSmallestBytes () + { + return rSmallestBytes; + } + + int64_t + getRLargestBytes () + { + return rLargestBytes; + } + + int32_t + getR0KB1KBCnt () + { + return r0KB1KBCnt; + } + + int32_t + getR1KB8KBCnt () + { + return r1KB8KBCnt; + } + + int32_t + getR8KB32KBCnt () + { + return r8KB32KBCnt; + } + + int32_t + getR32KB128KBCnt () + { + return r32KB128KBCnt; + } + + int32_t + getR128KB256KBCnt () + { + return r128KB256KBCnt; + } + + int32_t + getR256KB512KBCnt () + { + return r256KB512KBCnt; + } + + int32_t + getR512KB1000KBCnt () + { + return r512KB1000KBCnt; + } + + int32_t + getR1000KB10MBCnt () + { + return r1000KB10MBCnt; + } + + int32_t + getR10MB100MBCnt () + { + return r10MB100MBCnt; + } + + int32_t + getR100MB1GBCnt () + { + return r100MB1GBCnt; + } + + int32_t + getR1GB10GBCnt () + { + return r1GB10GBCnt; + } + + int32_t + getR10GB100GBCnt () + { + return r10GB100GBCnt; + } + + int32_t + getR100GB1TBCnt () + { + return r100GB1TBCnt; + } + + int32_t + getR1TB10TBCnt () + { + return r1TB10TBCnt; + } + +private: + char *fileName; // File name + hrtime_t readTime; // The Total time for read operations; + hrtime_t writeTime; // The Total time for write operations; + hrtime_t otherTime; // The Total time for other IO operations; + hrtime_t errorTime; // The Total time for failed IO operations; + int64_t readBytes; //The total bytes read + int64_t writeBytes; //The total bytes written + int32_t readCnt; // The read count + int32_t writeCnt; // The write count + int32_t otherCnt; // The other IO count + int32_t errorCnt; // The failed IO count + Vector<int> *fileDesList; // The list of file descriptors + Vector<int64_t> *virtualFds; // The list of file virtual descriptors + char fsType[FSTYPESZ]; // The file system type + int64_t virtualFd; // The virtual file descriptor + int32_t fileDes; // The file descriptor + Histable::Type histType; // The Histable type: IOACTFILE, IOACTVFD, ... + + // Write statistics + hrtime_t wSlowestBytes; + int64_t wSmallestBytes; + int64_t wLargestBytes; + int32_t w0KB1KBCnt; + int32_t w1KB8KBCnt; + int32_t w8KB32KBCnt; + int32_t w32KB128KBCnt; + int32_t w128KB256KBCnt; + int32_t w256KB512KBCnt; + int32_t w512KB1000KBCnt; + int32_t w1000KB10MBCnt; + int32_t w10MB100MBCnt; + int32_t w100MB1GBCnt; + int32_t w1GB10GBCnt; + int32_t w10GB100GBCnt; + int32_t w100GB1TBCnt; + int32_t w1TB10TBCnt; + + // Read statistics + hrtime_t rSlowestBytes; + int64_t rSmallestBytes; + int64_t rLargestBytes; + int32_t r0KB1KBCnt; + int32_t r1KB8KBCnt; + int32_t r8KB32KBCnt; + int32_t r32KB128KBCnt; + int32_t r128KB256KBCnt; + int32_t r256KB512KBCnt; + int32_t r512KB1000KBCnt; + int32_t r1000KB10MBCnt; + int32_t r10MB100MBCnt; + int32_t r100MB1GBCnt; + int32_t r1GB10GBCnt; + int32_t r10GB100GBCnt; + int32_t r100GB1TBCnt; + int32_t r1TB10TBCnt; +}; + +#endif diff --git a/gprofng/src/Filter.cc b/gprofng/src/Filter.cc new file mode 100644 index 0000000..34eda0d --- /dev/null +++ b/gprofng/src/Filter.cc @@ -0,0 +1,514 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include "Filter.h" +#include "util.h" +#include "i18n.h" +#include "data_pckts.h" +#include "StringBuilder.h" +#include "Experiment.h" + + +// ======================================================================== +// Subclass: FilterNumeric +// Public Methods + +FilterNumeric::FilterNumeric (Experiment *_exp, const char *_cmd, + const char *_name) +{ + exp = _exp; + cmd = dbe_strdup (_cmd); + name = dbe_strdup (_name); + pattern = NULL; + status = NULL; + items = NULL; + prop_name = NULL; + first = (uint64_t) - 1; + last = (uint64_t) - 1; + nselected = 0; + nitems = 0; +} + +FilterNumeric::~FilterNumeric () +{ + free (cmd); + free (name); + free (pattern); + free (status); + Destroy (items); +} + +// sets min and max for this filter; should be called when the range is +// known -- that comes after the first PathTree build, in the current +// sequence of things +void +FilterNumeric::set_range (uint64_t findex, uint64_t lindex, uint64_t total) +{ + if (first == findex && last == lindex) + return; + first = findex; + last = lindex; + nitems = total; + nselected = nitems; + if (pattern) + { + free (pattern); + pattern = NULL; + } + if (status) + { + free (status); + status = NULL; + } +} + +void +FilterNumeric::update_range () +{ + if (exp == NULL) + return; + if (streq (cmd, NTXT ("sample"))) + set_range (1, (uint64_t) exp->nsamples (), exp->nsamples ()); + else if (streq (cmd, NTXT ("thread"))) + set_range (exp->min_thread, exp->max_thread, exp->thread_cnt); + else if (streq (cmd, NTXT ("LWP"))) + set_range (exp->min_lwp, exp->max_lwp, exp->lwp_cnt); + else if (streq (cmd, NTXT ("cpu"))) + { + if (exp->min_cpu != (uint64_t) - 1) + set_range (exp->min_cpu, exp->max_cpu, exp->cpu_cnt); + } +} + +// get_advanced_filter -- returns a string matching the current setting +char * +FilterNumeric::get_advanced_filter () +{ + if (items == NULL) + return NULL; + if (items->size () == 0) + return dbe_strdup (NTXT ("0")); + + StringBuilder sb; + if (items->size () > 1) + sb.append ('('); + for (int i = 0; i < items->size (); i++) + { + RangePair *rp = items->fetch (i); + if (i > 0) + sb.append (NTXT (" || ")); + sb.append ('('); + sb.append (prop_name); + if (rp->first == rp->last) + { + sb.append (NTXT ("==")); + sb.append ((long long) rp->first); + } + else + { + sb.append (NTXT (">=")); + sb.append ((long long) rp->first); + sb.append (NTXT (" && ")); + sb.append (prop_name); + sb.append (NTXT ("<=")); + sb.append ((long long) rp->last); + } + sb.append (')'); + } + if (items->size () > 1) + sb.append (')'); + return sb.toString (); +} + + +// get_pattern -- returns a string matching the current setting + +char * +FilterNumeric::get_pattern () +{ + update_range (); + if (pattern) + return pattern; + StringBuilder sb; + if (items == NULL) + { + if (last == (uint64_t) - 1 && last == first) + // neither set; data not available + sb.append (GTXT ("(data not recorded)")); + else + sb.append (GTXT ("all")); + } + else if (items->size () == 0) + sb.append (GTXT ("none")); + else + { + for (int i = 0; i < items->size (); i++) + { + RangePair *rp = items->fetch (i); + if (i > 0) + sb.append (','); + sb.append ((long long) rp->first); + if (rp->first != rp->last) + { + sb.append ('-'); + sb.append ((long long) rp->last); + } + } + } + pattern = sb.toString (); + return pattern; +} + +char * +FilterNumeric::get_status () +{ + update_range (); + if (status == NULL) + update_status (); + return dbe_strdup (status); +} + +// set_pattern -- set the filter to a new pattern +// set error true/false if there was or was not an error parsing string +// Returns true/false if the filter changed, implying a rebuild of data +bool +FilterNumeric::set_pattern (char *str, bool *error) +{ + update_range (); + // save the old filter + Vector<RangePair *> *olditems = items; + *error = false; + if (strcmp (str, NTXT ("all")) == 0) + // if all, leave items NULL + items = NULL; + else if (strcmp (str, NTXT ("none")) == 0) + // if none, leave items as a zero-length vector + items = new Vector<RangePair *>(0); + else + { + uint64_t val, val2; + char *s = str; + char *nexts = s; + items = NULL; + for (bool done = false; done == false;) + { + // tokenize the string + // Does it start with a "-" ? + if (*nexts == '-') + val = first; // yes, set val to first, and see what follows + else + { + // it must start with a number + val = get_next_number (s, &nexts, error); + if (*error == true) + break; + } + + // look at the next character + switch (*nexts) + { + case ',': + s = ++nexts; + *error = include_range (val, val); + if (*error == true) + done = true; + break; + case '-': + s = ++nexts; + if (*nexts == ',' || *nexts == '\0') + val2 = last; + else + { + val2 = get_next_number (s, &nexts, error); + if (*error == true) + { + done = true; + break; + } + } + if (val > val2) + { + *error = true; + done = true; + break; + } + *error = include_range (val, val2); + if (*error == true) + { + done = true; + break; + } + if (*nexts == ',') + { + s = ++nexts; + break; + } + if (*nexts == '\0') + { + done = true; + break; + } + break; + case '\0': + *error = include_range (val, val); + done = true; + break; + default: + *error = true; + done = true; + break; + } + } + // if there was a parser error leave old setting + if (*error == true) + { + if (items) + { + items->destroy (); + delete items; + } + items = olditems; + return false; + } + } + + if (first != (uint64_t) - 1 && last != (uint64_t) - 1) + { + for (long i = VecSize (items) - 1; i >= 0; i--) + { + RangePair *rp = items->get (i); + if ((rp->first > last) || (rp->last < first)) + { + delete rp; + items->remove (i); + continue; + } + if (rp->first < first) + rp->first = first; + if (rp->last > last) + rp->last = last; + } + if (VecSize (items) == 1) + { + RangePair *rp = items->get (0); + if ((rp->first == first) && (rp->last == last)) + { + // All, leave items NULL + items->destroy (); + delete items; + items = NULL; + } + } + } + + // no error, delete the old setting + if (olditems != NULL) + { + olditems->destroy (); + delete olditems; + } + + bool changed; + // regenerate the pattern + if (pattern == NULL) + changed = true; + else + { + char *oldpattern = pattern; + pattern = NULL; // to force a recompute with new values + (void) get_pattern (); + changed = strcmp (pattern, oldpattern) != 0; + free (oldpattern); + } + return changed; +} + +//================================================================ +// Protected methods + +// set_status -- regenerate the status line, describing the current setting +void +FilterNumeric::update_status () +{ + // regenerate the status line + free (status); + nselected = 0; + if (items == NULL) + { + if (last == (uint64_t) - 1 && last == first) + // neither set; data not available + status = dbe_sprintf (GTXT ("(data not recorded)")); + else if (first == (uint64_t) - 1 || last == (uint64_t) - 1) + // range was not set + status = dbe_sprintf (GTXT ("(all)")); + else + // range was set, compute percentage + status = dbe_sprintf (GTXT ("total %lld, range: %lld-%lld"), + (long long) nitems, (long long) first, + (long long) last); + } + else + { + // some are selected + int index; + RangePair *rp; + Vec_loop (RangePair *, items, index, rp) + { + nselected += rp->last - rp->first + 1; + } + if (last == (uint64_t) - 1) + // range was not set + status = dbe_sprintf (GTXT ("(%lld items selected)"), + (long long) nselected); + else + // range was set + status = dbe_sprintf (GTXT ("total %lld, range: %lld-%lld"), + (long long) nitems, (long long) first, + (long long) last); + } +} + +// Add a range to the filter; called from set_pattern for each index, +// or index pair +bool +FilterNumeric::include_range (uint64_t findex, uint64_t lindex) +{ + int index; + RangePair *rp; + if (findex > lindex) + return true; + + bool done = false; + if (items == NULL) + items = new Vector<RangePair *>(0); + + Vec_loop (RangePair *, items, index, rp) + { + if (findex < rp->first) + { + // Case where the new pair starts before the old + if (lindex + 1 < rp->first) + { + // this pair comes cleanly in front of the current item + RangePair *rp2 = new RangePair (); + rp2->first = findex; + rp2->last = lindex; + items->insert (index, rp2); + done = true; + break; + } + // This new one extends the previous from the front + rp->first = findex; +chkextend: + if (lindex <= rp->last) + { + // but does not extend the back + done = true; + break; + } + // extend this one out + rp->last = lindex; + + // does it go into the next range? + if (index == items->size () - 1) + { + // this is the last range, so it does not + done = true; + break; + } + RangePair *next = items->fetch (index + 1); + if (lindex + 1 < next->first) + { + // no extension, we're done + done = true; + break; + } + // it does extend the next one + next->first = rp->first; + rp = next; + // remove the current one, promoting next + items->remove (index); + goto chkextend; + } + else if (findex > rp->last + 1) + // the new one is completely beyond the current + continue; + else + { + // the new one may start at or after the current, but it + // extends it out; set the current + // this pair overlaps the current item + // rp-> first is OK -- it's equal or less than findex + goto chkextend; + } + } + + if (done != true) + { + // fall through -- append to list + rp = new RangePair (); + rp->first = findex; + rp->last = lindex; + items->append (rp); + } + + return false; +} + +// Scan the filter to see if the number given is filtered in or out +// return true if number is in, false if it's out +bool +FilterNumeric::is_selected (uint64_t number) +{ + int index; + RangePair *rp; + if (items == NULL) + return true; + if (items->size () == 0) + return false; + + Vec_loop (RangePair *, items, index, rp) + { + if (number >= rp->first && number <= rp->last) + return true; + } + return false; +} + +// get_next_number +// Called from parser to extract a number from the current string position +// Sets fail true if there was an error, false otherwise +// returns the number as parsed +uint64_t +FilterNumeric::get_next_number (char *s, char **e, bool *fail) +{ + errno = 0; + *fail = false; + uint64_t val = strtoll (s, e, 10); + if (errno == EINVAL) + *fail = true; + return (val); +} diff --git a/gprofng/src/Filter.h b/gprofng/src/Filter.h new file mode 100644 index 0000000..1338218 --- /dev/null +++ b/gprofng/src/Filter.h @@ -0,0 +1,111 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _FILTER_H +#define _FILTER_H + +// A sample selection specifies a set of samples the user is interested in +// viewing as a whole. + +#include "vec.h" +#include "data_pckts.h" + +class Experiment; + +class FilterNumeric +{ +public: + FilterNumeric (Experiment *, const char *, const char *); + ~FilterNumeric (); + + // set or update the range of items first and last + void set_range (uint64_t findex, uint64_t lindex, uint64_t total); + + // Return a string representation of the current ranges + // E.g. "1-5,7,9,10,12-13,73" + char *get_pattern (); + + // Return a string for the current status: %, range, ... + // E.g. "100%" "100% [1-7]" "25% [1-4]" + char *get_status (); + + char *get_advanced_filter (); + + // Sets selection according to the string representation + // See above for return values and error handling + bool set_pattern (char *, bool *); + + // Return true if "number" is included in selection + bool is_selected (uint64_t number); + + char * + get_cmd () + { + return cmd; + }; + + char * + get_name () + { + return name; + }; + + uint64_t + nelem () + { + return nitems; + }; + + char *prop_name; // name in advanced filter + +private: + + typedef struct + { + uint64_t first; + uint64_t last; + } RangePair; + + void update_status (); + void update_range (); + + // Include "range" in selection + bool include_range (uint64_t findex, uint64_t lindex); + + // Parse a number from the string + uint64_t get_next_number (char *s, char **e, bool *fail); + + // Data + Vector<RangePair *> *items; // sorted array of items + uint64_t nselected; + uint64_t nitems; + + Experiment *exp; + char *cmd; + char *name; + char *pattern; + char *status; + + // First and Last items in selection + uint64_t first; + uint64_t last; +}; + +#endif /* _FILTER_H */ diff --git a/gprofng/src/FilterExp.h b/gprofng/src/FilterExp.h new file mode 100644 index 0000000..b186af1 --- /dev/null +++ b/gprofng/src/FilterExp.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _FILTEREXP_H +#define _FILTEREXP_H + +#include "Expression.h" + +class FilterExp +{ +public: + + FilterExp (Expression *_expr, Expression::Context *_ctx, bool _noParFilter) : + expr (_expr), ctx (_ctx), noParFilter (_noParFilter) { }; + + ~FilterExp () + { + delete ctx; + } + + bool + passes () + { + return expr ? expr->passes (ctx) : true; + } + + void + put (DataView *dview, long eventId) + { + ctx->put (dview, eventId); + } + + Expression *expr; + Expression::Context *ctx; + bool noParFilter; +}; + + +#endif /* _FILTEREXP_H */ diff --git a/gprofng/src/FilterSet.cc b/gprofng/src/FilterSet.cc new file mode 100644 index 0000000..29dc437 --- /dev/null +++ b/gprofng/src/FilterSet.cc @@ -0,0 +1,106 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "Experiment.h" +#include "StringBuilder.h" +#include "FilterSet.h" +#include "Filter.h" +#include "i18n.h" + +FilterSet::FilterSet (DbeView *_dbev, Experiment *_exp) +{ + dbev = _dbev; + exp = _exp; + enbl = false; + dfilter = new Vector<FilterNumeric *>; + FilterNumeric *f; + f = new FilterNumeric (exp, "sample", GTXT ("Samples")); + f->prop_name = NTXT ("SAMPLE_MAP"); + dfilter->append (f); + f = new FilterNumeric (exp, "thread", GTXT ("Threads")); + f->prop_name = NTXT ("THRID"); + dfilter->append (f); + f = new FilterNumeric (exp, "LWP", GTXT ("LWPs")); + f->prop_name = NTXT ("LWPID"); + dfilter->append (f); + f = new FilterNumeric (exp, "cpu", GTXT ("CPUs")); + f->prop_name = NTXT ("CPUID"); + dfilter->append (f); + f = new FilterNumeric (exp, "gcevent", GTXT ("GCEvents")); + f->prop_name = NTXT ("GCEVENT_MAP"); + dfilter->append (f); // must add new numeric below +} + +FilterSet::~FilterSet () +{ + dfilter->destroy (); + delete dfilter; +} + +FilterNumeric * +FilterSet::get_filter (int index) +{ + if (index < dfilter->size () && index >= 0) + return dfilter->fetch (index); + return NULL; +} + +char * +FilterSet::get_advanced_filter () +{ + StringBuilder sb; + bool filtrIsFalse = false; + + if (get_enabled ()) + { + Vector<FilterNumeric*> *filts = get_all_filters (); + if (filts == NULL) + return NULL; + for (int i = 0; i < filts->size (); i++) + { + FilterNumeric *f = filts->fetch (i); + if (f == NULL) + continue; + char *s = f->get_advanced_filter (); + if (s == NULL) + continue; + if (streq (s, NTXT ("0"))) + { + free (s); + sb.setLength (0); + filtrIsFalse = true; + break; + } + if (sb.length () != 0) + sb.append (NTXT (" && ")); + sb.append (s); + free (s); + } + } + else + filtrIsFalse = true; + if (filtrIsFalse) + sb.append ('0'); + else if (sb.length () == 0) + return NULL; + return sb.toString (); +} + diff --git a/gprofng/src/FilterSet.h b/gprofng/src/FilterSet.h new file mode 100644 index 0000000..6908565 --- /dev/null +++ b/gprofng/src/FilterSet.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _FILTERSET_H +#define _FILTERSET_H + +#include "dbe_types.h" +#include "vec.h" + +class Experiment; +class FilterNumeric; +class DbeView; + +#define SAMPLE_FILTER_IDX 0 +#define THREAD_FILTER_IDX 1 +#define LWP_FILTER_IDX 2 +#define CPU_FILTER_IDX 3 + +class FilterSet +{ +public: + + FilterSet (DbeView *_dbev, Experiment *_exp); + ~FilterSet (); + char *get_advanced_filter (); + FilterNumeric *get_filter (int); + + bool + get_enabled () + { + return enbl; + } + + void + set_enabled (bool b) + { + enbl = b; + } + + Vector<FilterNumeric*> * + get_all_filters () + { + return dfilter; + } + +private: + + DbeView *dbev; + Experiment *exp; + bool enbl; + Vector<FilterNumeric*> *dfilter; +}; + +#endif /* _FILTERSET_H */ + diff --git a/gprofng/src/Function.cc b/gprofng/src/Function.cc new file mode 100644 index 0000000..b0e4a8f --- /dev/null +++ b/gprofng/src/Function.cc @@ -0,0 +1,1160 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <assert.h> +#include <string.h> +#include <ctype.h> + +#include "demangle.h" +#include "util.h" +#include "DbeSession.h" +#include "Function.h" +#include "Module.h" +#include "LoadObject.h" +#include "Settings.h" +#include "DbeFile.h" +#include "DbeView.h" + +struct SrcInfo +{ + DbeLine *src_line; + SrcInfo *included_from; + SrcInfo *next; +}; + +struct PCInfo +{ + int64_t offset; + int64_t size; + SrcInfo *src_info; +}; + +Function::Function (uint64_t _id) +{ + id = _id; + instr_id = id << 32; + derivedNode = NULL; + module = NULL; + line_first = line_last = -1; + isOutlineFunction = false; + name = NULL; + mangled_name = NULL; + match_name = NULL; + comparable_name = NULL; + img_fname = NULL; + img_offset = 0; + chksum = 0; + flags = 0; + size = 0; + save_addr = FUNC_NO_SAVE; + linetab = NULL; + def_source = NULL; + indexStabsLink = NULL; + elfSym = NULL; + sources = NULL; + instrs = new Vector<DbeInstr*>; + addrs = NULL; + name_buf = NULL; + current_name_format = Histable::NA; + curr_srcinfo = NULL; + curr_srcfile = NULL; + srcinfo_list = NULL; + defaultDbeLine = NULL; + usrfunc = NULL; + alias = NULL; + instHTable = NULL; + addrIndexHTable = NULL; + isUsed = false; + isHideFunc = false; + inlinedSubr = NULL; + inlinedSubrCnt = 0; +} + +Function::~Function () +{ + free (mangled_name); + free (match_name); + free (comparable_name); + free (name_buf); + Destroy (linetab); + Destroy (instrs); + + while (srcinfo_list) + { + SrcInfo *t = srcinfo_list; + srcinfo_list = t->next; + delete t; + } + delete sources; + delete addrs; + delete[] instHTable; + delete[] addrIndexHTable; + if (indexStabsLink) + // Remove a link to the current function + indexStabsLink->indexStabsLink = NULL; +} + +char * +Function::get_name (NameFormat nfmt) +{ + if (nfmt == Histable::NA) + { + DbeView *dbeView = dbeSession->getView (0); + if (dbeView) + nfmt = dbeView->get_name_format (); + } + if (name_buf && (nfmt == current_name_format || nfmt == Histable::NA)) + return name_buf; + free (name_buf); + current_name_format = nfmt; + + bool soname_fmt = Histable::soname_fmt (nfmt); + int fname_fmt = Histable::fname_fmt (nfmt); + if (fname_fmt == Histable::MANGLED) + name_buf = strdup (mangled_name); + else + { + if (module && module->is_fortran () + && (streq (name, "MAIN") || streq (name, "MAIN_"))) + name_buf = strdup (match_name); + else + name_buf = strdup (name); + + if (fname_fmt == Histable::SHORT) + { + int i = get_paren (name_buf); + if (i != -1) + name_buf[i] = (char) 0; + } + } + if (soname_fmt) + { + char *fname = dbe_sprintf (NTXT ("%s [%s]"), name_buf, module->loadobject->get_name ()); + free (name_buf); + name_buf = fname; + } + return name_buf; +} + +uint64_t +Function::get_addr () +{ + LoadObject *lo = module ? module->loadobject : NULL; + int seg_idx = lo ? lo->seg_idx : -1; + return MAKE_ADDRESS (seg_idx, img_offset); +} + +Histable * +Function::convertto (Histable_type type, Histable *obj) +{ + Histable *res = NULL; + SourceFile *source = (SourceFile*) obj; + switch (type) + { + case INSTR: + res = find_dbeinstr (0, 0); + break; + case LINE: + { + // mapPCtoLine will implicitly read line info if necessary + res = mapPCtoLine (0, source); + break; + } + case FUNCTION: + res = this; + break; + case SOURCEFILE: + res = def_source; + break; + default: + assert (0); + } + return res; +} + +void +Function::set_name (char *string) +{ + if (string == NULL) + return; + set_mangled_name (string); + + // strip away any globalization prefix, and save result for matching + char *mname = string; + if (strncmp (string, "$X", 2) == 0 || strncmp (string, ".X", 2) == 0) + { + // name was globalized + char *n = strchr (string + 2, (int) '.'); + if (n != NULL) + mname = n + 1; + } + set_match_name (mname); + name = NULL; + if (module) + { + if (name == NULL && *match_name == '_') + { + int flag = DMGL_PARAMS; + if (module->lang_code == Sp_lang_java) + flag |= DMGL_JAVA; + name = cplus_demangle (match_name, flag); + } + } + if (name == NULL) // got demangled string + name = dbe_strdup (match_name); + set_comparable_name (name); +} + +void +Function::set_mangled_name (const char *string) +{ + if (string) + { + free (mangled_name); + mangled_name = dbe_strdup (string); + } +} + +void +Function::set_match_name (const char *string) +{ + if (string) + { + free (match_name); + match_name = dbe_strdup (string); + } +} + +void +Function::set_comparable_name (const char *string) +{ + if (string) + { + free (comparable_name); + comparable_name = dbe_strdup (string); + + // remove blanks from comparable_name + for (char *s = comparable_name, *s1 = comparable_name;;) + { + if (*s == 0) + { + *s1 = 0; + break; + } + else if (*s != ' ') + { + *s1 = *s; + s1++; + } + s++; + } + } +} + +// This function looks at the name of a function, and determines whether +// or not it may be a derived function -- outline, mtask, or clone -- +// If it is, it writes the function name as demangled, +// and sets a pointer to the function from which it was derived +void +Function::findDerivedFunctions () + +{ + MPFuncTypes ftype; + int index; + Function *fitem; + unsigned long long line_no; + char *namefmt; + char *subname = mangled_name; + char *demname; + + // see if we've already done this + if ((flags & FUNC_FLAG_RESDER) != 0) + return; + + // set flag for future + flags = flags | FUNC_FLAG_RESDER; + if (module == NULL) + return; + if (*subname != '_' || subname[1] != '$') // Not a specially named function + return; + + // look for the current versions of naming + if (strncmp (subname + 2, NTXT ("d1"), 2) == 0) // doall function + ftype = MPF_DOALL; + else if (strncmp (subname + 2, "p1", 2) == 0) // parallel region function + ftype = MPF_PAR; + else if (strncmp (subname + 2, "l1", 2) == 0) // single thread loop setup + ftype = MPF_DOALL; + else if (strncmp (subname + 2, "s1", 2) == 0) // parallel section function + ftype = MPF_SECT; + else if (strncmp (subname + 2, "t1", 2) == 0) // task function + ftype = MPF_TASK; + else if (strncmp (subname + 2, "o1", 2) == 0) // outline function + { + ftype = MPF_OUTL; + isOutlineFunction = true; + } + else if (strncmp (subname + 2, "c1", 2) == 0) // clone function + ftype = MPF_CLONE; + else // Not an encoded name, just return + return; + + // we know it's one of the above prefixes + char *sub = dbe_strdup (name + 4); // starting with base-26 number + char *p = sub; + + // skip the base-26 number, and extract the line number + while (isalpha ((int) (*p)) != 0 && *p != 0) + p++; + line_no = atoll (p); + + // skip past the number to to the . + while (*p != '.' && *p != 0) + p++; + if (*p == 0) + { + // can't be right + free (sub); + return; + } + // skip the trailing . + p++; + subname = p; + bool foundmatch = false; + + // Find the function from which it is derived -- the one that matched subname + Vec_loop (Function*, module->functions, index, fitem) + { + if (streq (subname, fitem->mangled_name)) + { // found it + foundmatch = true; + usrfunc = fitem; + + // set the derived node + if ((fitem->flags & FUNC_FLAG_RESDER) == 0) + // ensure that it, too, is resolved if derived + fitem->findDerivedFunctions (); + + // Build a demangled name + switch (ftype) + { + case MPF_OUTL: + isOutlineFunction = true; + namefmt = GTXT ("%s -- outline code from line %lld [%s]"); + derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no); + break; + case MPF_PAR: + namefmt = GTXT ("%s -- OMP parallel region from line %lld [%s]"); + break; + case MPF_DOALL: + namefmt = GTXT ("%s -- Parallel loop from line %lld [%s]"); + break; + case MPF_SECT: + namefmt = GTXT ("%s -- OMP sections from line %lld [%s]"); + break; + case MPF_CLONE: + // Note that clones are handled differently -- no line number and + // clones are NOT shown as called from the original + // so after constructing the name, just return + // later, establish link from clone to parent + demname = dbe_sprintf (GTXT ("%s -- cloned version [%s]"), + fitem->get_name (), name); + free (name); + // set the name to the demangled version + name = demname; + free (sub); + derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no); + return; + case MPF_TASK: + namefmt = GTXT ("%s -- OMP task from line %lld [%s]"); + break; + default: + free (sub); + return; + + } + + // Finally, construct the demangled name + demname = dbe_sprintf (namefmt, fitem->get_name (), line_no, name); + free (name); + name = demname; + setLineFirst ((int) line_no); + break; + } + } + + if (foundmatch == false && ftype == MPF_OUTL) + { + // Even if derived node was not found, we can demangle + demname = dbe_sprintf (GTXT ("%s -- outline code [%s]"), subname, + mangled_name); + free (name); + name = demname; + } + free (sub); +} + +SrcInfo * +Function::new_srcInfo () +{ + SrcInfo *t = new SrcInfo (); + t->next = srcinfo_list; + srcinfo_list = t; + return t; +} + +void +Function::pushSrcFile (SourceFile* source, int /*lineno*/) +{ + // create new file stack + if (curr_srcfile == NULL) + { + curr_srcfile = source; + return; + } + + SrcInfo *src_info = new_srcInfo (); + // In the ideal world, we need a DbeLine(III) here, + // but right now it would make us later believe that there are + // instructions generated for #include lines. To avoid that, + // we ask for a DbeLine(II). + src_info->src_line = curr_srcfile->find_dbeline (this, 0 /*lineno*/); + if (src_info->src_line) + { + src_info->included_from = curr_srcinfo; + curr_srcinfo = src_info; + } + curr_srcfile = source; + setSource (); +} + +SourceFile * +Function::popSrcFile () +{ + if (curr_srcinfo != NULL) + { + curr_srcfile = curr_srcinfo->src_line->sourceFile; + curr_srcinfo = curr_srcinfo->included_from; + } + else + curr_srcfile = NULL; + return curr_srcfile; +} + +void +Function::copy_PCInfo (Function *from) +{ + if (line_first <= 0) + line_first = from->line_first; + if (line_last <= 0) + line_last = from->line_last; + if (def_source == NULL) + def_source = from->def_source; + for (int i = 0, sz = from->linetab ? from->linetab->size () : 0; i < sz; i++) + { + PCInfo *pcinf = from->linetab->fetch (i); + DbeLine *dbeLine = pcinf->src_info->src_line; + add_PC_info (pcinf->offset, dbeLine->lineno, dbeLine->sourceFile); + } +} + +void +Function::add_PC_info (uint64_t offset, int lineno, SourceFile *cur_src) +{ + if (lineno <= 0 || size < 0 || offset >= (uint64_t) size) + return; + if (cur_src == NULL) + cur_src = curr_srcfile ? curr_srcfile : def_source; + if (linetab == NULL) + linetab = new Vector<PCInfo*>; + + int left = 0; + int right = linetab->size () - 1; + DbeLine *dbeline; + while (left <= right) + { + int x = (left + right) / 2; + PCInfo *pcinf = linetab->fetch (x); + uint64_t pcinf_offset = ((uint64_t) pcinf->offset); + if (offset == pcinf_offset) + { + dbeline = cur_src->find_dbeline (this, lineno); + dbeline->init_Offset (offset); + pcinf->src_info->src_line = dbeline; + // Ignore duplicate offset + return; + } + else if (offset > pcinf_offset) + left = x + 1; + else + right = x - 1; + } + PCInfo *pcinfo = new PCInfo; + pcinfo->offset = offset; + + // Form new SrcInfo + SrcInfo *srcInfo = new_srcInfo (); + dbeline = cur_src->find_dbeline (this, lineno); + dbeline->init_Offset (offset); + srcInfo->src_line = dbeline; + // For now don't build included_from list. + // We need better compiler support for that. + //srcInfo->included_from = curr_srcinfo; + srcInfo->included_from = NULL; + pcinfo->src_info = srcInfo; + + // Update the size of the current line in both structures: + // current PCInfo and corresponding DbeLine. + if (left < linetab->size ()) + pcinfo->size = linetab->fetch (left)->offset - offset; + else + pcinfo->size = size - offset; + pcinfo->src_info->src_line->size += pcinfo->size; + + // If not the first line, update the size of the previous line + if (left > 0) + { + PCInfo *pcinfo_prev = linetab->fetch (left - 1); + int64_t delta = (offset - pcinfo_prev->offset) - pcinfo_prev->size; + pcinfo_prev->size += delta; + pcinfo_prev->src_info->src_line->size += delta; + } + + linetab->insert (left, pcinfo); + if (cur_src == def_source) + { + if (line_first <= 0) + setLineFirst (lineno); + if (line_last <= 0 || lineno > line_last) + line_last = lineno; + } +} + +PCInfo * +Function::lookup_PCInfo (uint64_t offset) +{ + module->read_stabs (); + if (linetab == NULL) + linetab = new Vector<PCInfo*>; + + int left = 0; + int right = linetab->size () - 1; + while (left <= right) + { + int x = (left + right) / 2; + PCInfo *pcinfo = linetab->fetch (x); + if (offset >= ((uint64_t) pcinfo->offset)) + { + if (offset < (uint64_t) (pcinfo->offset + pcinfo->size)) + return pcinfo; + left = x + 1; + } + else + right = x - 1; + } + return NULL; +} + +DbeInstr* +Function::mapLineToPc (DbeLine *dbeLine) +{ + if (dbeLine && linetab) + { + DbeLine *dbl = dbeLine->dbeline_base; + for (int i = 0, sz = linetab->size (); i < sz; i++) + { + PCInfo *pcinfo = linetab->get (i); + if (pcinfo->src_info + && (pcinfo->src_info->src_line->dbeline_base == dbl)) + { + DbeInstr *dbeInstr = find_dbeinstr (PCLineFlag, pcinfo->offset); + if (dbeInstr) + { + dbeInstr->lineno = dbeLine->lineno; + return dbeInstr; + } + } + } + } + return NULL; +} + +DbeLine* +Function::mapPCtoLine (uint64_t addr, SourceFile *src) +{ + PCInfo *pcinfo = lookup_PCInfo (addr); + if (pcinfo == NULL) + { + if (defaultDbeLine == NULL) + defaultDbeLine = getDefSrc ()->find_dbeline (this, 0); + return defaultDbeLine; + } + DbeLine *dbeline = pcinfo->src_info->src_line; + + // If source-context is not specified return the line + // from which this pc has been generated. + if (src == NULL) + return dbeline; + if (dbeline->sourceFile == src) + return dbeline->dbeline_base; + return src->find_dbeline (this, 0); +} + +DbeInstr * +Function::find_dbeinstr (int flag, uint64_t addr) +{ + DbeInstr *instr; + + enum + { + FuncInstHTableSize = 128 + }; + + int hash = (((int) addr) >> 2) & (FuncInstHTableSize - 1); + if (instHTable == NULL) + { + if (size > 2048) + { + instHTable = new DbeInstr*[FuncInstHTableSize]; + for (int i = 0; i < FuncInstHTableSize; i++) + instHTable[i] = NULL; + } + } + else + { + instr = instHTable[hash]; + if (instr && instr->addr == addr && instr->flags == flag) + return instr; + } + + int left = 0; + int right = instrs->size () - 1; + while (left <= right) + { + int index = (left + right) / 2; + instr = instrs->fetch (index); + if (addr < instr->addr) + right = index - 1; + else if (addr > instr->addr) + left = index + 1; + else + { + if (flag == instr->flags) + { + if (instHTable) + instHTable[hash] = instr; + return instr; + } + else if (flag < instr->flags) + right = index - 1; + else + left = index + 1; + } + } + + // None found, create a new one + instr = new DbeInstr (instr_id++, flag, this, addr); + instrs->insert (left, instr); + if (instHTable) + instHTable[hash] = instr; + return instr; +} + +// LIBRARY_VISIBILITY +DbeInstr * +Function::create_hide_instr (DbeInstr *instr) +{ + DbeInstr *new_instr = new DbeInstr (instr_id++, 0, this, instr->addr); + return new_instr; +} + +uint64_t +Function::find_previous_addr (uint64_t addr) +{ + if (addrs == NULL) + { + addrs = module->getAddrs (this); + if (addrs == NULL) + return addr; + } + + int index = -1, not_found = 1; + + enum + { + FuncAddrIndexHTableSize = 128 + }; + int hash = (((int) addr) >> 2) & (FuncAddrIndexHTableSize - 1); + if (addrIndexHTable == NULL) + { + if (size > 2048) + { + addrIndexHTable = new int[FuncAddrIndexHTableSize]; + for (int i = 0; i < FuncAddrIndexHTableSize; i++) + addrIndexHTable[i] = -1; + } + } + else + { + index = addrIndexHTable[hash]; + if (index >= 0 && addrs->fetch (index) == addr) + not_found = 0; + } + + int left = 0; + int right = addrs->size () - 1; + while (not_found && left <= right) + { + index = (left + right) / 2; + uint64_t addr_test = addrs->fetch (index); + if (addr < addr_test) + right = index - 1; + else if (addr > addr_test) + left = index + 1; + else + { + if (addrIndexHTable) + addrIndexHTable[hash] = index; + not_found = 0; + } + } + if (not_found) + return addr; + if (index > 0) + index--; + return addrs->fetch (index); +} + +void +Function::setSource () +{ + SourceFile *sf = module->getIncludeFile (); + if (sf == NULL) + sf = getDefSrc (); + if (def_source == NULL) + setDefSrc (sf); + if (sf == def_source) + return; + if (sources == NULL) + { + sources = new Vector<SourceFile*>; + sources->append (def_source); + sources->append (sf); + } + else if (sources->find (sf) < 0) + sources->append (sf); +} + +void +Function::setDefSrc (SourceFile *sf) +{ + if (sf) + { + def_source = sf; + if (line_first > 0) + add_PC_info (0, line_first, def_source); + } +} + +void +Function::setLineFirst (int lineno) +{ + if (lineno > 0) + { + line_first = lineno; + if (line_last <= 0) + line_last = lineno; + if (def_source) + add_PC_info (0, line_first, def_source); + } +} + +Vector<SourceFile*> * +Function::get_sources () +{ + if (module) + module->read_stabs (); + if (sources == NULL) + { + sources = new Vector<SourceFile*>; + sources->append (getDefSrc ()); + } + return sources; +} + +SourceFile* +Function::getDefSrc () +{ + if (module) + module->read_stabs (); + if (def_source == NULL) + setDefSrc (module->getMainSrc ()); + return def_source; +} + +char * +Function::getDefSrcName () +{ + SourceFile *sf = getDefSrc (); + if (sf) + return sf->dbeFile->getResolvedPath (); + if (module) + return module->file_name; + sf = dbeSession->get_Unknown_Source (); + return sf->get_name (); +} + +#define cmpValue(a, b) ((a) > (b) ? 1 : (a) == (b) ? 0 : -1) + +int +Function::func_cmp (Function *func, SourceFile *srcContext) +{ + if (def_source != func->def_source) + { + if (srcContext == NULL) + srcContext = getDefSrc (); + if (def_source == srcContext) + return -1; + if (func->def_source == srcContext) + return 1; + return cmpValue (img_offset, func->img_offset); + } + + if (line_first == func->line_first) + return cmpValue (img_offset, func->img_offset); + if (line_first <= 0) + { + if (func->line_first > 0) + return 1; + return cmpValue (img_offset, func->img_offset); + } + if (func->line_first <= 0) + return -1; + return cmpValue (line_first, func->line_first); +} + +Vector<Histable*> * +Function::get_comparable_objs () +{ + update_comparable_objs (); + if (comparable_objs || dbeSession->expGroups->size () <= 1 || module == NULL) + return comparable_objs; + if (module == NULL || module->loadobject == NULL) + return NULL; + Vector<Histable*> *comparableModules = module->get_comparable_objs (); + if (comparableModules == NULL) + { + return NULL; + } + comparable_objs = new Vector<Histable*>(comparableModules->size ()); + for (long i = 0, sz = comparableModules->size (); i < sz; i++) + { + Function *func = NULL; + comparable_objs->store (i, func); + Module *mod = (Module*) comparableModules->fetch (i); + if (mod == NULL) + continue; + if (mod == module) + func = this; + else + { + for (long i1 = 0, sz1 = VecSize (mod->functions); i1 < sz1; i1++) + { + Function *f = mod->functions->get (i1); + if ((f->comparable_objs == NULL) + && (strcmp (f->comparable_name, comparable_name) == 0)) + { + func = f; + func->comparable_objs = comparable_objs; + break; + } + } + } + comparable_objs->store (i, func); + } + Vector<Histable*> *comparableLoadObjs = + module->loadobject->get_comparable_objs (); + if (VecSize (comparableLoadObjs) == VecSize (comparable_objs)) + { + for (long i = 0, sz = VecSize (comparableLoadObjs); i < sz; i++) + { + LoadObject *lo = (LoadObject *) comparableLoadObjs->get (i); + Function *func = (Function *) comparable_objs->get (i); + if (func || (lo == NULL)) + continue; + if (module->loadobject == lo) + func = this; + else + { + for (long i1 = 0, sz1 = VecSize (lo->functions); i1 < sz1; i1++) + { + Function *f = lo->functions->fetch (i1); + if ((f->comparable_objs == NULL) + && (strcmp (f->comparable_name, comparable_name) == 0)) + { + func = f; + func->comparable_objs = comparable_objs; + break; + } + } + } + comparable_objs->store (i, func); + } + } + dump_comparable_objs (); + return comparable_objs; +} + +JMethod::JMethod (uint64_t _id) : Function (_id) +{ + mid = 0LL; + addr = (Vaddr) 0; + signature = NULL; + jni_function = NULL; +} + +JMethod::~JMethod () +{ + free (signature); +} + +uint64_t +JMethod::get_addr () +{ + if (addr != (Vaddr) 0) + return addr; + else + return Function::get_addr (); +} + +typedef struct +{ + size_t used_in; + size_t used_out; +} MethodField; + +static void +write_buf (char* buf, char* str) +{ + while ((*buf++ = *str++)); +} + +/** Translate one field from the nane buffer. + * return how many chars were read from name and how many bytes were used in buf. + */ +static MethodField +translate_method_field (const char* name, char* buf) +{ + MethodField out, t; + switch (*name) + { + case 'L': + name++; + out.used_in = 1; + out.used_out = 0; + while (*name != ';') + { + *buf = *name++; + if (*buf == '/') + *buf = '.'; + buf++; + out.used_in++; + out.used_out++; + } + out.used_in++; /* the ';' is also used. */ + break; + case 'Z': + write_buf (buf, NTXT ("boolean")); + out.used_out = 7; + out.used_in = 1; + break; + case 'B': + write_buf (buf, NTXT ("byte")); + out.used_out = 4; + out.used_in = 1; + break; + case 'C': + write_buf (buf, NTXT ("char")); + out.used_out = 4; + out.used_in = 1; + break; + case 'S': + write_buf (buf, NTXT ("short")); + out.used_out = 5; + out.used_in = 1; + break; + case 'I': + write_buf (buf, NTXT ("int")); + out.used_out = 3; + out.used_in = 1; + break; + case 'J': + write_buf (buf, NTXT ("long")); + out.used_out = 4; + out.used_in = 1; + break; + case 'F': + write_buf (buf, NTXT ("float")); + out.used_out = 5; + out.used_in = 1; + break; + case 'D': + write_buf (buf, NTXT ("double")); + out.used_out = 6; + out.used_in = 1; + break; + case 'V': + write_buf (buf, NTXT ("void")); + out.used_out = 4; + out.used_in = 1; + break; + case '[': + t = translate_method_field (name + 1, buf); + write_buf (buf + t.used_out, NTXT ("[]")); + out.used_out = t.used_out + 2; + out.used_in = t.used_in + 1; + break; + default: + out.used_out = 0; + out.used_in = 0; + } + return out; +} + +/** + * translate method name to full method signature + * into the output buffer (buf). + * ret_type - true for printing result type + */ +static bool +translate_method (char* mname, char *signature, bool ret_type, char* buf) +{ + MethodField p; + size_t l; + int first = 1; + if (signature == NULL) + return false; + + const char *c = strchr (signature, ')'); + if (c == NULL) + return false; + if (ret_type) + { + p = translate_method_field (++c, buf); + buf += p.used_out; + *buf++ = ' '; + } + + l = strlen (mname); + memcpy (buf, mname, l + 1); + buf += l; + // *buf++ = ' '; // space before () + *buf++ = '('; + + c = signature + 1; + while (*c != ')') + { + if (!first) + { + *buf++ = ','; + *buf++ = ' '; + } + first = 0; + p = translate_method_field (c, buf); + c += p.used_in; + buf += p.used_out; + } + + *buf++ = ')'; + *buf = '\0'; + return true; +} + +void +JMethod::set_name (char *string) +{ + if (string == NULL) + return; + set_mangled_name (string); + + char buf[MAXDBUF]; + *buf = '\0'; + if (translate_method (string, signature, false, buf)) + { + name = dbe_strdup (buf); // got translated string + Dprintf (DUMP_JCLASS_READER, + "JMethod::set_name: true name=%s string=%s signature=%s\n", + STR (name), STR (string), STR (signature)); + } + else + { + name = dbe_strdup (string); + Dprintf (DUMP_JCLASS_READER, + "JMethod::set_name: false name=%s signature=%s\n", + STR (name), STR (signature)); + } + set_match_name (name); + set_comparable_name (name); +} + +bool +JMethod::jni_match (Function *func) +{ + if (func == NULL || (func->flags & FUNC_NOT_JNI) != 0) + return false; + if (jni_function == func) + return true; + + char *fname = func->get_name (); + if ((func->flags & FUNC_JNI_CHECKED) == 0) + { + func->flags |= FUNC_JNI_CHECKED; + if (strncmp (func->get_name (), NTXT ("Java_"), 5) != 0) + { + func->flags |= FUNC_NOT_JNI; + return false; + } + } + + char *d = name; + char *s = fname + 5; + while (*d && *d != '(' && *d != ' ') + { + if (*d == '.') + { + if (*s++ != '_') + return false; + d++; + } + else if (*d == '_') + { + if (*s++ != '_') + return false; + if (*s++ != '1') + return false; + d++; + } + else if (*d++ != *s++) + return false; + } + jni_function = func; + return true; +} diff --git a/gprofng/src/Function.h b/gprofng/src/Function.h new file mode 100644 index 0000000..b0a856b --- /dev/null +++ b/gprofng/src/Function.h @@ -0,0 +1,222 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_FUNCTION_H +#define _DBE_FUNCTION_H + +// A Function object represents an individual function in a .o file. + +#include "util.h" +#include "Histable.h" +#include "SourceFile.h" + +class Module; +class Symbol; +class InlinedSubr; +struct SrcInfo; +struct PCInfo; +template <class ITEM> class Vector; + +const uint64_t FUNC_NO_SAVE = (uint64_t) - 1; +const uint64_t FUNC_ROOT = (uint64_t) - 2; + +enum +{ + FUNC_FLAG_PLT = 1, + FUNC_FLAG_DYNAMIC = 2, + FUNC_FLAG_RESDER = 4, // set if derived function resolution has been done + FUNC_FLAG_NO_OFFSET = 8, // set if disassembly should not show offset from function + FUNC_FLAG_SIMULATED = 16, // not a real function like <Total>, <Unknown>, etc. + FUNC_FLAG_NATIVE = 32, // no byte code for JMethod + FUNC_NOT_JNI = 64, // a function name is not "Java_..." + FUNC_JNI_CHECKED = 128 // already checked for "Java_..." +}; + +const int MAXDBUF = 32768; // the longest demangled name handled + +class Function : public Histable +{ +public: + + enum MPFuncTypes + { + MPF_DOALL, + MPF_PAR, + MPF_SECT, + MPF_TASK, + MPF_CLONE, + MPF_OUTL + }; + + Function (uint64_t _id); + virtual ~Function (); + + virtual uint64_t get_addr (); + virtual char *get_name (NameFormat = NA); + virtual Vector<Histable*> *get_comparable_objs (); + virtual void set_name (char *); // Set the demangled name + virtual Histable *convertto (Histable_type type, Histable *obj = NULL); + + virtual Histable_type + get_type () + { + return FUNCTION; + }; + + virtual int64_t + get_size () + { + return size; + }; + + void set_comparable_name (const char *string); + void set_mangled_name (const char *string); + void set_match_name (const char *string); + + // Find any derived functions, and set their derivedNode + void findDerivedFunctions (); + void findKrakatoaDerivedFunctions (); + void add_PC_info (uint64_t offset, int lineno, SourceFile *cur_src = NULL); + void pushSrcFile (SourceFile* source, int lineno); + SourceFile *popSrcFile (); + int func_cmp (Function *func, SourceFile *srcContext = NULL); + void copy_PCInfo (Function *f); + DbeLine *mapPCtoLine (uint64_t addr, SourceFile *src = NULL); + DbeInstr *mapLineToPc (DbeLine *dbeLine); + DbeInstr *find_dbeinstr (int flag, uint64_t addr); + DbeInstr *create_hide_instr (DbeInstr *instr); + uint64_t find_previous_addr (uint64_t addr); + SourceFile *getDefSrc (); + char *getDefSrcName (); + void setDefSrc (SourceFile *sf); + void setLineFirst (int lineno); + Vector<SourceFile*> *get_sources (); + + char * + get_mangled_name () + { + return mangled_name; + } + + char * + get_match_name () + { + return match_name; + } + + inline Function * + cardinal () + { + return alias ? alias : this; + } + + unsigned int flags; // FUNC_FLAG_* + Module *module; // pointer to module containing source + int line_first; // range of line numbers for function + int line_last; + int64_t size; // size of the function in bytes + uint64_t save_addr; // used for detection of leaf routines + DbeInstr *derivedNode; // If derived from another function + bool isOutlineFunction; // if outline (save assumed) + unsigned int chksum; // check sum of instructions + char *img_fname; // file containing function image + uint64_t img_offset; // file offset of the image + SourceFile *curr_srcfile; + DbeLine *defaultDbeLine; + Function *usrfunc; // User function + Function *alias; + bool isUsed; + bool isHideFunc; + SourceFile *def_source; + Function *indexStabsLink; // correspondent function for the .o file + Symbol *elfSym; + InlinedSubr *inlinedSubr; + int inlinedSubrCnt; + +private: + DbeInstr **instHTable; // hash table for DbeInstr + int *addrIndexHTable; // hash table for addrs index + void setSource (); + PCInfo *lookup_PCInfo (uint64_t offset); + SrcInfo *new_srcInfo (); + + char *mangled_name; + char *match_name; // mangled name, with globalization stripped + char *comparable_name; // demangled name, with globalization and blanks stripped + char *name_buf; + NameFormat current_name_format; + Vector<PCInfo*> *linetab; + Vector<DbeInstr*> *instrs; + Vector<uint64_t> *addrs; + uint64_t instr_id; + Vector<SourceFile*> *sources; + SrcInfo *curr_srcinfo; // the current source stack of the function + SrcInfo *srcinfo_list; // master list for SrcInfo +}; + +class JMethod : public Function +{ +public: + JMethod (uint64_t _id); + virtual ~JMethod (); + virtual void set_name (char *); + virtual uint64_t get_addr (); + + void + set_addr (Vaddr _addr) + { + addr = _addr; + } + + uint64_t + get_mid () + { + return mid; + } + + void + set_mid (uint64_t _mid) + { + mid = _mid; + } + + char * + get_signature () + { + return signature; + } + + void + set_signature (const char *s) + { + signature = dbe_strdup (s); + } + + // Returns true if func's name matches method's as its JNI implementation + bool jni_match (Function *func); + +private: + uint64_t mid; + Vaddr addr; + char *signature; + Function *jni_function; +}; + +#endif /* _DBE_FUNCTION_H */ diff --git a/gprofng/src/HashMap.h b/gprofng/src/HashMap.h new file mode 100644 index 0000000..f66a6fe --- /dev/null +++ b/gprofng/src/HashMap.h @@ -0,0 +1,435 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +// java.util.HashMap + +#ifndef _DBE_HASHMAP_H +#define _DBE_HASHMAP_H + +#include "vec.h" +#include "util.h" +#include "StringBuilder.h" +#include "Histable.h" +#include "MemObject.h" + +template <typename Key_t> inline int get_hash_code (Key_t a); +template <typename Key_t> inline bool is_equals (Key_t a, Key_t b); +template <typename Key_t> inline Key_t copy_key (Key_t a); +template <typename Key_t> inline void delete_key (Key_t a); + +// Specialization for char* +template<> inline int +get_hash_code (char *a) +{ + return (int) (crc64 (a, strlen (a)) & 0x7fffffff); +} + +template<> inline bool +is_equals (char *a, char *b) +{ + return dbe_strcmp (a, b) == 0; +} + +template<> inline char * +copy_key (char *a) +{ + return dbe_strdup (a); +} + +template<> inline void +delete_key (char *a) +{ + return free (a); +} + +template<> inline int +get_hash_code (uint64_t a) +{ + return (int) (a & 0x7fffffff); +} + +template<> inline bool +is_equals (uint64_t a, uint64_t b) +{ + return a == b; +} + +template<> inline uint64_t +copy_key (uint64_t a) +{ + return a; +} + +template<> inline void +delete_key (uint64_t a) +{ + a = a; +} + +template<> inline int +get_hash_code (Histable* a) +{ + return (int) (a->id & 0x7fffffff); +} + +template<> inline bool +is_equals (Histable* a, Histable* b) +{ + return a == b; +} + +template<> inline Histable* +copy_key (Histable* a) +{ + return a; +} + +template<> inline void +delete_key (Histable* a) +{ + a->id = a->id; +} + +template<> inline int +get_hash_code (MemObj* a) +{ + return (int) (a->id & 0x7fffffff); +} + +template<> inline bool +is_equals (MemObj* a, MemObj* b) +{ + return a == b; +} + +template<> inline MemObj* +copy_key (MemObj* a) +{ + return a; +} + +template<> inline void +delete_key (MemObj* a) +{ + a->id = a->id; +} + +template <typename Key_t, typename Value_t> +class HashMap +{ +public: + HashMap (int initialCapacity = 0); + + ~HashMap () + { + clear (); + delete vals; + delete[] hashTable; + } + + Value_t put (Key_t key, Value_t val); + Value_t get (Key_t key); + Value_t get (Key_t key, Value_t val); // Create a new map if key is not here + void clear (); + Value_t remove (Key_t); + Vector<Value_t> *values (Key_t key); // Return a list of values for 'key' + + bool + containsKey (Key_t key) + { + Value_t p = get (key); + return p != NULL; + }; + + Vector<Value_t> * + values () + { + return vals; + } + + void + reset () + { + clear (); + } + + int + get_phaseIdx () + { + return phaseIdx; + } + + void + set_phaseIdx (int phase) + { + if (phase == 0) clear (); + phaseIdx = phase; + } + char *dump (); + +private: + + enum + { + HASH_SIZE = 511, + MAX_HASH_SIZE = 1048575 + }; + + typedef struct Hash + { + Key_t key; + Value_t val; + struct Hash *next; + } Hash_t; + + void resize (); + + int + hashCode (Key_t key) + { + return get_hash_code (key) % hash_sz; + } + + bool + equals (Key_t a, Key_t b) + { + return is_equals (a, b); + } + + Key_t + copy (Key_t key) + { + return copy_key (key); + } + + Hash_t **hashTable; + Vector<Value_t> *vals; + int phaseIdx; + int hash_sz; + int nelem; +}; + +template <typename Key_t, typename Value_t> +HashMap<Key_t, Value_t> +::HashMap (int initialCapacity) +{ + if (initialCapacity > 0) + vals = new Vector<Value_t>(initialCapacity); + else + vals = new Vector<Value_t>(); + phaseIdx = 0; + nelem = 0; + hash_sz = HASH_SIZE; + hashTable = new Hash_t*[hash_sz]; + for (int i = 0; i < hash_sz; i++) + hashTable[i] = NULL; +} + +template <typename Key_t, typename Value_t> +void +HashMap<Key_t, Value_t>::clear () +{ + vals->reset (); + phaseIdx = 0; + nelem = 0; + for (int i = 0; i < hash_sz; i++) + { + Hash_t *next; + for (Hash_t *p = hashTable[i]; p; p = next) + { + next = p->next; + delete_key (p->key); + delete p; + } + hashTable[i] = NULL; + } +} + +template <typename Key_t, typename Value_t> +void +HashMap<Key_t, Value_t>::resize () +{ + int old_hash_sz = hash_sz; + hash_sz = old_hash_sz * 2 + 1; + Hash_t **old_hash_table = hashTable; + hashTable = new Hash_t*[hash_sz]; + for (int i = 0; i < hash_sz; i++) + hashTable[i] = NULL; + nelem = 0; + for (int i = 0; i < old_hash_sz; i++) + { + if (old_hash_table[i] != NULL) + { + Hash_t *old_p; + Hash_t *p = old_hash_table[i]; + while (p != NULL) + { + put (p->key, p->val); + old_p = p; + p = p->next; + delete old_p; + } + } + } + delete[] old_hash_table; +} + +template <typename Key_t, typename Value_t> +Value_t +HashMap<Key_t, Value_t>::get (Key_t key) +{ + int hash_code = hashCode (key); + for (Hash_t *p = hashTable[hash_code]; p; p = p->next) + if (equals (key, p->key)) + return p->val; + return NULL; +} + +template <typename Key_t, typename Value_t> +Vector<Value_t> * +HashMap<Key_t, Value_t>::values (Key_t key) +{ + Vector<Value_t> *list = new Vector<Value_t>(); + int hash_code = hashCode (key); + for (Hash_t *p = hashTable[hash_code]; p; p = p->next) + { + if (equals (key, p->key)) + list->append (p->val); + } + return list; +} + +template <typename Key_t, typename Value_t> +Value_t +HashMap<Key_t, Value_t>::get (const Key_t key, Value_t val) +{ + int hash_code = hashCode (key); + Hash_t *p, *first = NULL; + for (p = hashTable[hash_code]; p; p = p->next) + { + if (equals (key, p->key)) + { + if (first == NULL) + first = p; + if (val == p->val) + return first->val; // Always return the first value + } + } + vals->append (val); + p = new Hash_t (); + p->val = val; + p->key = copy (key); + if (first) + { + p->next = first->next; + first->next = p; + return first->val; // Always return the first value + } + else + { + p->next = hashTable[hash_code]; + hashTable[hash_code] = p; + return val; + } +} + +template <typename Key_t, typename Value_t> +Value_t +HashMap<Key_t, Value_t>::remove (Key_t key) +{ + int hash_code = hashCode (key); + Value_t val = NULL; + for (Hash_t *prev = NULL, *p = hashTable[hash_code]; p != NULL;) + { + if (equals (key, p->key)) + { + if (prev == NULL) + hashTable[hash_code] = p->next; + else + prev->next = p->next; + if (val == NULL) + val = p->val; + delete_key (p->key); + delete p; + } + else + { + prev = p; + p = p->next; + } + } + return val; +} + +template <typename Key_t, typename Value_t> +Value_t +HashMap<Key_t, Value_t>::put (Key_t key, Value_t val) +{ + int hash_code = hashCode (key); + vals->append (val); + for (Hash_t *p = hashTable[hash_code]; p != NULL; p = p->next) + { + if (equals (key, p->key)) + { + Value_t v = p->val; + p->val = val; + return v; + } + } + Hash_t *p = new Hash_t (); + p->val = val; + p->key = copy (key); + p->next = hashTable[hash_code]; + hashTable[hash_code] = p; + nelem++; + if (nelem == hash_sz) + resize (); + return val; +} + +template <typename Key_t, typename Value_t> +char * +HashMap<Key_t, Value_t>::dump () +{ + StringBuilder sb; + char buf[128]; + snprintf (buf, sizeof (buf), "HashMap: size=%d ##########\n", vals->size ()); + sb.append (buf); + for (int i = 0; i < hash_sz; i++) + { + if (hashTable[i] == NULL) + continue; + snprintf (buf, sizeof (buf), "%3d:", i); + sb.append (buf); + char *s = NTXT (" "); + for (Hash_t *p = hashTable[i]; p; p = p->next) + { + sb.append (s); + s = NTXT (" "); + sb.append (p->key); + snprintf (buf, sizeof (buf), " --> 0x%p '%s'\n", + p->val, p->val->get_name ()); + sb.append (buf); + } + } + return sb.toString (); +} + +#endif // _DBE_HASHMAP_H diff --git a/gprofng/src/HeapActivity.cc b/gprofng/src/HeapActivity.cc new file mode 100644 index 0000000..943183d --- /dev/null +++ b/gprofng/src/HeapActivity.cc @@ -0,0 +1,408 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "DbeSession.h" +#include "HeapData.h" +#include "StringBuilder.h" +#include "i18n.h" +#include "util.h" +#include "HeapActivity.h" +#include "MetricList.h" +#include "Application.h" +#include "Experiment.h" +#include "DbeView.h" +#include "Exp_Layout.h" +#include "i18n.h" + +HeapActivity::HeapActivity (DbeView *_dbev) +{ + dbev = _dbev; + hDataTotal = NULL; + hDataObjs = NULL; + hDataObjsCallStack = NULL; + hasCallStack = false; + hDataCalStkMap = NULL; + hist_data_callstack_all = NULL; +} + +void +HeapActivity::reset () +{ + delete hDataTotal; + hDataTotal = NULL; + delete hDataObjsCallStack; + hDataObjsCallStack = NULL; + hasCallStack = false; + hDataObjs = NULL; + delete hDataCalStkMap; + hDataCalStkMap = NULL; + hist_data_callstack_all = NULL; +} + +void +HeapActivity::createHistItemTotals (Hist_data *hist_data, MetricList *mlist, + Histable::Type hType, bool empty) +{ + int mIndex; + Metric *mtr; + Hist_data::HistItem *hi; + HeapData *hData = NULL; + if (hDataTotal == NULL) + { + hDataTotal = new HeapData (TOTAL_HEAPNAME); + hDataTotal->setHistType (hType); + hDataTotal->setStackId (TOTAL_STACK_ID); + hDataTotal->id = 0; + } + + hData = new HeapData (hDataTotal); + hData->setHistType (hType); + hi = hist_data->append_hist_item (hData); + + Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) + { + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + + hist_data->total->value[mIndex].tag = vType; + hi->value[mIndex].tag = vType; + switch (mtype) + { + case BaseMetric::HEAP_ALLOC_BYTES: + if (!empty) + { + hist_data->total->value[mIndex].ll = hDataTotal->getAllocBytes (); + hi->value[mIndex].ll = hDataTotal->getAllocBytes (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::HEAP_ALLOC_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = hDataTotal->getAllocCnt (); + hi->value[mIndex].ll = hDataTotal->getAllocCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::HEAP_LEAK_BYTES: + if (!empty) + { + hist_data->total->value[mIndex].ll = hDataTotal->getLeakBytes (); + hi->value[mIndex].ll = hDataTotal->getLeakBytes (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::HEAP_LEAK_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = hDataTotal->getLeakCnt (); + hi->value[mIndex].ll = hDataTotal->getLeakCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + default: + break; + } + } +} + +void +HeapActivity::computeHistTotals (Hist_data *hist_data, MetricList *mlist) +{ + int mIndex; + Metric *mtr; + Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) + { + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + + hist_data->total->value[mIndex].tag = vType; + switch (mtype) + { + case BaseMetric::HEAP_ALLOC_BYTES: + hist_data->total->value[mIndex].ll = hDataTotal->getAllocBytes (); + break; + case BaseMetric::HEAP_ALLOC_CNT: + hist_data->total->value[mIndex].ll = hDataTotal->getAllocCnt (); + break; + case BaseMetric::HEAP_LEAK_BYTES: + hist_data->total->value[mIndex].ll = hDataTotal->getLeakBytes (); + break; + case BaseMetric::HEAP_LEAK_CNT: + hist_data->total->value[mIndex].ll = hDataTotal->getLeakCnt (); + break; + default: + break; + } + } +} + +void +HeapActivity::computeHistData (Hist_data *hist_data, MetricList *mlist, + Hist_data::Mode mode, Histable *selObj) +{ + + Hist_data::HistItem *hi = NULL; + + int numObjs = hDataObjs->size (); + int numMetrics = mlist->get_items ()->size (); + for (int i = 0; i < numObjs; i++) + { + HeapData *hData = hDataObjs->fetch (i); + if (mode == Hist_data::ALL) + hi = hist_data->append_hist_item (hData); + else if (mode == Hist_data::SELF) + { + if (hData->id == selObj->id) + hi = hist_data->append_hist_item (hData); + else + continue; + } + + for (int mIndex = 0; mIndex < numMetrics; mIndex++) + { + Metric *mtr = mlist->get_items ()->fetch (mIndex); + if (!mtr->is_visible () && !mtr->is_tvisible () + && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + hi->value[mIndex].tag = vType; + switch (mtype) + { + case BaseMetric::HEAP_ALLOC_BYTES: + hi->value[mIndex].ll = hData->getAllocBytes (); + break; + case BaseMetric::HEAP_ALLOC_CNT: + hi->value[mIndex].ll = hData->getAllocCnt (); + break; + case BaseMetric::HEAP_LEAK_BYTES: + hi->value[mIndex].ll = hData->getLeakBytes (); + break; + case BaseMetric::HEAP_LEAK_CNT: + hi->value[mIndex].ll = hData->getLeakCnt (); + break; + default: + break; + } + } + } +} + +Hist_data * +HeapActivity::compute_metrics (MetricList *mlist, Histable::Type type, + Hist_data::Mode mode, Histable *selObj) +{ + // it's already there, just return it + if (mode == Hist_data::ALL && type == Histable::HEAPCALLSTACK + && hist_data_callstack_all != NULL) + return hist_data_callstack_all; + + bool has_data = false; + Hist_data *hist_data = NULL; + VMode viewMode = dbev->get_view_mode (); + switch (type) + { + case Histable::HEAPCALLSTACK: + if (!hasCallStack) // It is not computed yet + computeCallStack (type, viewMode); + + // computeCallStack() creates hDataObjsCallStack + // hDataObjsCallStack contains the list of call stack objects + if (hDataObjsCallStack != NULL) + { + hDataObjs = hDataObjsCallStack; + has_data = true; + } + else + has_data = false; + + if (has_data && mode == Hist_data::ALL && hist_data_callstack_all == NULL) + { + hist_data_callstack_all = new Hist_data (mlist, type, mode, true); + hist_data = hist_data_callstack_all; + } + else if (has_data) + hist_data = new Hist_data (mlist, type, mode, false); + else + { + hist_data = new Hist_data (mlist, type, mode, false); + createHistItemTotals (hist_data, mlist, type, true); + return hist_data; + } + break; + default: + fprintf (stderr, + "HeapActivity cannot process data due to wrong Histable (type=%d) \n", + type); + abort (); + } + + if (mode == Hist_data::ALL || (mode == Hist_data::SELF && selObj->id == 0)) + createHistItemTotals (hist_data, mlist, type, false); + else + computeHistTotals (hist_data, mlist); + computeHistData (hist_data, mlist, mode, selObj); + + // Determine by which metric to sort if any + bool rev_sort = mlist->get_sort_rev (); + int sort_ind = -1; + int nmetrics = mlist->get_items ()->size (); + + for (int mind = 0; mind < nmetrics; mind++) + if (mlist->get_sort_ref_index () == mind) + sort_ind = mind; + + hist_data->sort (sort_ind, rev_sort); + hist_data->compute_minmax (); + + return hist_data; +} + +void +HeapActivity::computeCallStack (Histable::Type type, VMode viewMode) +{ + bool has_data = false; + reset (); + uint64_t stackIndex = 0; + HeapData *hData = NULL; + + delete hDataCalStkMap; + hDataCalStkMap = new DefaultMap<uint64_t, HeapData*>; + + delete hDataTotal; + hDataTotal = new HeapData (TOTAL_HEAPNAME); + hDataTotal->setHistType (type); + + // There is no call stack for total, use the index for id + hDataTotal->id = stackIndex++; + + // get the list of io events from DbeView + int numExps = dbeSession->nexps (); + + for (int k = 0; k < numExps; k++) + { + // Investigate the performance impact of processing the heap events twice. + // This is a 2*n performance issue + dbev->get_filtered_events (k, DATA_HEAPSZ); + + DataView *heapPkts = dbev->get_filtered_events (k, DATA_HEAP); + if (heapPkts == NULL) + continue; + + Experiment *exp = dbeSession->get_exp (k); + long sz = heapPkts->getSize (); + int pid = 0; + int userExpId = 0; + if (sz > 0) + { + pid = exp->getPID (); + userExpId = exp->getUserExpId (); + } + for (long i = 0; i < sz; ++i) + { + uint64_t nByte = heapPkts->getULongValue (PROP_HSIZE, i); + uint64_t stackId = (uint64_t) getStack (viewMode, heapPkts, i); + Heap_type heapType = (Heap_type) heapPkts->getIntValue (PROP_HTYPE, i); + uint64_t leaked = heapPkts->getULongValue (PROP_HLEAKED, i); + int64_t heapSize = heapPkts->getLongValue (PROP_HCUR_ALLOCS, i); + hrtime_t packetTimestamp = heapPkts->getLongValue (PROP_TSTAMP, i); + hrtime_t timestamp = packetTimestamp - exp->getStartTime () + + exp->getRelativeStartTime (); + + switch (heapType) + { + case MMAP_TRACE: + case MALLOC_TRACE: + case REALLOC_TRACE: + if (stackId != 0) + { + hData = hDataCalStkMap->get (stackId); + if (hData == NULL) + { + char *stkName = dbe_sprintf (GTXT ("Stack 0x%llx"), + (unsigned long long) stackId); + hData = new HeapData (stkName); + hDataCalStkMap->put (stackId, hData); + hData->id = (int64_t) stackId; + hData->setStackId (stackIndex); + stackIndex++; + hData->setHistType (type); + } + } + else + continue; + + hData->addAllocEvent (nByte); + hDataTotal->addAllocEvent (nByte); + hDataTotal->setAllocStat (nByte); + hDataTotal->setPeakMemUsage (heapSize, hData->getStackId (), + timestamp, pid, userExpId); + if (leaked > 0) + { + hData->addLeakEvent (leaked); + hDataTotal->addLeakEvent (leaked); + hDataTotal->setLeakStat (leaked); + } + break; + case MUNMAP_TRACE: + case FREE_TRACE: + if (hData == NULL) + hData = new HeapData (TOTAL_HEAPNAME); + hDataTotal->setPeakMemUsage (heapSize, hData->getStackId (), + timestamp, pid, userExpId); + break; + case HEAPTYPE_LAST: + break; + } + has_data = true; + } + } + + if (has_data) + { + hDataObjsCallStack = hDataCalStkMap->values ()->copy (); + hasCallStack = true; + } +} diff --git a/gprofng/src/HeapActivity.h b/gprofng/src/HeapActivity.h new file mode 100644 index 0000000..582b0b7 --- /dev/null +++ b/gprofng/src/HeapActivity.h @@ -0,0 +1,76 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _HEAPACTIVITY_H +#define _HEAPACTIVITY_H + + +#include <stdio.h> + +#include "vec.h" +#include "Histable.h" +#include "Hist_data.h" +#include "Metric.h" +#include "HeapData.h" +#include "DefaultMap.h" +#include "dbe_types.h" + +class Experiment; +class Expression; +class DataView; +class DbeView; + +class HeapActivity +{ +public: + + HeapActivity (DbeView *_dbev); + + ~HeapActivity () + { + this->reset (); + } + + void reset (void); + Hist_data *compute_metrics (MetricList *, Histable::Type, + Hist_data::Mode, Histable*); + +private: + + void computeCallStack (Histable::Type, VMode viewMode); + void createHistItemTotals (Hist_data *, MetricList *, Histable::Type, bool); + void computeHistTotals (Hist_data *, MetricList *); + void computeHistData (Hist_data *, MetricList *, Hist_data::Mode, Histable *); + + Vector<HeapData*> *hDataObjs; + Vector<HeapData*> *hDataObjsCallStack; + bool hasCallStack; + HeapData *hDataTotal; + + // list of HeapData objects using the stack id as the key + DefaultMap<uint64_t, HeapData*> *hDataCalStkMap; + + // the cached data for mode=Hist_Data::ALL + Hist_data *hist_data_callstack_all; + + DbeView *dbev; +}; + +#endif /* _HEAPACTIVITY_H */ diff --git a/gprofng/src/HeapData.cc b/gprofng/src/HeapData.cc new file mode 100644 index 0000000..5f001ad --- /dev/null +++ b/gprofng/src/HeapData.cc @@ -0,0 +1,284 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <assert.h> +#include <string.h> + +#include "util.h" +#include "HeapData.h" + +void +HeapData::init () +{ + allocBytes = 0; + leakBytes = 0; + allocCnt = 0; + leakCnt = 0; + stackId = 0; + histType = Histable::HEAPCALLSTACK; + peakMemUsage = 0; + timestamp = 0; + pid = 0; + userExpId = 0; + aSmallestBytes = _10TB; + aLargestBytes = 0; + a0KB1KBCnt = 0; + a1KB8KBCnt = 0; + a8KB32KBCnt = 0; + a32KB128KBCnt = 0; + a128KB256KBCnt = 0; + a256KB512KBCnt = 0; + a512KB1000KBCnt = 0; + a1000KB10MBCnt = 0; + a10MB100MBCnt = 0; + a100MB1GBCnt = 0; + a1GB10GBCnt = 0; + a10GB100GBCnt = 0; + a100GB1TBCnt = 0; + a1TB10TBCnt = 0; + + lSmallestBytes = _10TB; + lLargestBytes = 0; + l0KB1KBCnt = 0; + l1KB8KBCnt = 0; + l8KB32KBCnt = 0; + l32KB128KBCnt = 0; + l128KB256KBCnt = 0; + l256KB512KBCnt = 0; + l512KB1000KBCnt = 0; + l1000KB10MBCnt = 0; + l10MB100MBCnt = 0; + l100MB1GBCnt = 0; + l1GB10GBCnt = 0; + l10GB100GBCnt = 0; + l100GB1TBCnt = 0; + l1TB10TBCnt = 0; +} + +HeapData::HeapData (char *sName) +{ + stackName = dbe_strdup (sName); + peakStackIds = new Vector<uint64_t>; + peakTimestamps = new Vector<hrtime_t>; + init (); +} + +HeapData::HeapData (HeapData *hData) +{ + stackName = dbe_strdup (hData->stackName); + stackId = hData->stackId; + histType = hData->histType; + allocBytes = hData->allocBytes; + leakBytes = hData->leakBytes; + allocCnt = hData->allocCnt; + leakCnt = hData->leakCnt; + peakMemUsage = hData->peakMemUsage; + timestamp = hData->timestamp; + pid = hData->getPid (); + userExpId = hData->getUserExpId (); + peakStackIds = new Vector<uint64_t>; + Vector<uint64_t> *sIds = hData->peakStackIds; + uint64_t sId; + if (sIds != NULL) + for (int i = 0; i < sIds->size (); i++) + { + sId = sIds->fetch (i); + peakStackIds->append (sId); + } + + peakTimestamps = new Vector<hrtime_t>; + Vector<hrtime_t> *pts = hData->peakTimestamps; + hrtime_t ts; + if (pts != NULL) + for (int i = 0; i < pts->size (); i++) + { + ts = pts->fetch (i); + peakTimestamps->append (ts); + } + + aSmallestBytes = hData->aSmallestBytes; + aLargestBytes = hData->aLargestBytes; + a0KB1KBCnt = hData->a0KB1KBCnt; + a1KB8KBCnt = hData->a1KB8KBCnt; + a8KB32KBCnt = hData->a8KB32KBCnt; + a32KB128KBCnt = hData->a32KB128KBCnt; + a128KB256KBCnt = hData->a128KB256KBCnt; + a256KB512KBCnt = hData->a256KB512KBCnt; + a512KB1000KBCnt = hData->a512KB1000KBCnt; + a1000KB10MBCnt = hData->a1000KB10MBCnt; + a10MB100MBCnt = hData->a10MB100MBCnt; + a100MB1GBCnt = hData->a100MB1GBCnt; + a1GB10GBCnt = hData->a1GB10GBCnt; + a10GB100GBCnt = hData->a10GB100GBCnt; + a100GB1TBCnt = hData->a100GB1TBCnt; + a1TB10TBCnt = hData->a1TB10TBCnt; + + lSmallestBytes = hData->lSmallestBytes; + lLargestBytes = hData->lLargestBytes; + l0KB1KBCnt = hData->l0KB1KBCnt; + l1KB8KBCnt = hData->l1KB8KBCnt; + l8KB32KBCnt = hData->l8KB32KBCnt; + l32KB128KBCnt = hData->l32KB128KBCnt; + l128KB256KBCnt = hData->l128KB256KBCnt; + l256KB512KBCnt = hData->l256KB512KBCnt; + l512KB1000KBCnt = hData->l512KB1000KBCnt; + l1000KB10MBCnt = hData->l1000KB10MBCnt; + l10MB100MBCnt = hData->l10MB100MBCnt; + l100MB1GBCnt = hData->l100MB1GBCnt; + l1GB10GBCnt = hData->l1GB10GBCnt; + l10GB100GBCnt = hData->l10GB100GBCnt; + l100GB1TBCnt = hData->l100GB1TBCnt; + l1TB10TBCnt = hData->l1TB10TBCnt; +} + +HeapData::~HeapData () +{ + free (stackName); + delete peakStackIds; + delete peakTimestamps; +} + +Histable* +HeapData::convertto (Histable_type type, Histable*) +{ + return type == histType ? this : NULL; +} + +char* +HeapData::get_name (Histable::NameFormat /*_nfmt*/) +{ + return stackName; +} + +char* +HeapData::get_raw_name (Histable::NameFormat /*_nfmt*/) +{ + return stackName; +} + +void +HeapData::set_name (char* _name) +{ + free (stackName); + stackName = dbe_strdup (_name); +} + +void +HeapData::setPeakMemUsage (int64_t pmu, uint64_t sId, hrtime_t ts, int procId, int uei) +{ + if (peakMemUsage < pmu) + { + peakMemUsage = pmu; + peakStackIds->reset (); + peakStackIds->append (sId); + peakTimestamps->reset (); + peakTimestamps->append (ts); + pid = procId; + userExpId = uei; + } + else if (peakMemUsage == pmu) + { + for (int i = 0; i < peakStackIds->size (); i++) + { + uint64_t curSId = peakStackIds->fetch (i); + if (curSId == sId) + return; + } + peakStackIds->append (sId); + peakTimestamps->append (ts); + pid = procId; + userExpId = uei; + } +} + +void +HeapData::setAllocStat (int64_t nb) +{ + if (aSmallestBytes > nb) + aSmallestBytes = nb; + if (aLargestBytes < nb) + aLargestBytes = nb; + if (nb >= 0 && nb <= _1KB) + a0KB1KBCnt++; + else if (nb <= _8KB) + a1KB8KBCnt++; + else if (nb <= _32KB) + a8KB32KBCnt++; + else if (nb <= _128KB) + a32KB128KBCnt++; + else if (nb <= _256KB) + a128KB256KBCnt++; + else if (nb <= _512KB) + a256KB512KBCnt++; + else if (nb <= _1000KB) + a512KB1000KBCnt++; + else if (nb <= _10MB) + a1000KB10MBCnt++; + else if (nb <= _100MB) + a10MB100MBCnt++; + else if (nb <= _1GB) + a100MB1GBCnt++; + else if (nb <= _10GB) + a1GB10GBCnt++; + else if (nb <= _100GB) + a10GB100GBCnt++; + else if (nb <= _1TB) + a100GB1TBCnt++; + else if (nb <= _10TB) + a1TB10TBCnt++; +} + +void +HeapData::setLeakStat (int64_t nb) +{ + if (lSmallestBytes > nb) + lSmallestBytes = nb; + if (lLargestBytes < nb) + lLargestBytes = nb; + if (nb >= 0 && nb <= _1KB) + l0KB1KBCnt++; + else if (nb <= _8KB) + l1KB8KBCnt++; + else if (nb <= _32KB) + l8KB32KBCnt++; + else if (nb <= _128KB) + l32KB128KBCnt++; + else if (nb <= _256KB) + l128KB256KBCnt++; + else if (nb <= _512KB) + l256KB512KBCnt++; + else if (nb <= _1000KB) + l512KB1000KBCnt++; + else if (nb <= _10MB) + l1000KB10MBCnt++; + else if (nb <= _100MB) + l10MB100MBCnt++; + else if (nb <= _1GB) + l100MB1GBCnt++; + else if (nb <= _10GB) + l1GB10GBCnt++; + else if (nb <= _100GB) + l10GB100GBCnt++; + else if (nb <= _1TB) + l100GB1TBCnt++; + else if (nb <= _10TB) + l1TB10TBCnt++; +} diff --git a/gprofng/src/HeapData.h b/gprofng/src/HeapData.h new file mode 100644 index 0000000..7ff68e9 --- /dev/null +++ b/gprofng/src/HeapData.h @@ -0,0 +1,450 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _HEAPDATA_H +#define _HEAPDATA_H + +#include "gp-defs.h" +#include "gp-time.h" + +#include "vec.h" +#include "data_pckts.h" +#include "Histable.h" + +#define TOTAL_HEAPNAME NTXT("<Total>") +#define TOTAL_STACK_ID 0 +#define _1KB 1024 +#define _8KB 8192 +#define _32KB 32768 +#define _128KB 131072 +#define _256KB 262144 +#define _512KB 524288 +#define _1000KB 1048576 +#define _10MB 10485760 +#define _100MB 104857600 +#define _1GB 1073741824 +#define _10GB 10737418240 +#define _100GB 107374182400 +#define _1TB 1099511627776 +#define _10TB 10995116277760 + +class HeapData : public Histable +{ + friend class HeapActivity; +public: + HeapData (char *sName); + HeapData (HeapData *hData); + ~HeapData (); + char *get_raw_name (Histable::NameFormat nfmt); + void init (); + void setStackName (char* sName); + void setPeakMemUsage (int64_t pmu, uint64_t sId, hrtime_t ts, int procId, int uei); + + virtual char *get_name (Histable::NameFormat nfmt); + virtual void set_name (char * _name); + virtual Histable *convertto (Histable_type, Histable* = NULL); + + virtual Histable_type + get_type () + { + return histType; + } + + virtual uint64_t + get_addr () + { + return stackId; + } + + uint64_t + get_index () + { + return stackId; + } + + char * + getStackName () + { + return stackName; + } + + void + addAllocEvent (uint64_t nb) + { + allocBytes += nb; + allocCnt++; + } + + uint64_t + getAllocBytes () + { + return allocBytes; + } + + int32_t + getAllocCnt () + { + return allocCnt; + } + + void + addLeakEvent (uint64_t nb) + { + leakBytes += nb; + leakCnt++; + } + + uint64_t + getLeakBytes () + { + return leakBytes; + } + + int32_t + getLeakCnt () + { + return leakCnt; + } + + void + setStackId (uint64_t sId) + { + stackId = sId; + } + + uint64_t + getStackId () + { + return stackId; + } + + void + setTimestamp (hrtime_t ts) + { + timestamp = ts; + } + + hrtime_t + getTimestamp () + { + return timestamp; + } + + void + setHistType (Histable::Type hType) + { + histType = hType; + } + + Histable::Type + getHistType () + { + return histType; + } + + int64_t + getPeakMemUsage () + { + return peakMemUsage; + } + + Vector<uint64_t> * + getPeakStackIds () + { + return peakStackIds; + } + + Vector<hrtime_t> * + getPeakTimestamps () + { + return peakTimestamps; + } + + void + setPid (int procId) + { + pid = procId; + } + + int + getPid () + { + return pid; + } + + void + setUserExpId (int uei) + { + userExpId = uei; + } + + int + getUserExpId () + { + return userExpId; + } + + void setAllocStat (int64_t nb); + + int64_t + getASmallestBytes () + { + return aSmallestBytes; + } + + int64_t + getALargestBytes () + { + return aLargestBytes; + } + + int32_t + getA0KB1KBCnt () + { + return a0KB1KBCnt; + } + + int32_t + getA1KB8KBCnt () + { + return a1KB8KBCnt; + } + + int32_t + getA8KB32KBCnt () + { + return a8KB32KBCnt; + } + + int32_t + getA32KB128KBCnt () + { + return a32KB128KBCnt; + } + + int32_t + getA128KB256KBCnt () + { + return a128KB256KBCnt; + } + + int32_t + getA256KB512KBCnt () + { + return a256KB512KBCnt; + } + + int32_t + getA512KB1000KBCnt () + { + return a512KB1000KBCnt; + } + + int32_t + getA1000KB10MBCnt () + { + return a1000KB10MBCnt; + } + + int32_t + getA10MB100MBCnt () + { + return a10MB100MBCnt; + } + + int32_t + getA100MB1GBCnt () + { + return a100MB1GBCnt; + } + + int32_t + getA1GB10GBCnt () + { + return a1GB10GBCnt; + } + + int32_t + getA10GB100GBCnt () + { + return a10GB100GBCnt; + } + + int32_t + getA100GB1TBCnt () + { + return a100GB1TBCnt; + } + + int32_t + getA1TB10TBCnt () + { + return a1TB10TBCnt; + } + + void setLeakStat (int64_t nb); + + int64_t + getLSmallestBytes () + { + return lSmallestBytes; + } + + int64_t + getLLargestBytes () + { + return lLargestBytes; + } + + int32_t + getL0KB1KBCnt () + { + return l0KB1KBCnt; + } + + int32_t + getL1KB8KBCnt () + { + return l1KB8KBCnt; + } + + int32_t + getL8KB32KBCnt () + { + return l8KB32KBCnt; + } + + int32_t + getL32KB128KBCnt () + { + return l32KB128KBCnt; + } + + int32_t + getL128KB256KBCnt () + { + return l128KB256KBCnt; + } + + int32_t + getL256KB512KBCnt () + { + return l256KB512KBCnt; + } + + int32_t + getL512KB1000KBCnt () + { + return l512KB1000KBCnt; + } + + int32_t + getL1000KB10MBCnt () + { + return l1000KB10MBCnt; + } + + int32_t + getL10MB100MBCnt () + { + return l10MB100MBCnt; + } + + int32_t + getL100MB1GBCnt () + { + return l100MB1GBCnt; + } + + int32_t + getL1GB10GBCnt () + { + return l1GB10GBCnt; + } + + int32_t + getL10GB100GBCnt () + { + return l10GB100GBCnt; + } + + int32_t + getL100GB1TBCnt () + { + return l100GB1TBCnt; + } + + int32_t + getL1TB10TBCnt () + { + return l1TB10TBCnt; + } + +private: + char *stackName; // stack name + uint64_t allocBytes; // The total bytes allocated + uint64_t leakBytes; // The total bytes leaked + int32_t allocCnt; // The alloc count + int32_t leakCnt; // The leak count + Histable::Type histType; // The Histable type: HEAPCALLSTACK + int64_t peakMemUsage; // Keep track of peak memory usage + uint64_t stackId; + Vector<uint64_t> *peakStackIds; // The peak memory usage stack ids + hrtime_t timestamp; + Vector<hrtime_t> *peakTimestamps; // The peak data + int pid; // The process id + int userExpId; // The experiment id + + int64_t aSmallestBytes; + int64_t aLargestBytes; + int32_t a0KB1KBCnt; + int32_t a1KB8KBCnt; + int32_t a8KB32KBCnt; + int32_t a32KB128KBCnt; + int32_t a128KB256KBCnt; + int32_t a256KB512KBCnt; + int32_t a512KB1000KBCnt; + int32_t a1000KB10MBCnt; + int32_t a10MB100MBCnt; + int32_t a100MB1GBCnt; + int32_t a1GB10GBCnt; + int32_t a10GB100GBCnt; + int32_t a100GB1TBCnt; + int32_t a1TB10TBCnt; + + int64_t lSmallestBytes; + int64_t lLargestBytes; + int32_t l0KB1KBCnt; + int32_t l1KB8KBCnt; + int32_t l8KB32KBCnt; + int32_t l32KB128KBCnt; + int32_t l128KB256KBCnt; + int32_t l256KB512KBCnt; + int32_t l512KB1000KBCnt; + int32_t l1000KB10MBCnt; + int32_t l10MB100MBCnt; + int32_t l100MB1GBCnt; + int32_t l1GB10GBCnt; + int32_t l10GB100GBCnt; + int32_t l100GB1TBCnt; + int32_t l1TB10TBCnt; +}; + +#endif diff --git a/gprofng/src/HeapMap.cc b/gprofng/src/HeapMap.cc new file mode 100644 index 0000000..8e6c009 --- /dev/null +++ b/gprofng/src/HeapMap.cc @@ -0,0 +1,325 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "util.h" +#include "HeapMap.h" + +#define HEAPCHUNKSZ 1024 // number of HeapObj's in a chunk +#define HEAPCHAINS 9192 // number of address-based chains +#define hash(x) (((x) >> 6) % HEAPCHAINS) + +typedef struct HeapObj +{ + uint64_t addr; + uint64_t size; + long val; + HeapObj *next; +} HeapObj; + +typedef struct HeapChunk +{ + void *addr; + HeapChunk *next; +} HeapChunk; + +HeapMap::HeapMap () +{ + chunks = NULL; + empty = NULL; + chain = new HeapObj*[HEAPCHAINS]; + for (int i = 0; i < HEAPCHAINS; i++) + chain[i] = NULL; + + mmaps = new HeapObj; + mmaps->addr = (uint64_t) 0; + mmaps->size = (uint64_t) 0; + mmaps->val = -1; + mmaps->next = NULL; +} + +HeapMap::~HeapMap () +{ + // free up all the chunks + HeapChunk *c = chunks; + while (c != NULL) + { + HeapChunk *next = c->next; + delete c; + c = next; + } + delete[] chain; + delete mmaps; +} + +void +HeapMap::allocate (uint64_t addr, long val) +{ + // get a HeapObj, and set it up for the allocated block + HeapObj *incoming = getHeapObj (); + incoming->addr = addr; + incoming->val = val; + incoming->next = NULL; + + // determine which chain the block belongs on + int ichain = (int) hash (addr); + + // if this is the first, just set it up and return + if (chain[ichain] == NULL) + { + chain[ichain] = incoming; + return; + } + // chain is non-empty -- find the slot for this one + // chain is maintained in reverse address order + HeapObj *prev = NULL; + HeapObj *next = chain[ichain]; + + for (;;) + { + if ((next == NULL) || (next->addr < incoming->addr)) + { + // we've found the spot + incoming->next = next; + if (prev == NULL) + chain[ichain] = incoming; + else + prev->next = incoming; + return; + } + if (next->addr == incoming->addr) + { + // error -- two blocks with same address active + // ignore the new one + releaseHeapObj (incoming); + return; + } + // not yet, keep looking + prev = next; + next = next->next; + } +} + +long +HeapMap::deallocate (uint64_t addr) +{ + int ichain = (int) hash (addr); + HeapObj *cur = chain[ichain]; + HeapObj *prev = NULL; + while (cur != NULL) + { + if (cur->addr == addr) + { + // we've found the block + long val = cur->val; + + // delete the block from the chain + if (prev == NULL) + chain[ichain] = cur->next; + else + prev->next = cur->next; + releaseHeapObj (cur); + return val; + } + prev = cur; + cur = cur->next; + } + + // block not found + return 0; +} + +void +HeapMap::allocateChunk () +{ + // allocate the memory + HeapChunk *c = new HeapChunk; + HeapObj *newc = new HeapObj[HEAPCHUNKSZ]; + + // set up the chunk, and queue it for destructor's use + c->addr = (void *) newc; + c->next = chunks; + chunks = c; + + // Initialize the HeapObj's in the chunk to one chain + // last entry is left NULL, terminating the chain + for (int i = 0; i < (HEAPCHUNKSZ - 1); i++) + newc[i].next = newc + i + 1; + newc[HEAPCHUNKSZ - 1].next = NULL; + + // put that chain on the empty queue + empty = newc; +} + +HeapObj * +HeapMap::getHeapObj () +{ + if (empty == NULL) + allocateChunk (); + HeapObj *ret = empty; + empty = ret->next; + return ret; +} + +void +HeapMap::releaseHeapObj (HeapObj *obj) +{ + obj->next = empty; + empty = obj; +} + +UnmapChunk* +HeapMap::mmap (uint64_t addr, int64_t size, long val) +{ + HeapObj *incoming = getHeapObj (); + incoming->addr = addr; + incoming->size = size; + incoming->val = val; + incoming->next = NULL; + UnmapChunk* list = process (incoming, addr, size); + return list; +} + +UnmapChunk* +HeapMap::munmap (uint64_t addr, int64_t size) +{ + UnmapChunk* list = process (NULL, addr, size); + return list; +} + +UnmapChunk* +HeapMap::process (HeapObj *obj, uint64_t addr, int64_t size) +{ + // Some graphics are used to clarify the alignment of mmap regions + // obj, shown as consecutive pages: "NNNNNN" + // cur, shown as consecutive pages: "CCCCCC" + + // Find the first overlap, start of N is before end of C. Examples: + // CCCCC + // NNNNN + // or + // CCCCC + // NNN + // or + // CCCCC + // NNNNN + // or + // CCCCC + // NNNNNNN + HeapObj *prev = mmaps; + HeapObj *cur = prev->next; + while (cur) + { + if (addr < cur->addr + cur->size) + break; + prev = cur; + cur = prev->next; + } + + // None found + if (cur == NULL) + { + prev->next = obj; + return NULL; + } + + UnmapChunk* list = NULL; + if (addr > cur->addr) + { + if (cur->addr + cur->size <= addr + size) + { + // Process overlap on the left (low side) of new allocation + // New range: N-start to C-end (gets freed below) + prev = cur; + cur = getHeapObj (); + cur->addr = addr; + cur->size = prev->addr + prev->size - addr; + cur->val = prev->val; + cur->next = prev->next; + + // Truncate range: C-start to N-start + prev->size = addr - prev->addr; + } + else + { + // Process new allocation that fits completely within old allocation + // New range: N-start to N-end (freed below) + int64_t c_end = cur->addr + cur->size; + prev = cur; + cur = getHeapObj (); + cur->addr = addr; + cur->size = size; + cur->val = prev->val; + cur->next = prev->next; + + // Truncate range: C-start to N-start + prev->size = addr - prev->addr; + + // New range: N-end to C-end. + HeapObj *next = getHeapObj (); + next->addr = addr + size; + next->size = c_end - next->addr; + next->val = cur->val; + next->next = cur->next; + cur->next = next; + } + } + + // Process all full overlaps. + // Copy details of cur to UnmapChunk list, remove cur from mmaps + while (cur && cur->addr + cur->size <= addr + size) + { + + UnmapChunk* uc = new UnmapChunk; + uc->val = cur->val; + uc->size = cur->size; + uc->next = list; + list = uc; + + HeapObj *t = cur; + cur = cur->next; + releaseHeapObj (t); + } + + if (cur && cur->addr < addr + size) + { + // Process the last overlap (right side of new allocation) + // Copy details of cur to UnmapChunk list + UnmapChunk* uc = new UnmapChunk; + uc->val = cur->val; + uc->size = addr + size - cur->addr; + uc->next = list; + list = uc; + + // Truncate left side of cur + cur->size -= uc->size; + cur->addr = addr + size; + } + + // Insert new allocation + if (obj) + { + prev->next = obj; + obj->next = cur; + } + else + prev->next = cur; + return list; +} diff --git a/gprofng/src/HeapMap.h b/gprofng/src/HeapMap.h new file mode 100644 index 0000000..c46129d --- /dev/null +++ b/gprofng/src/HeapMap.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _HEAPMAP_H +#define _HEAPMAP_H + +#include "dbe_types.h" +#include "vec.h" + +struct HeapObj; +struct HeapChunk; + +typedef struct UnmapChunk +{ + long val; + int64_t size; + UnmapChunk *next; +} UnmapChunk; + +class HeapMap +{ +public: + HeapMap (); + ~HeapMap (); + void allocate (uint64_t addr, long val); + long deallocate (uint64_t addr); + UnmapChunk *mmap (uint64_t addr, int64_t size, long val); + UnmapChunk *munmap (uint64_t addr, int64_t size); + +private: + void allocateChunk (); + HeapObj *getHeapObj (); + void releaseHeapObj (HeapObj*); + UnmapChunk *process (HeapObj *obj, uint64_t addr, int64_t size); + + HeapChunk *chunks; + HeapObj *empty; + HeapObj **chain; + HeapObj *mmaps; +}; + +#endif /* _HEAPMAP_H */ diff --git a/gprofng/src/Hist_data.cc b/gprofng/src/Hist_data.cc new file mode 100644 index 0000000..4412203 --- /dev/null +++ b/gprofng/src/Hist_data.cc @@ -0,0 +1,1886 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <assert.h> + +#include "util.h" +#include "DefaultMap.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "DataObject.h" +#include "Function.h" +#include "Hist_data.h" +#include "Histable.h" +#include "MemObject.h" +#include "IndexObject.h" +#include "MetricList.h" +#include "Metric.h" +#include "Module.h" +#include "LoadObject.h" +#include "Settings.h" +#include "StringBuilder.h" +#include "ExpGroup.h" +#include "PathTree.h" +#include "DbeView.h" +#include "FileData.h" + +Hist_data::HistItem::HistItem (long n) +{ + obj = NULL; + type = 0; + size = n; + value = new TValue[n]; + memset (value, 0, sizeof (TValue) * n); +} + +Hist_data::HistItem::~HistItem () +{ + for (long i = 0; i < size; i++) + if (value[i].tag == VT_LABEL) + free (value[i].l); + delete[] value; +} + +long +Hist_data::size () +{ + // If the data values have not been computed, do so + // Return the total number of items + return hist_items->size (); +} + +Hist_data::HistItem * +Hist_data::fetch (long index) +{ + return (index < VecSize (hist_items)) ? hist_items->get (index) : NULL; +} + +int +Hist_data::sort_compare (HistItem *hi_1, HistItem *hi_2, Sort_type stype, + long ind, Hist_data *hdata) +{ + // Sort the data depending upon order and type + int result = 0; + Histable::Type type = hi_1->obj->get_type (); + if (stype == ALPHA) + { + if (type != Histable::MEMOBJ && type != Histable::INDEXOBJ + && type != Histable::IOACTVFD && type != Histable::IOACTFILE + && type != Histable::IOCALLSTACK) + { + char *nm1 = hi_1->obj->get_name (); + char *nm2 = hi_2->obj->get_name (); + if (nm1 != NULL && nm2 != NULL) + result = strcoll (nm1, nm2); + } + else if (type == Histable::IOCALLSTACK || type == Histable::IOACTVFD + || type == Histable::IOACTFILE) + { + uint64_t idx1, idx2; + idx1 = ((FileData *) (hi_1->obj))->get_index (); + idx2 = ((FileData *) (hi_2->obj))->get_index (); + if (idx1 < idx2) + result = -1; + else if (idx1 > idx2) + result = 1; + else + result = 0; + } + else + { + // for memory and index objects, "alphabetic" is really by index + // <Total> has index -2, and always comes first + // <Unknown> has index -1, and always comes second. + uint64_t i1, i2; + bool needsStringCompare = false; + if (type == Histable::MEMOBJ) + { + i1 = ((MemObj *) (hi_1->obj))->get_index (); + i2 = ((MemObj *) (hi_2->obj))->get_index (); + } + else if (type == Histable::INDEXOBJ) + { + i1 = ((IndexObject *) (hi_1->obj))->get_index (); + i2 = ((IndexObject *) (hi_2->obj))->get_index (); + needsStringCompare = + ((IndexObject *) (hi_1->obj))->requires_string_sort (); + } + else + abort (); + if (i1 == (uint64_t) - 2) + result = -1; + else if (i2 == (uint64_t) - 2) + result = 1; + else if (i1 == (uint64_t) - 1) + result = -1; + else if (i2 == (uint64_t) - 1) + result = 1; + else if (needsStringCompare) + { + char *nm1 = hi_1->obj->get_name (); + char *nm2 = hi_2->obj->get_name (); + if (nm1 != NULL && nm2 != NULL) + { + char nm1_lead = nm1[0]; + char nm2_lead = nm2[0]; + // put "(unknown)" and friends at end of list + if (nm1_lead == '(' && nm1_lead != nm2_lead) + result = 1; + else if (nm2_lead == '(' && nm1_lead != nm2_lead) + result = -1; + else + result = strcoll (nm1, nm2); + } + } + if (result == 0) + { // matches, resolve by index + if (i1 < i2) + result = -1; + else if (i1 > i2) + result = 1; + } + } + } + else if (stype == AUX) + { + switch (type) + { + case Histable::INSTR: + { + DbeInstr *instr1 = (DbeInstr*) hi_1->obj; + DbeInstr *instr2 = (DbeInstr*) hi_2->obj; + result = instr1 ? instr1->pc_cmp (instr2) : instr2 ? 1 : 0; + break; + } + case Histable::LINE: + { + DbeLine *dbl1 = (DbeLine*) hi_1->obj; + DbeLine *dbl2 = (DbeLine*) hi_2->obj; + result = dbl1->line_cmp (dbl2); + } + break; + default: + assert (0); + } + } + else if (stype == VALUE) + { + Metric *m = hdata->get_metric_list ()->get (ind); + if ((m->get_visbits () & (VAL_DELTA | VAL_RATIO)) != 0) + { + TValue v1, v2; + int first_ind = hdata->hist_metrics[ind].indFirstExp; + if ((m->get_visbits () & VAL_DELTA) != 0) + { + v1.make_delta (hi_1->value + ind, hi_1->value + first_ind); + v2.make_delta (hi_2->value + ind, hi_2->value + first_ind); + } + else + { + v1.make_ratio (hi_1->value + ind, hi_1->value + first_ind); + v2.make_ratio (hi_2->value + ind, hi_2->value + first_ind); + } + result = v1.compare (&v2); + } + else + result = hi_1->value[ind].compare (hi_2->value + ind); + } + return result; +} + +int +Hist_data::sort_compare_all (const void *a, const void *b, const void *arg) +{ + HistItem *hi_1 = *((HistItem **) a); + HistItem *hi_2 = *((HistItem **) b); + + Hist_data *hdata = (Hist_data*) arg; + int result = sort_compare (hi_1, hi_2, hdata->sort_type, hdata->sort_ind, hdata); + if (hdata->sort_order == DESCEND) + result = -result; + + // Use the name as the 2d sort key (always ASCEND) + // except for MemoryObjects and IndexObjects, where the index is used + // For the Alphabetic sort + if (result == 0) + { + result = sort_compare (hi_1, hi_2, ALPHA, 0, NULL); + if (result == 0) + { + for (long i = 0, sz = hdata->metrics->size (); i < sz; i++) + { + Metric *m = hdata->metrics->get (i); + if (m->get_type () != Metric::ONAME) + { + result = sort_compare (hi_1, hi_2, VALUE, i, hdata); + if (result != 0) + { + if (hdata->sort_order == DESCEND) + result = -result; + break; + } + } + } + } + } + + // Use the address as the 3d sort key + // ( FUNCTION only, always ASCEND ) + if (result == 0 && hi_1->obj->get_type () == Histable::FUNCTION) + { + Function *f1 = (Function*) hi_1->obj; + Function *f2 = (Function*) hi_2->obj; + if (f1->get_addr () < f2->get_addr ()) + result = -1; + else if (f1->get_addr () > f2->get_addr ()) + result = 1; + } + + // Use the Histable id (ID of function, line, etc.) as the 4th sort key + // Note that IDs are not guaranteed to be stable, + if (result == 0) + { + if (hi_1->obj->id < hi_2->obj->id) + result = -1; + else if (hi_1->obj->id > hi_2->obj->id) + result = 1; + } + + if (result == 0) + return result; // shouldn't happen in most cases; line allows for breakpoint + if (hdata->rev_sort) + result = -result; + return result; +} + +int +Hist_data::sort_compare_dlayout (const void *a, const void *b, const void *arg) +{ + assert ((a != (const void *) NULL)); + assert ((b != (const void *) NULL)); + HistItem *hi_1 = *((HistItem **) a); + HistItem *hi_2 = *((HistItem **) b); + DataObject * dobj1 = (DataObject *) (hi_1->obj); + DataObject * dobj2 = (DataObject *) (hi_2->obj); + DataObject * parent1 = dobj1->parent; + DataObject * parent2 = dobj2->parent; + + Hist_data *hdata = (Hist_data*) arg; + + // are the two items members of the same object? + if (parent1 == parent2) + { + // yes + if (parent1) + { + // and they have real parents... + if (parent1->get_typename ()) + { // element + // use dobj1/dobj2 offset for sorting + uint64_t off1 = dobj1->get_offset (); + uint64_t off2 = dobj2->get_offset (); + if (off1 < off2) + return -1; + if (off1 > off2) + return 1; + return 0; + } + } + } + else + { // parents differ + if (parent1) + { + if (parent1 == dobj2) + // sorting an object and its parent: parent always first + return 1; + dobj1 = parent1; + } + if (parent2) + { + if (parent2 == dobj1) + return -1; + dobj2 = parent2; + } + } + // Either two unknowns, or two scalars, or two parents + hi_1 = hdata->hi_map->get (dobj1); + hi_2 = hdata->hi_map->get (dobj2); + return sort_compare_all ((const void*) &hi_1, (const void*) &hi_2, hdata); +} + +Hist_data::Hist_data (MetricList *_metrics, Histable::Type _type, + Hist_data::Mode _mode, bool _viewowned) +{ + hist_items = new Vector<HistItem*>; + metrics = _metrics; + nmetrics = metrics->get_items ()->size (); + type = _type; + mode = _mode; + gprof_item = new_hist_item (NULL); + viewowned = _viewowned; + sort_ind = -1; + rev_sort = false; + + Histable *tobj = new Other; + tobj->name = dbe_strdup (NTXT ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")); + minimum = new_hist_item (tobj); + + tobj = new Other; + tobj->name = dbe_strdup (NTXT ("")); + maximum = new_hist_item (tobj); + + tobj = new Other; + tobj->name = dbe_strdup (NTXT ("xxxxxxxxxxxxxxxxxxxxxx")); + maximum_inc = new_hist_item (tobj); + + tobj = new Other; + tobj->name = dbe_strdup (NTXT ("<Total>")); + total = new_hist_item (tobj); + + tobj = new Other; + tobj->name = dbe_strdup (NTXT ("XXXX Threshold XXXX")); + threshold = new_hist_item (tobj); + + hi_map = new HashMap<Histable*, HistItem*>; + callsite_mark = new DefaultMap<Histable*, int>; + hist_metrics = new Metric::HistMetric[metrics->size ()]; + for (long i = 0, sz = metrics->size (); i < sz; i++) + { + Metric::HistMetric *h = hist_metrics + i; + h->init (); + Metric *m = metrics->get (i); + if (0 != (m->get_visbits () & (VAL_DELTA | VAL_RATIO))) + h->indFirstExp = + metrics->get_listorder (m->get_cmd (), + m->get_subtype (), "EXPGRID==1"); + if (m->is_tvisible () && m->get_type () == BaseMetric::HWCNTR + && m->get_dependent_bm ()) + h->indTimeVal = + metrics->get_listorder (m->get_dependent_bm ()->get_cmd (), + m->get_subtype (), m->get_expr_spec ()); + } + status = NO_DATA; +} + +Hist_data::~Hist_data () +{ + delete[] hist_metrics; + if (hist_items) + { + hist_items->destroy (); + delete hist_items; + hist_items = NULL; + } + if (gprof_item) + { + delete gprof_item; + gprof_item = NULL; + } + if (maximum) + { + delete maximum->obj; + delete maximum; + maximum = NULL; + } + if (maximum_inc) + { + delete maximum_inc->obj; + delete maximum_inc; + maximum_inc = NULL; + } + if (minimum) + { + delete minimum->obj; + delete minimum; + minimum = NULL; + } + if (total) + { + delete total->obj; + delete total; + total = NULL; + } + if (threshold) + { + delete threshold->obj; + delete threshold; + threshold = NULL; + } + delete metrics; + delete hi_map; + delete callsite_mark; +} + +void +Hist_data::dump (char *msg, FILE *f) +{ + fprintf (f, " Hist_data dump: %s\n", msg); + fprintf (f, " %d=%d metrics\n", (int) nmetrics, (int) metrics->size ()); + for (int i = 0; i < nmetrics; i++) + { + Metric *m = metrics->get_items ()->fetch (i); + char *s = m->get_expr_spec (); + fprintf (f, " %4d %15s %4d %15s\n", i, m->get_mcmd (0), + m->get_id (), s ? s : "(NULL)"); + } + + fprintf (f, NTXT (" HistItem listing\n")); + int n = hist_items->size (); + for (int j = -1; j < n; j++) + { + HistItem *hi; + if (j < 0) + { + hi = total; + fprintf (f, NTXT (" total")); + } + else + { + hi = hist_items->fetch (j); + fprintf (f, NTXT ("%30s"), hi->obj->get_name ()); + } + for (int i = 0; i < nmetrics; i++) + { + char *stmp = hi->value[i].l; + switch (hi->value[i].tag) + { + case VT_SHORT: fprintf (f, NTXT (" %d"), hi->value[i].s); + break; + case VT_INT: fprintf (f, NTXT (" %d"), hi->value[i].i); + break; + case VT_LLONG: fprintf (f, NTXT (" %12lld"), hi->value[i].ll); + break; + case VT_FLOAT: fprintf (f, NTXT (" %f"), hi->value[i].f); + break; + case VT_DOUBLE: fprintf (f, NTXT (" %12.6lf"), hi->value[i].d); + break; + case VT_HRTIME: fprintf (f, NTXT (" %12llu"), hi->value[i].ull); + break; + case VT_LABEL: fprintf (f, NTXT (" %s"), stmp ? stmp: "(unnamed)"); + break; + case VT_ADDRESS: fprintf (f, NTXT (" %12lld"), hi->value[i].ll); + break; + case VT_OFFSET: fprintf (f, NTXT (" %p"), hi->value[i].p); + break; + case VT_ULLONG: fprintf (f, NTXT (" %12llu"), hi->value[i].ull); + break; + default: fprintf (f, NTXT (" ")); + break; + } + } + fprintf (f, NTXT ("\n")); + } +} + +void +Hist_data::sort (long ind, bool reverse) +{ + if (mode != MODL && ind != -1 && ind == sort_ind && reverse == rev_sort) + // there's no change to the sorting + return; + + if (mode == MODL) + { + sort_type = AUX; + sort_order = ASCEND; + } + else + { + if (ind == -1) + return; + Metric::Type mtype = metrics->get_items ()->fetch (ind)->get_type (); + sort_type = mtype == Metric::ONAME ? ALPHA : VALUE; + sort_order = (mtype == Metric::ONAME || mtype == Metric::ADDRESS) ? + ASCEND : DESCEND; + sort_ind = ind; + rev_sort = reverse; + } + + if (mode == Hist_data::LAYOUT || mode == Hist_data::DETAIL) + hist_items->sort ((CompareFunc) sort_compare_dlayout, this); + else + hist_items->sort ((CompareFunc) sort_compare_all, this); + + // ensure that <Total> comes first/last + char *tname = NTXT ("<Total>"); + for (int i = 0; i < hist_items->size (); ++i) + { + HistItem *hi = hist_items->fetch (i); + char *name = hi->obj->get_name (); + if (name != NULL && streq (name, tname)) + { + int idx0 = rev_sort ? hist_items->size () - 1 : 0; + if (i != idx0) + { + hist_items->remove (i); + hist_items->insert (idx0, hi); + } + break; + } + } +} + +void +Hist_data::resort (MetricList *mlist) +{ + if (mlist->get_type () != metrics->get_type ()) + if (metrics->get_type () == MET_CALL) + // wrong type of list -- internal error + abort (); + + // get the new sort order + int ind = mlist->get_sort_ref_index (); + bool reverse = mlist->get_sort_rev (); + sort (ind, reverse); +} + +void +Hist_data::compute_minmax () +{ + HistItem *hi; + int index; + + for (int mind = 0; mind < nmetrics; mind++) + { + Metric *mtr = metrics->get_items ()->fetch (mind); + if (mtr->get_subtype () == Metric::STATIC) + continue; + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + ValueTag vtype = mtr->get_vtype2 (); + + switch (vtype) + { + case VT_INT: + minimum->value[mind].tag = VT_INT; + minimum->value[mind].i = 0; + maximum->value[mind].tag = VT_INT; + maximum->value[mind].i = 0; + maximum_inc->value[mind].tag = VT_INT; + maximum_inc->value[mind].i = 0; + + Vec_loop (HistItem *, hist_items, index, hi) + { + if (metrics->get_type () == MET_SRCDIS + && callsite_mark->get (hi->obj)) + { + if (hi->value[mind].i > maximum_inc->value[mind].i) + maximum_inc->value[mind].i = hi->value[mind].i; + // ignore ones that has inclusive time for src/dis view + } + else if (hi->value[mind].i > maximum->value[mind].i) + maximum->value[mind].i = hi->value[mind].i; + if (hi->value[mind].i < minimum->value[mind].i) + minimum->value[mind].i = hi->value[mind].i; + } + break; + case VT_DOUBLE: + minimum->value[mind].tag = VT_DOUBLE; + minimum->value[mind].d = 0.0; + maximum->value[mind].tag = VT_DOUBLE; + maximum->value[mind].d = 0.0; + maximum_inc->value[mind].tag = VT_DOUBLE; + maximum_inc->value[mind].d = 0.0; + Vec_loop (HistItem*, hist_items, index, hi) + { + if (metrics->get_type () == MET_SRCDIS && callsite_mark->get (hi->obj)) + { + if (hi->value[mind].d > maximum_inc->value[mind].d) + { + maximum_inc->value[mind].d = hi->value[mind].d; + maximum_inc->value[mind].sign = hi->value[mind].sign; + } + // ignore ones that has inclusive time for src/dis view + } + else + { + if (hi->value[mind].d > maximum->value[mind].d) + { + maximum->value[mind].d = hi->value[mind].d; + maximum->value[mind].sign = hi->value[mind].sign; + } + if (hi->value[mind].d < minimum->value[mind].d) + { + minimum->value[mind].d = hi->value[mind].d; + minimum->value[mind].sign = hi->value[mind].sign; + } + } + } + break; + case VT_LLONG: + case VT_ULLONG: + case VT_ADDRESS: + minimum->value[mind].tag = vtype; + minimum->value[mind].ll = 0; + maximum->value[mind].tag = vtype; + maximum->value[mind].ll = 0; + maximum_inc->value[mind].tag = vtype; + maximum_inc->value[mind].ll = 0; + Vec_loop (HistItem*, hist_items, index, hi) + { + if (metrics->get_type () == MET_SRCDIS && callsite_mark->get (hi->obj)) + { + if (hi->value[mind].ll > maximum_inc->value[mind].ll) + { + maximum_inc->value[mind].ll = hi->value[mind].ll; + maximum_inc->value[mind].sign = hi->value[mind].sign; + } + // ignore ones that has inclusive time for src/dis view + } + else + { + if (hi->value[mind].ll > maximum->value[mind].ll) + { + maximum->value[mind].ll = hi->value[mind].ll; + maximum->value[mind].sign = hi->value[mind].sign; + } + if (hi->value[mind].ll < minimum->value[mind].ll) + { + minimum->value[mind].ll = hi->value[mind].ll; + minimum->value[mind].sign = hi->value[mind].sign; + } + } + } + break; + default: + break; + } + } +} + +Hist_data::HistItem * +Hist_data::new_hist_item (Histable *obj) +{ + long sz = get_metric_list ()->size (); + HistItem *hi = new HistItem (sz); + hi->obj = obj; + + // We precalculate all metrics as integer values + // and convert them to appropriate types later. + for (long i = 0; i < sz; i++) + { + hi->value[i].tag = VT_INT; + hi->value[i].i = 0; + } + return hi; +} + +Hist_data::HistItem * +Hist_data::new_hist_item (Histable *obj, int itype, TValue *value) +{ + long sz = get_metric_list ()->size (); + HistItem *hi = new HistItem (sz); + hi->obj = obj; + hi->type = itype; + if (value) + for (long i = 0; i < sz; i++) + hi->value[i] = value[i]; + + return hi; +} + +Hist_data::HistItem * +Hist_data::find_hist_item (Histable *obj) +{ + if (obj == NULL) + return NULL; + return hi_map->get (obj); +} + +Hist_data::HistItem * +Hist_data::append_hist_item (Histable *obj) +{ + if (obj == NULL) + return NULL; + HistItem *hi = hi_map->get (obj); + if (hi == NULL) + { + hi = new_hist_item (obj); + hist_items->append (hi); + hi_map->put (obj, hi); + } + if (status == NO_DATA) + status = SUCCESS; + return hi; +} + +void +Hist_data::append_hist_item (HistItem *hi) +{ + hist_items->append (hi); +} + +bool +Hist_data::above_threshold (HistItem* hi) +{ + bool mark = false; + int index; + Metric *mitem; + + Vec_loop (Metric*, metrics->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + switch (hi->value[index].tag) + { + case VT_DOUBLE: + if (hi->value[index].d > threshold->value[index].d) + mark = true; + break; + case VT_INT: + if (hi->value[index].i > threshold->value[index].i) + mark = true; + break; + case VT_LLONG: + if (hi->value[index].ll > threshold->value[index].ll) + mark = true; + break; + case VT_ULLONG: + if (hi->value[index].ull > threshold->value[index].ull) + mark = true; + break; + // ignoring the following cases (why?) + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + } + return mark; +} + +void +Hist_data::set_threshold (double proportion) +{ + int index; + Metric *mitem; + Vec_loop (Metric*, metrics->get_items (), index, mitem) + { + TValue *thresh = &threshold->value[index]; + TValue *mtotal = &total->value[index]; + thresh->tag = mitem->get_vtype (); + + if (mitem->get_subtype () == Metric::STATIC) + continue; + switch (thresh->tag) + { + case VT_INT: + thresh->i = (int) (proportion * (double) mtotal->i); + break; + case VT_DOUBLE: + thresh->d = proportion * mtotal->d; + break; + case VT_LLONG: + case VT_ULLONG: + thresh->ull = (unsigned long long) (proportion * (double) mtotal->ll); + break; + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + } +} + +double +Hist_data::get_percentage (double value, int mindex) +{ + double total_value; + if (value == 0.0) + return 0.0; + + // Get the total value of this sample set. + // The value must be greater than 0. + total_value = total->value[mindex].to_double (); + + // Find out what percentage of the total value this item is. + // Make sure we don't divide by zero. + if (total_value == 0.0) + return 0.0; + return value / total_value; +} + +int +Hist_data::print_label (FILE *out_file, Metric::HistMetric *hist_metric, + int space) +{ + int name_offset = 0; + StringBuilder sb, sb1, sb2, sb3; + if (space > 0) + { + char *fmt = NTXT ("%*s"); + sb.appendf (fmt, space, NTXT ("")); + sb1.appendf (fmt, space, NTXT ("")); + sb2.appendf (fmt, space, NTXT ("")); + sb3.appendf (fmt, space, NTXT ("")); + } + for (int i = 0; i < nmetrics; i++) + { + Metric *m = metrics->get (i); + Metric::HistMetric *hm = &hist_metric[i]; + int len = hm->width; + char *fmt = NTXT ("%-*s"); + if ((i > 0) && (m->get_type () == Metric::ONAME)) + { + name_offset = sb1.length (); + fmt = NTXT (" %-*s"); + len--; + } + sb.appendf (fmt, len, m->legend ? m->legend : NTXT ("")); + sb1.appendf (fmt, len, hm->legend1); + sb2.appendf (fmt, len, hm->legend2); + sb3.appendf (fmt, len, hm->legend3); + } + sb.trim (); + if (sb.length () != 0) + { + sb.toFileLn (out_file); + } + sb1.toFileLn (out_file); + sb2.toFileLn (out_file); + sb3.toFileLn (out_file); + return name_offset; +} + +void +Hist_data::print_content (FILE *out_file, Metric::HistMetric *hist_metric, int limit) +{ + StringBuilder sb; + int cnt = VecSize (hist_items); + if (cnt > limit && limit > 0) + cnt = limit; + for (int i = 0; i < cnt; i++) + { + sb.setLength (0); + print_row (&sb, i, hist_metric, NTXT (" ")); + sb.toFileLn (out_file); + } +} + +static void +append_str (StringBuilder *sb, char *s, size_t len, int vis_bits) +{ + if ((vis_bits & VAL_RATIO) != 0) + { + if (*s != 'N') // Nan + sb->appendf (NTXT ("x ")); + else + sb->appendf (NTXT (" ")); + sb->appendf (NTXT ("%*s"), (int) (len - 2), s); + } + else + sb->appendf (NTXT ("%*s"), (int) len, s); +} + +void +Hist_data::print_row (StringBuilder *sb, int row, Metric::HistMetric *hmp, char *mark) +{ + TValue res; + char buf[256]; + // Print only a list of user's metrics. ( nmetrics <= mlist->size() ) + for (long i = 0; i < nmetrics; i++) + { + // Print only a list of user's metrics. + Metric *m = metrics->get (i); + if (!m->is_any_visible ()) + continue; + Metric::HistMetric *hm = hmp + i; + int len = sb->length (); + if (m->is_tvisible ()) + { + TValue *v = get_value (&res, hist_metrics[i].indTimeVal, row); + char *s = v->to_str (buf, sizeof (buf)); + append_str (sb, s, hm->maxtime_width, m->get_visbits ()); + } + if (m->is_visible ()) + { + TValue *v = get_value (&res, i, row); + char *s = v->to_str (buf, sizeof (buf)); + if (m->get_type () == BaseMetric::ONAME) + { + sb->append (mark); + if (i + 1 == nmetrics) + sb->appendf (NTXT ("%s"), s); + else + sb->appendf (NTXT ("%-*s "), (int) hm->maxvalue_width, s); + continue; + } + else + { + if (len != sb->length ()) + sb->append (' '); + append_str (sb, s, hm->maxvalue_width, m->get_visbits ()); + } + } + if (m->is_pvisible ()) + { + if (len != sb->length ()) + sb->append (' '); + long met_ind = i; + if (m->is_tvisible () && !m->is_visible ()) + met_ind = hist_metrics[i].indTimeVal; + TValue *v = get_real_value (&res, met_ind, row); + double percent = get_percentage (v->to_double (), met_ind); + if (percent == 0.0) + // adjust to change format from xx.yy% + sb->append (NTXT (" 0. ")); + else + // adjust format below to change format from xx.yy% + sb->appendf (NTXT ("%6.2f"), (100.0 * percent)); + } + len = sb->length () - len; + if (hm->width > len && i + 1 != nmetrics) + sb->appendf (NTXT ("%*s"), (int) (hm->width - len), NTXT (" ")); + } +} + +TValue * +Hist_data::get_real_value (TValue *res, int met_index, int row) +{ + HistItem *hi = hist_items->get (row); + Metric *m = metrics->get (met_index); + if (m->get_type () == BaseMetric::ONAME) + { + res->l = dbe_strdup (hi->obj->get_name ()); + res->tag = VT_LABEL; + return res; + } + return hi->value + met_index; +} + +TValue * +Hist_data::get_value (TValue *res, int met_index, int row) +{ + HistItem *hi = hist_items->get (row); + Metric *m = metrics->get (met_index); + if ((m->get_visbits () & (VAL_DELTA | VAL_RATIO)) != 0) + { + int ind = hist_metrics[met_index].indFirstExp; + if ((m->get_visbits () & VAL_DELTA) != 0) + res->make_delta (hi->value + met_index, hi->value + ind); + else + res->make_ratio (hi->value + met_index, hi->value + ind); + return res; + } + return get_real_value (res, met_index, row); +} + +TValue * +Hist_data::get_value (TValue *res, int met_index, HistItem *hi) +{ + Metric *m = metrics->get (met_index); + if ((m->get_visbits () & (VAL_DELTA | VAL_RATIO)) != 0) + { + int ind = hist_metrics[met_index].indFirstExp; + if ((m->get_visbits () & VAL_DELTA) != 0) + res->make_delta (hi->value + met_index, hi->value + ind); + else + res->make_ratio (hi->value + met_index, hi->value + ind); + return res; + } + if (m->get_type () == BaseMetric::ONAME) + { + res->l = dbe_strdup (hi->obj->get_name ()); + res->tag = VT_LABEL; + return res; + } + return hi->value + met_index; +} + +Metric::HistMetric * +Hist_data::get_histmetrics () +{ + // find the width for each column. + for (long i = 0, sz = metrics->size (); i < sz; i++) + { + Metric *m = metrics->get (i); + Metric::HistMetric *hm = hist_metrics + i; + if (m->is_value_visible ()) + { + TValue res; + for (long i1 = 0, sz1 = VecSize(hist_items); i1 < sz1; i1++) + { + TValue *v = get_value (&res, i, i1); + long len = v->get_len (); + if (hm->maxvalue_width < len) + hm->maxvalue_width = len; + } + if ((m->get_visbits () & VAL_RATIO) != 0) + hm->maxvalue_width += 2; // "x " + } + } + + for (long i = 0, sz = metrics->size (); i < sz; i++) + { + Metric *m = metrics->get (i); + Metric::HistMetric *hm = hist_metrics + i; + if (m->is_time_visible ()) + // take a value from depended metric + hm->maxtime_width = hist_metrics[hm->indTimeVal].maxvalue_width; + m->legend_width (hm, 2); + } + return hist_metrics; +} + +void +Hist_data::update_total (Hist_data::HistItem *new_total) +{ + for (long i = 0, sz = metrics->size (); i < sz; i++) + total->value[i] = new_total->value[i]; +} + +void +Hist_data::update_max (Metric::HistMetric *hm_tmp) +{ + Metric::HistMetric *hms = get_histmetrics (); + for (int i = 0; i < nmetrics; i++) + { + Metric::HistMetric *hm = hms + i; + Metric::HistMetric *hm1 = hm_tmp + i; + if (hm1->maxtime_width < hm->maxtime_width) + hm1->maxtime_width = hm->maxtime_width; + if (hm1->maxvalue_width < hm->maxvalue_width) + hm1->maxvalue_width = hm->maxvalue_width; + } +} + +void +Hist_data::update_legend_width (Metric::HistMetric *hm_tmp) +{ + for (int i = 0; i < nmetrics; i++) + { + Metric *m = metrics->get (i); + m->legend_width (hm_tmp + i, 2); + } +} + +void +Metric::HistMetric::update_max (Metric::HistMetric *hm) +{ + if (maxtime_width < hm->maxtime_width) + maxtime_width = hm->maxtime_width; + if (maxvalue_width < hm->maxvalue_width) + maxvalue_width = hm->maxvalue_width; +} + +void +Metric::HistMetric::init () +{ + width = 0; + maxvalue_width = 0; + maxtime_width = 0; + legend1[0] = 0; + legend2[0] = 0; + legend3[0] = 0; + indFirstExp = -1; + indTimeVal = -1; +} + +size_t +Hist_data::value_maxlen (int mindex) +{ + size_t maxlen = maximum->value[mindex].get_len (); + size_t minlen = minimum->value[mindex].get_len (); + // minlen can be bigger than maxlen only for negative value + return minlen > maxlen ? minlen : maxlen; +} + +size_t +Hist_data::time_len (TValue *value, int clock) +{ + TValue tm_value; + tm_value.tag = VT_DOUBLE; + tm_value.sign = value->sign; + tm_value.d = 1.e-6 * value->ll / clock; + return tm_value.get_len (); +} + +size_t +Hist_data::time_maxlen (int mindex, int clock) +{ + size_t maxlen = time_len (&(maximum->value[mindex]), clock); + size_t minlen = time_len (&(minimum->value[mindex]), clock); + // minlen can be bigger than maxlen only for negative value + return minlen > maxlen ? minlen : maxlen; +} + +size_t +Hist_data::name_len (HistItem *item) +{ + char *name = item->obj->get_name (); + return strlen (name); +} + +size_t +Hist_data::name_maxlen () +{ + size_t res = 0; + for (long i = 0; i < size (); i++) + { + HistItem *hi = fetch (i); + size_t len = name_len (hi); + if (res < len) + res = len; + } + return res; +} + +// Returns vector of object ids for the vector of selections +// returns NULL if no valid selections +Vector<uint64_t> * +Hist_data::get_object_indices (Vector<int> *selections) +{ + // if no selections, return NULL + if (selections == NULL || selections->size () == 0) + return NULL; + + Vector<uint64_t> *indices = new Vector<uint64_t>; + for (long i = 0, sz = selections->size (); i < sz; i++) + { + int sel = selections->get (i); + HistItem *hi = hist_items->get (sel); + if (hi == NULL || hi->obj == NULL) + continue; + Vector<Histable*> *v = hi->obj->get_comparable_objs (); + for (long i1 = 0, sz1 = v ? v->size () : 0; i1 < sz1; i1++) + { + Histable *h1 = v->get (i1); + if (h1 && (indices->find_r (h1->id) < 0)) + indices->append (h1->id); + } + if (indices->find_r (hi->obj->id) < 0) + indices->append (hi->obj->id); + } + return indices; +} + +DbeInstr::DbeInstr (uint64_t _id, int _flags, Function *_func, uint64_t _addr) +{ + id = _id; + flags = _flags; + addr = _addr; + func = _func; + img_offset = addr + func->img_offset; + lineno = -1; + size = 0; + current_name_format = NA; + isUsed = false; + inlinedInd = -1; +} + +int +DbeInstr::pc_cmp (DbeInstr *instr2) +{ + int result = 0; + if (instr2 == NULL) + return -1; + + // All PC's with the Line flag go to the + // end of the list. See Module::init_index() + if (flags & PCLineFlag) + { + if (instr2->flags & PCLineFlag) + { + if (addr < instr2->addr) + result = -1; + else if (addr > instr2->addr) + result = 1; + else + result = 0; + } + else + result = 1; + } + else if (instr2->flags & PCLineFlag) + result = -1; + else if (func == instr2->func) + { + if (size == 0) + { + if (addr < instr2->addr) + result = -1; + else if (addr == instr2->addr) + result = 0; + else if (addr >= instr2->addr + instr2->size) + result = 1; + else + result = 0; + } + else if (instr2->size == 0) + { + if (addr > instr2->addr) + result = 1; + else if (addr + size <= instr2->addr) + result = -1; + else + result = 0; + } + else if (addr < instr2->addr) + result = -1; + else if (addr > instr2->addr) + result = 1; + else + result = 0; + + if (result == 0) + { + if (flags & PCTrgtFlag) + { + if (!(instr2->flags & PCTrgtFlag)) + result = -1; + } + else if (instr2->flags & PCTrgtFlag) + result = 1; + } + } + else + result = func->func_cmp (instr2->func); + return result; +} + +char * +DbeInstr::get_name (NameFormat nfmt) +{ + if (name && (nfmt == current_name_format || nfmt == Histable::NA)) + return name; + + free (name); + name = NULL; + current_name_format = nfmt; + char *fname = func->get_name (nfmt); + + if (func->flags & FUNC_FLAG_NO_OFFSET) + name = dbe_strdup (fname); + else if (addr == (uint64_t) - 1 + && func != dbeSession->get_JUnknown_Function ()) + // We use three heuristics above to recognize this special case. + // Once the original problem with bci == -1 is fixed, we don't + // need it any more. + name = dbe_sprintf (GTXT ("<Function %s: HotSpot-compiled leaf instructions>"), + fname); + else if (addr == (uint64_t) - 3) + name = dbe_sprintf (GTXT ("%s <Java native method>"), fname); + else + { + char buf[64], *typetag = NTXT (""), *alloc_typetag = NULL; + StringBuilder sb; + sb.append (fname); + if (func != dbeSession->get_JUnknown_Function ()) + { + if (addr <= 0xFFFFFFFFU) + snprintf (buf, sizeof (buf), " + 0x%08X", (unsigned int) addr); + else + snprintf (buf, sizeof (buf), " + 0x%016llX", + (unsigned long long) addr); + } + else + { + char *subname; + switch ((long int) addr) + { + case -1: + subname = GTXT ("agent error"); + break; + case -2: + subname = GTXT ("GC active"); + break; + case -3: + subname = GTXT ("unknown non-Java frame"); + break; + case -4: + subname = GTXT ("unwalkable non-Java frame"); + break; + case -5: + subname = GTXT ("unknown Java frame"); + break; + case -6: + subname = GTXT ("unwalkable Java frame"); + break; + case -7: + subname = GTXT ("unknown thread state"); + break; + case -8: + subname = GTXT ("thread in exit"); + break; + case -9: + subname = GTXT ("deopt in process ticks"); + break; + case -10: + subname = GTXT ("safepoint synchronizing ticks"); + break; + default: + subname = GTXT ("unexpected error"); + break; + } + snprintf (buf, sizeof (buf), "<%s (%d)>", subname, (int) addr); + } + sb.append (buf); + if (flags & PCTrgtFlag) + // annotate synthetic instruction + sb.append ('*'); // special distinguishing marker + + DbeLine *dbeline = mapPCtoLine (NULL); + char *str = NULL; + if (dbeline && dbeline->lineno > 0) + str = strrchr (dbeline->get_name (nfmt), ','); + if (str) + sb.append (str); + if (strlen (typetag) > 0) + { // include padding for alignment + do + { + sb.append (' '); + } + while (sb.length () < 40); + sb.append (typetag); + delete alloc_typetag; + } + if (inlinedInd >= 0) + add_inlined_info (&sb); + name = sb.toString (); + } + return name; +} + +DbeLine* +DbeInstr::mapPCtoLine (SourceFile *sf) +{ + if (inlinedInd == -1) + { + inlinedInd = -2; + for (int i = 0; i < func->inlinedSubrCnt; i++) + { + InlinedSubr *p = func->inlinedSubr + i; + if (p->level == 0) + { + if (addr < p->low_pc) + break; + if (p->contains (addr)) + { + inlinedInd = i; + break; + } + } + } + } + if (inlinedInd >= 0) + { + DbeLine *dl = func->inlinedSubr[inlinedInd].dbeLine; + return dl->sourceFile->find_dbeline (func, dl->lineno); + } + return func->mapPCtoLine (addr, sf); +} + +void +DbeInstr::add_inlined_info (StringBuilder *sb) +{ + do + { + sb->append (' '); + } + while (sb->length () < 40); + sb->append (NTXT ("<-- ")); + + InlinedSubr *last = NULL; + for (int i = inlinedInd; i < func->inlinedSubrCnt; i++) + { + InlinedSubr *p = func->inlinedSubr + i; + if (p->level == 0 && i > inlinedInd) + break; + if (!p->contains (addr)) + continue; + if (last) + { + if (last->fname) + { + sb->append (last->fname); + sb->append (' '); + } + DbeLine *dl = p->dbeLine; + sb->appendf (NTXT ("%s:%lld <-- "), get_basename (dl->sourceFile->get_name ()), (long long) dl->lineno); + } + last = p; + } + if (last) + { + if (last->fname) + { + sb->append (last->fname); + sb->append (' '); + } + } + DbeLine *dl = func->mapPCtoLine (addr, NULL); + sb->appendf ("%s:%lld ", get_basename (dl->sourceFile->get_name ()), + (long long) dl->lineno); +} + +char * +DbeInstr::get_descriptor () +{ + char *typetag = NTXT (""); + if ((flags & PCTrgtFlag) == 0) // not synthetic instruction + { // use memop descriptor, if available + Module *mod = func->module; + if (mod->hwcprof && mod->infoList) + { + long i; + inst_info_t *info = NULL; + Vec_loop (inst_info_t*, mod->infoList, i, info) + { + if (info->offset == func->img_offset + addr) break; + } + if (info) + { + long t; + datatype_t *dtype = NULL; + Vec_loop (datatype_t*, mod->datatypes, t, dtype) + { + if (dtype->datatype_id == info->memop->datatype_id) + break; + } + if (dtype && dtype->dobj) + typetag = dtype->dobj->get_name (); + } + } + } + return dbe_strdup (typetag); +} + +int64_t +DbeInstr::get_size () +{ + // Function *func = (Function*)dbeSession->get_hobj( pc ); + // Module *mod = func ? func->module : NULL; + // return mod ? mod->instrSize( func->img_offset + addr ) : 0; + return size; +} + +uint64_t +DbeInstr::get_addr () +{ + return func->get_addr () + addr; +} + +Histable * +DbeInstr::convertto (Type type, Histable *obj) +{ + Histable *res = NULL; + SourceFile *source = (SourceFile*) obj; + switch (type) + { + case INSTR: + res = this; + break; + case LINE: + res = mapPCtoLine (source); + break; + case SOURCEFILE: + res = mapPCtoLine (source); + if (res) + res = ((DbeLine*) res)->sourceFile; + break; + case FUNCTION: + res = func; + break; + default: + assert (0); + } + return res; +} + +char * +DbeEA::get_name (NameFormat) +{ + if (name == NULL) + // generate one + name = dbe_strdup (dbeSession->localized_SP_UNKNOWN_NAME); + return name; +} + +Histable * +DbeEA::convertto (Type type, Histable *obj) +{ + Histable *res = NULL; + assert (obj == NULL); + switch (type) + { + case EADDR: + res = this; + break; + case DOBJECT: + res = dobj; + break; + default: + assert (0); + } + return res; +} + +DbeLine::DbeLine (Function *_func, SourceFile *sf, int _lineno) +{ + func = _func; + lineno = _lineno; + sourceFile = sf; + id = sf->id + _lineno; + offset = 0; + size = 0; + flags = 0; + include = NULL; + dbeline_func_next = NULL; + dbeline_base = this; + current_name_format = Histable::NA; +} + +DbeLine::~DbeLine () +{ + delete dbeline_func_next; +} + +int +DbeLine::line_cmp (DbeLine *dbl) +{ + return lineno - dbl->lineno; +} + +void +DbeLine::init_Offset (uint64_t p_offset) +{ + if (offset == 0) + offset = p_offset; + if (dbeline_base && dbeline_base->offset == 0) + dbeline_base->offset = p_offset; +} + +char * +DbeLine::get_name (NameFormat nfmt) +{ + char *srcname = NULL, *basename, *fname; + + if (func == NULL) + { + if (name) + return name; + srcname = sourceFile->get_name (); + basename = get_basename (srcname); + name = dbe_sprintf (GTXT ("line %u in \"%s\""), lineno, basename); + return name; + } + + if (name && (nfmt == current_name_format || nfmt == Histable::NA)) + return name; + + current_name_format = nfmt; + free (name); + fname = func->get_name (nfmt); + if (func->flags & (FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET)) + { + name = dbe_strdup (fname); + return name; + } + + if (sourceFile) + srcname = sourceFile->get_name (); + if (!srcname || strlen (srcname) == 0) + srcname = func->getDefSrcName (); + basename = get_basename (srcname); + + if (lineno != 0) + { + if (sourceFile == func->getDefSrc ()) + name = dbe_sprintf (GTXT ("%s, line %u in \"%s\""), fname, lineno, + basename); + else + name = dbe_sprintf (GTXT ("%s, line %u in alternate source context \"%s\""), + fname, lineno, basename); + } + else if (sourceFile == NULL || (sourceFile->flags & SOURCE_FLAG_UNKNOWN) != 0) + name = dbe_sprintf (GTXT ("<Function: %s, instructions without line numbers>"), + fname); + else + name = dbe_sprintf (GTXT ("<Function: %s, instructions from source file %s>"), + fname, basename); + return name; +} + +int64_t +DbeLine::get_size () +{ + return size; +} + +uint64_t +DbeLine::get_addr () +{ + if (func == NULL && dbeline_func_next == NULL) + return (uint64_t) 0; + Function *f = func ? func : dbeline_func_next->func; + return f->get_addr () + offset; +} + +Histable * +DbeLine::convertto (Type type, Histable *obj) +{ + Histable *res = NULL; + switch (type) + { + case INSTR: + { + Function *f = (Function *) convertto (FUNCTION, NULL); + if (f) + res = f->find_dbeinstr (0, offset); + break; + } + case LINE: + res = dbeline_base; + break; + case FUNCTION: + if (func) + { + res = func; + break; + } + else + { + int not_found = 1; + for (DbeLine *dl = dbeline_base; dl; dl = dl->dbeline_func_next) + { + Function *f = dl->func; + not_found = (obj == NULL // XXXX pass dbeview as Histable* + || ((DbeView*) obj)->get_path_tree ()->get_func_nodeidx (f) == 0); + if (f && f->def_source == sourceFile && (!not_found)) + { + res = f; + break; + } + } + if (res == NULL && dbeline_func_next) + { + for (DbeLine *dl = dbeline_base; dl; dl = dl->dbeline_func_next) + { + Function *f = dl->func; + if (f && f->def_source == sourceFile) + { + res = f; + break; + } + } + } + if (res == NULL && dbeline_func_next) + res = dbeline_func_next->func; + } + break; + case SOURCEFILE: + res = (include) ? include : sourceFile; + break; + default: + assert (0); + } + return res; +} + +CStack_data::CStack_data (MetricList *_metrics) +{ + metrics = _metrics; + total = new_cstack_item (); + cstack_items = new Vector<CStack_item*>; +} + +CStack_data::CStack_item::CStack_item (long n) +{ + stack = NULL; + count = 0; + val = 0; + value = new TValue[n]; + memset (value, 0, sizeof (TValue) * n); +} + +CStack_data::CStack_item::~CStack_item () +{ + delete stack; + delete[] value; +} + +CStack_data::CStack_item * +CStack_data::new_cstack_item () +{ + int nmetrics = metrics->get_items ()->size (); + CStack_item *item = new CStack_item (nmetrics); + + // We precalculate all metrics as integer values + // and convert them to appropriate types later. + for (int i = 0; i < nmetrics; i++) + item->value[i].tag = metrics->get_items ()->fetch (i)->get_vtype (); + return item; +} + +HistableFile::HistableFile () +{ + dbeFile = NULL; + isUsed = false; +} + +Histable::Histable () +{ + name = NULL; + id = 0; + comparable_objs = NULL; + phaseCompareIdx = -1; +} + +Histable::~Histable () +{ + delete_comparable_objs (); + free (name); +} + +void +Histable::delete_comparable_objs () +{ + if (comparable_objs) + { + Vector<Histable*> *v = comparable_objs; + for (int i = 0; i < v->size (); i++) + { + Histable *h = v->fetch (i); + if (h) + { + h->comparable_objs = NULL; + h->phaseCompareIdx = phaseCompareIdx; + } + } + delete v; + } +} + +void +Histable::update_comparable_objs () +{ + if (phaseCompareIdx != ExpGroup::phaseCompareIdx) + { + phaseCompareIdx = ExpGroup::phaseCompareIdx; + delete_comparable_objs (); + } +} + +Vector<Histable*> * +Histable::get_comparable_objs () +{ + return comparable_objs; +} + +Histable * +Histable::get_compare_obj () +{ + Vector<Histable*> *v = get_comparable_objs (); + for (long i = 0, sz = VecSize (v); i < sz; i++) + { + Histable *h = v->get (i); + if (h) + return h; + } + return this; +} + +#define CASE_S(x) case x: return (char *) #x + +char * +Histable::type_to_string () +{ + switch (get_type ()) + { + CASE_S (INSTR); + CASE_S (LINE); + CASE_S (FUNCTION); + CASE_S (MODULE); + CASE_S (LOADOBJECT); + CASE_S (EADDR); + CASE_S (MEMOBJ); + CASE_S (INDEXOBJ); + CASE_S (PAGE); + CASE_S (DOBJECT); + CASE_S (SOURCEFILE); + CASE_S (EXPERIMENT); + CASE_S (OTHER); + default: + break; + } + return NTXT ("ERROR"); +} + +void +Histable::dump_comparable_objs () +{ + Dprintf (DEBUG_COMPARISON, + "# Histable::dump_comparable_objs type=%s(%d) 0x%lx id=%lld %s\n", + type_to_string (), get_type (), (unsigned long) this, (long long) id, + STR (get_name ())); + for (int i = 0, sz = comparable_objs ? comparable_objs->size () : 0; i < sz; i++) + { + Histable *h = comparable_objs->fetch (i); + Dprintf (DEBUG_COMPARISON, " %d type=%s(%d) 0x%lx id=%lld %s\n", i, + h ? h->type_to_string () : "", h ? h->get_type () : -1, + (unsigned long) h, (long long) (h ? h->id : 0), + h ? STR (h->get_name ()) : NTXT ("")); + } +} + +char * +Histable::dump () +{ + StringBuilder sb; + sb.appendf (sizeof (long) == 32 + ? " 0x%08lx : type=%s(%d) id=%lld %s" + : " 0x%016lx : type=%s(%d) id=%lld %s", + (unsigned long) this, type_to_string (), get_type (), + (long long) id, STR (get_name ())); + switch (get_type ()) + { + case INSTR: + { + DbeInstr *o = (DbeInstr *) this; + sb.appendf (sizeof (long) == 32 + ? " func=0x%08lx lineno=%lld" + : " func=0x%016lx lineno=%lld", + (unsigned long) o->func, (long long) o->lineno); + break; + } + case LINE: + { + DbeLine *o = (DbeLine *) this; + sb.appendf (sizeof (long) == 32 + ? " func=0x%08lx sourceFile=0x%08lx lineno=%lld" + : " func=0x%016lx sourceFile=0x%016lx lineno=%lld", + (unsigned long) o->func, (unsigned long) o->sourceFile, + (long long) o->lineno); + break; + } + default: + break; + } + return sb.toString (); +} diff --git a/gprofng/src/Hist_data.h b/gprofng/src/Hist_data.h new file mode 100644 index 0000000..c5f7281 --- /dev/null +++ b/gprofng/src/Hist_data.h @@ -0,0 +1,292 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _HIST_DATA_H +#define _HIST_DATA_H + +// A Hist_data object is used to obtain data used for constructing +// a histogram display. + +#include <sys/types.h> + +#include <vec.h> +#include <Map.h> +#include <HashMap.h> + +#include "dbe_structs.h" +#include "Histable.h" +#include "DerivedMetrics.h" + +class DbeLine; +class MetricList; + +class Hist_data +{ +public: + friend class DbeView; + friend class er_print_histogram; + friend class PathTree; + friend class DataSpace; + friend class MemorySpace; + friend class IOActivity; + friend class HeapActivity; + + // HistItem contains all the data about a single histogram item. + struct HistItem + { + HistItem (long n); + ~HistItem (); + Histable *obj; // info on the object + int type; // annotated src/dis: type + TValue *value; // numeric values + long size; + }; + + enum Hist_status + { + SUCCESS = 0, + NO_DATA + }; + + enum Mode + { + ALL, + CALLERS, + CALLEES, + SELF, + MODL, + LAYOUT, + DETAIL + }; + + enum Sort_order + { + ASCEND, + DESCEND + }; + + enum Sort_type + { + ALPHA, + VALUE, + AUX + }; + + Hist_data (MetricList *, Histable::Type, Mode, bool _viewowned = false); + + virtual ~Hist_data (); + void dump (char *msg, FILE *f); + + Hist_status + get_status (void) + { + return status; + } + + // Return the view ownership flag + bool + isViewOwned (void) + { + return viewowned; + } + + // Return the total number of items + long size (void); + + // Append a new HistItem for the specified Histable + HistItem *append_hist_item (Histable *obj); + void append_hist_item (HistItem *hi); + TValue *get_real_value (TValue *res, int met_index, int row); + TValue *get_value (TValue *res, int met_index, int row); + TValue *get_value (TValue *res, int met_index, HistItem *hi); + void print_row (StringBuilder *sb, int row, Metric::HistMetric *hist_metric, char *mark); + void print_content (FILE *out_file, Metric::HistMetric *hist_metric, int limit); + int print_label (FILE *out_file, Metric::HistMetric *hist_metric, int space); + void update_total (Hist_data::HistItem *new_total); + void update_max (Metric::HistMetric *hm_tmp); + void update_legend_width (Metric::HistMetric *hm_tmp); + + // Find an existing HistItem + HistItem *find_hist_item (Histable *obj); + + // sort the data + void sort (long ind, bool reverse); + + // resort the data, if metric sort or direction has changed + void resort (MetricList *mlist); + + // compute minima and maxima + void compute_minmax (void); + + // fetch() takes a hist item index and returns a ptr to the item + HistItem *fetch (long index); + + HistItem * + get_maximums (void) + { + return maximum; + } + + HistItem * + get_maximums_inc (void) + { + return maximum_inc; + } + + HistItem * + get_minimums (void) + { + return minimum; + } + + HistItem * + get_totals (void) + { + return total; + } + + Vector<HistItem*> * + get_hist_items (void) + { + return hist_items; + } + + void + set_status (Hist_status st) + { + status = st; + } + + MetricList * + get_metric_list (void) + { + return metrics; + } + + Map<Histable*, int> * + get_callsite_mark () + { + return callsite_mark; + } + + Metric::HistMetric *get_histmetrics (); + void set_threshold (double proportion); + bool above_threshold (HistItem *hi); + double get_percentage (double value, int mindex); + size_t value_maxlen (int mindex); // Return the drawing length + size_t time_len (TValue *value, int clock); + size_t time_maxlen (int mindex, int clock); + size_t name_len (HistItem *item); + size_t name_maxlen (); + HistItem *new_hist_item (Histable *obj, int itype, TValue *value); + HistItem *update_hist_item (HistItem *hi, TValue *value); + Vector<uint64_t> *get_object_indices (Vector<int> *selections); + +private: + + Metric::HistMetric *hist_metrics; + Vector<HistItem*> *hist_items; // Actual histogram values + HashMap<Histable*, HistItem*>*hi_map; // map: Histable -> HistItem + Map<Histable*, int>*callsite_mark; + Hist_status status; + int nmetrics; // number of metrics + MetricList *metrics; + Histable::Type type; + Sort_order sort_order; + Sort_type sort_type; + int sort_ind; + bool rev_sort; // true if sort is reversed + + Mode mode; + HistItem *gprof_item; // used for gprof-style info + Histable *spontaneous; + + // Private state variables + HistItem *maximum; + HistItem *minimum; + HistItem *maximum_inc; + HistItem *total; + HistItem *threshold; + + // Perform the sort operation with this function + static int sort_compare_all (const void *a, const void *b, const void *arg); + static int sort_compare_dlayout (const void *a, const void *b, const void *arg); + static int sort_compare (HistItem *hi_1, HistItem *hi_2, Sort_type stype, + long ind, Hist_data *hdata); + + // Allocate a new structure of dynamic size + HistItem *new_hist_item (Histable *obj); + + // Flag indicating whether or not the Hist_data structure + // is owned by a DbeView, which has responsibility for + // deleting it, or not, in which case the last user deletes it. + // XXX this is very ugly, and arises from the inconsistent handling + // XXX of the Hist_data structure in various bits of code. + bool viewowned; +}; + +// This structure is destined to merge with Hist_data. +// We currently use it to present callstack data such as +// leak and allocation lists. + +class DbeInstr; + +struct CStack_data +{ + + struct CStack_item + { + CStack_item (long n); + ~CStack_item (); + long count; + int64_t val; + Vector<DbeInstr*> *stack; + TValue *value; // numeric values + }; + + Vector<CStack_item*> *cstack_items; + CStack_item *total; + + CStack_item *new_cstack_item (); + CStack_data (MetricList *); + + long + size () + { + return cstack_items->size (); + } + + CStack_item * + fetch (long i) + { + return cstack_items->fetch (i); + } + + ~CStack_data () + { + cstack_items->destroy (); + delete cstack_items; + delete total; + } + + MetricList *metrics; +}; + +#endif /* _HIST_DATA_H */ diff --git a/gprofng/src/Histable.h b/gprofng/src/Histable.h new file mode 100644 index 0000000..c4cf854 --- /dev/null +++ b/gprofng/src/Histable.h @@ -0,0 +1,333 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _HISTABLE_H +#define _HISTABLE_H + +// +// The Histable class hierarchy is used to build up a representation of +// the codeobjects (functions, modules, loadObjects, etc.) that make up the +// text address space of a program. The hierarchy is as follows: +// +// Histable (public) +// LoadObject (public) +// Module (public) +// Function (public) +// +// Dataobjects are objects from the data address space of a program. +// The reason for calling the base class "Histable" is because these +// objects are all valid objects for computing histograms on. + +// A Histable object represents an object in the program text or data. + +#include "dbe_structs.h" +#include "Emsg.h" +#include "Expression.h" + +class DataObject; +class Function; +class SourceFile; +class DbeFile; +class DbeLine; +template <class ITEM> class Vector; + +class Histable +{ + friend class Hist_data; +public: + + enum Type + { + INSTR, LINE, FUNCTION, MODULE, LOADOBJECT, + EADDR, MEMOBJ, INDEXOBJ, PAGE, DOBJECT, + SOURCEFILE, IOACTFILE, IOACTVFD, IOCALLSTACK, + HEAPCALLSTACK, EXPERIMENT, OTHER + }; + + // NameFormat for functions and function based objects + + enum NameFormat + { + NA, LONG, SHORT, MANGLED, SONAME = 0x10 + }; + + static NameFormat + make_fmt (int fnfmt, bool sofmt = false) + { + return (NameFormat) (sofmt ? fnfmt | SONAME : fnfmt); + } + + static int + fname_fmt (NameFormat fmt) + { + return (fmt & ~SONAME); + } + + static bool + soname_fmt (NameFormat fmt) + { + return (fmt & SONAME); + } + + Histable (); + char *dump (); + + virtual ~Histable (); + + virtual char * + get_name (NameFormat = NA) + { + return name; // Return the name of the object + } + + virtual void + set_name (char * _name) + { + name = _name; + } + + virtual void set_name_from_context (Expression::Context *) { } + virtual Type get_type () = 0; + + virtual int64_t + get_size () + { + return 0; + } + + virtual uint64_t + get_addr () + { + return 0ULL; + } + + virtual Vector<Histable*> *get_comparable_objs (); + Histable *get_compare_obj (); + + virtual Histable * + convertto (Type, Histable* = NULL) + { + return this; + } + + Vector<Histable*> *comparable_objs; + int64_t id; // A unique id of this object, within its specific Type + +protected: + char *name; // Object name + int phaseCompareIdx; + void update_comparable_objs (); + void dump_comparable_objs (); + char *type_to_string (); + void delete_comparable_objs (); +}; + +typedef Histable::Type Histable_type; + +// An Other object represents some random histable object +class Other : public Histable +{ +public: + + virtual Type + get_type () + { + return OTHER; + } + + uint64_t value64; + uint32_t tag; +}; + +// DbeInstr represents an instruction. +// +// Used by Analyzer for: Disassembly, PCs, Timeline, and Event tabs. +// +class DbeInstr : public Histable +{ +public: + DbeInstr (uint64_t _id, int _flags, Function *_func, uint64_t _addr); + + virtual Type + get_type () + { + return INSTR; + } + + virtual char *get_name (NameFormat = NA); + virtual int64_t get_size (); + virtual uint64_t get_addr (); + virtual Histable *convertto (Type type, Histable *obj = NULL); + DbeLine *mapPCtoLine (SourceFile *sf); + void add_inlined_info (StringBuilder *sb); + char *get_descriptor (); + int pc_cmp (DbeInstr *instr2); + + uint64_t addr; + uint64_t img_offset; // file offset of the image + int flags; + Function *func; + int lineno; + int inlinedInd; + int64_t size; + bool isUsed; + +private: + NameFormat current_name_format; +}; + +class DbeEA : public Histable +{ +public: + + DbeEA (DataObject *_dobj, Vaddr _eaddr) + { + dobj = _dobj; + eaddr = _eaddr; + }; + + virtual Type + get_type () + { + return EADDR; + }; + + virtual int64_t + get_size () + { + return 1; + }; + + virtual uint64_t + get_addr () + { + return eaddr; + }; + + virtual char *get_name (NameFormat = NA); + virtual Histable *convertto (Type type, Histable *obj = NULL); + + DataObject *dobj; + Vaddr eaddr; +}; + +// DbeLine represents a line in a source file. +// +// For each top-level DbeLine instance, there are three DbeLine subtypes: +// +// A The top-level DbeLine is associated with a sourceFile & lineno, but +// not any particular function. This form of DbeLine is used +// to represent Analyzer Source tab lines. +// +// B Function-specific lines, those associated with a function in addition +// to the the sourceFile & lineno, are stored in a linked list. +// (see "dbeline_func_next"). +// This subtype is used to differentiate a line found in #included source +// that is referenced by multiple functions. +// It is used in the Analyzer Lines tab. +// +// C Function-specific "lines" that don't have line number info are referenced +// from each linked-list element's "dbeline_func_pseudo" field. +// This subtype is needed when a binary doesn't identify line numbers. +// It is used in the Analyzer Lines tab. +// +// When the user switches views between tabs, a selected object in the old +// tab must be translated to an approprate object in the new tab. +// When switching to the Source Tab, the top-level DbeLine (dbeline_base) +// should be used. +// When switching to the Lines Tab, a function-specific dbeline_func_* +// should be used. +// + +class DbeLine : public Histable +{ +public: + + enum Flag + { + OMPPRAGMA = 1 + }; + + DbeLine (Function *_func, SourceFile *sf, int _lineno); + virtual ~DbeLine (); + virtual char *get_name (NameFormat = NA); + virtual int64_t get_size (); + virtual uint64_t get_addr (); + virtual Histable *convertto (Type type, Histable *obj = NULL); + + void init_Offset (uint64_t p_offset); + int line_cmp (DbeLine *dbl); + + virtual Type + get_type () + { + return LINE; + } + + void + set_flag (Flag flag) + { + flags |= flag; + } + + bool + is_set (Flag flag) + { + return (flags & flag) != 0; + } + + Function *func; // note: will be NULL in the base (head) dbeline + int lineno; + int64_t size; + SourceFile *sourceFile; // Default source file + SourceFile *include; // included source file or NULL + + DbeLine *dbeline_base; + // Head of list, a dbeline associated with sourceFile & lineno, but not func: + // dbeline_base->lineno: non-zero + // dbeline_base->sourceFile: non-null + // dbeline_base->func: NULL + // dbeline_base->dbeline_base: this + // dbeline_base->dbeline_func_next: first func-specific dbeline + + DbeLine *dbeline_func_next; + // If non-null, pointer to a function-specific dbeline where: + // dbeline_func_next->lineno: same as dbeline_base->lineno + // dbeline_func_next->sourceFile: same as dbeline_base->sourceFile + // dbeline_func_next->func: pointer to unique function + // dbeline_func_next->dbeline_base: head of the linked list. + // dbeline_func_next->dbeline_func_next: next function-specific dbeline. + +private: + int current_name_format; + int64_t offset; + int flags; +}; + +class HistableFile : public Histable, public DbeMessages +{ +public: + HistableFile (); + + bool isUsed; + DbeFile *dbeFile; +}; + +#endif /* _HISTABLE_H */ diff --git a/gprofng/src/IOActivity.cc b/gprofng/src/IOActivity.cc new file mode 100644 index 0000000..401cab5 --- /dev/null +++ b/gprofng/src/IOActivity.cc @@ -0,0 +1,825 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "DbeSession.h" +#include "FileData.h" +#include "StringBuilder.h" +#include "i18n.h" +#include "util.h" +#include "IOActivity.h" +#include "MetricList.h" +#include "Application.h" +#include "Experiment.h" +#include "DbeView.h" +#include "Exp_Layout.h" +#include "i18n.h" + +IOActivity::IOActivity (DbeView *_dbev) +{ + dbev = _dbev; + fDataHash = NULL; + fDataTotal = NULL; + fDataObjs = NULL; + fDataObjsFile = NULL; + hasFile = false; + fDataObjsVfd = NULL; + hasVfd = false; + fDataObjsCallStack = NULL; + hasCallStack = false; + fDataCalStkMap = NULL; + fDataVfdMap = NULL; + hist_data_file_all = NULL; + hist_data_vfd_all = NULL; + hist_data_callstack_all = NULL; +} + +void +IOActivity::reset () +{ + int numExps = dbeSession->nexps (); + FileData *fData = NULL; + DefaultMap<int64_t, FileData*>* fDataMap; + for (int k = 0; k < numExps; k++) + { + Experiment *exp = dbeSession->get_exp (k); + fDataMap = exp->getFDataMap (); + if (fDataMap == NULL) + continue; + + fDataObjs = fDataMap->values (); + if (fDataObjs == NULL) + continue; + int numFiles = fDataObjs->size (); + for (int j = 0; j < numFiles; j++) + { + fData = fDataObjs->fetch (j); + fData->init (); + } + } + + delete fDataHash; + fDataHash = NULL; + delete fDataTotal; + fDataTotal = NULL; + + delete fDataObjsFile; + fDataObjsFile = NULL; + hasFile = false; + + delete fDataObjsVfd; + fDataObjsVfd = NULL; + hasVfd = false; + + delete fDataObjsCallStack; + fDataObjsCallStack = NULL; + hasCallStack = false; + + delete fDataObjs; + fDataObjs = NULL; + delete fDataCalStkMap; + fDataCalStkMap = NULL; + delete fDataVfdMap; + fDataVfdMap = NULL; + + // These three pointers are deleted by DbeView + // They are named iofile_data, iovfd_data, and iocs_data + hist_data_file_all = NULL; + hist_data_vfd_all = NULL; + hist_data_callstack_all = NULL; +} + +void +IOActivity::createHistItemTotals (Hist_data *hist_data, MetricList *mlist, + Histable::Type hType, bool empty) +{ + int mIndex; + Metric *mtr; + Hist_data::HistItem *hi; + FileData *fData = NULL; + + if (fDataTotal == NULL) + { + fDataTotal = new FileData (TOTAL_FILENAME); + fDataTotal->setHistType (hType); + fDataTotal->setVirtualFd (VIRTUAL_FD_TOTAL); + fDataTotal->id = 0; + } + + fData = new FileData (fDataTotal); + fData->setHistType (hType); + hi = hist_data->append_hist_item (fData); + Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) + { + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + hist_data->total->value[mIndex].tag = vType; + hi->value[mIndex].tag = vType; + double prec = (double) NANOSEC; + switch (mtype) + { + case BaseMetric::IO_READ_BYTES: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getReadBytes (); + hi->value[mIndex].ll = fDataTotal->getReadBytes (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_READ_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getReadCnt (); + hi->value[mIndex].ll = fDataTotal->getReadCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_READ_TIME: + if (!empty) + { + hist_data->total->value[mIndex].d = + (double) fDataTotal->getReadTime () / prec; + hi->value[mIndex].d = hist_data->total->value[mIndex].d; + } + else + { + hist_data->total->value[mIndex].d = 0.0; + hi->value[mIndex].d = 0.0; + } + break; + case BaseMetric::IO_WRITE_BYTES: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getWriteBytes (); + hi->value[mIndex].ll = fDataTotal->getWriteBytes (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_WRITE_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getWriteCnt (); + hi->value[mIndex].ll = fDataTotal->getWriteCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_WRITE_TIME: + if (!empty) + { + hist_data->total->value[mIndex].d = + (double) fDataTotal->getWriteTime () / prec; + hi->value[mIndex].d = hist_data->total->value[mIndex].d; + } + else + { + hist_data->total->value[mIndex].d = 0.0; + hi->value[mIndex].d = 0.0; + } + break; + case BaseMetric::IO_OTHER_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getOtherCnt (); + hi->value[mIndex].ll = fDataTotal->getOtherCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_OTHER_TIME: + if (!empty) + { + hist_data->total->value[mIndex].d = + (double) fDataTotal->getOtherTime () / prec; + hi->value[mIndex].d = hist_data->total->value[mIndex].d; + } + else + { + hist_data->total->value[mIndex].d = 0.0; + hi->value[mIndex].d = 0.0; + } + break; + case BaseMetric::IO_ERROR_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getErrorCnt (); + hi->value[mIndex].ll = fDataTotal->getErrorCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_ERROR_TIME: + if (!empty) + { + hist_data->total->value[mIndex].d = (double) fDataTotal->getErrorTime () / prec; + hi->value[mIndex].d = hist_data->total->value[mIndex].d; + } + else + { + hist_data->total->value[mIndex].d = 0.0; + hi->value[mIndex].d = 0.0; + } + break; + default: + break; + } + } +} + +void +IOActivity::computeHistTotals (Hist_data *hist_data, MetricList *mlist) +{ + int mIndex; + Metric *mtr; + Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) + { + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + hist_data->total->value[mIndex].tag = vType; + double prec = (double) NANOSEC; + switch (mtype) + { + case BaseMetric::IO_READ_BYTES: + hist_data->total->value[mIndex].ll = fDataTotal->getReadBytes (); + break; + case BaseMetric::IO_READ_CNT: + hist_data->total->value[mIndex].ll = fDataTotal->getReadCnt (); + break; + case BaseMetric::IO_READ_TIME: + hist_data->total->value[mIndex].d = + (double) fDataTotal->getReadTime () / prec; + break; + case BaseMetric::IO_WRITE_BYTES: + hist_data->total->value[mIndex].ll = fDataTotal->getWriteBytes (); + break; + case BaseMetric::IO_WRITE_CNT: + hist_data->total->value[mIndex].ll = fDataTotal->getWriteCnt (); + break; + case BaseMetric::IO_WRITE_TIME: + hist_data->total->value[mIndex].d = + (double) fDataTotal->getWriteTime () / prec; + break; + case BaseMetric::IO_OTHER_CNT: + hist_data->total->value[mIndex].ll = fDataTotal->getOtherCnt (); + break; + case BaseMetric::IO_OTHER_TIME: + hist_data->total->value[mIndex].d = + (double) fDataTotal->getOtherTime () / prec; + break; + case BaseMetric::IO_ERROR_CNT: + hist_data->total->value[mIndex].ll = fDataTotal->getErrorCnt (); + break; + case BaseMetric::IO_ERROR_TIME: + hist_data->total->value[mIndex].d = + (double) fDataTotal->getErrorTime () / prec; + break; + default: + break; + } + } +} + +void +IOActivity::computeHistData (Hist_data *hist_data, MetricList *mlist, + Hist_data::Mode mode, Histable *selObj) +{ + + Hist_data::HistItem *hi = NULL; + int numObjs = fDataObjs->size (); + int numMetrics = mlist->get_items ()->size (); + + for (int i = 0; i < numObjs; i++) + { + FileData *fData = fDataObjs->fetch (i); + if (mode == Hist_data::ALL) + hi = hist_data->append_hist_item (fData); + else if (mode == Hist_data::SELF) + { + if (fData->id == selObj->id) + hi = hist_data->append_hist_item (fData); + else + continue; + } + + for (int mIndex = 0; mIndex < numMetrics; mIndex++) + { + Metric *mtr = mlist->get_items ()->fetch (mIndex); + if (!mtr->is_visible () && !mtr->is_tvisible () + && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + hi->value[mIndex].tag = vType; + + double prec = (double) NANOSEC; + switch (mtype) + { + case BaseMetric::IO_READ_BYTES: + hi->value[mIndex].ll = fData->getReadBytes (); + break; + case BaseMetric::IO_READ_CNT: + hi->value[mIndex].ll = fData->getReadCnt (); + break; + case BaseMetric::IO_READ_TIME: + hi->value[mIndex].d = (double) fData->getReadTime () / prec; + break; + case BaseMetric::IO_WRITE_BYTES: + hi->value[mIndex].ll = fData->getWriteBytes (); + break; + case BaseMetric::IO_WRITE_CNT: + hi->value[mIndex].ll = fData->getWriteCnt (); + break; + case BaseMetric::IO_WRITE_TIME: + hi->value[mIndex].d = (double) fData->getWriteTime () / prec; + break; + case BaseMetric::IO_OTHER_CNT: + hi->value[mIndex].ll = fData->getOtherCnt (); + break; + case BaseMetric::IO_OTHER_TIME: + hi->value[mIndex].d = (double) fData->getOtherTime () / prec; + break; + case BaseMetric::IO_ERROR_CNT: + hi->value[mIndex].ll = fData->getErrorCnt (); + break; + case BaseMetric::IO_ERROR_TIME: + hi->value[mIndex].d = (double) fData->getErrorTime () / prec; + break; + default: + break; + } + } + } +} + +Hist_data * +IOActivity::compute_metrics (MetricList *mlist, Histable::Type type, + Hist_data::Mode mode, Histable *selObj) +{ + + // it's already there, just return it + if (mode == Hist_data::ALL) + { + if (type == Histable::IOACTFILE && hist_data_file_all) + return hist_data_file_all; + else if (type == Histable::IOACTVFD && hist_data_vfd_all) + return hist_data_vfd_all; + else if (type == Histable::IOCALLSTACK && hist_data_callstack_all) + return hist_data_callstack_all; + } + + bool has_data = false; + Hist_data *hist_data = NULL; + VMode viewMode = dbev->get_view_mode (); + + switch (type) + { + case Histable::IOACTVFD: + if (!hasVfd) + computeData (type); + + // computeData() creates fDataObjsVfd + // fDataObjsVfd contains the list of vfd objects + if (fDataObjsVfd != NULL) + { + // fDataObjs is used in other methods + fDataObjs = fDataObjsVfd; + has_data = true; + } + else + has_data = false; + + if (has_data && mode == Hist_data::ALL && hist_data_vfd_all == NULL) + { + hist_data_vfd_all = new Hist_data (mlist, type, mode, true); + hist_data = hist_data_vfd_all; + } + else if (has_data) + hist_data = new Hist_data (mlist, type, mode, false); + else + { + hist_data = new Hist_data (mlist, type, mode, false); + createHistItemTotals (hist_data, mlist, type, true); + return hist_data; + } + break; + case Histable::IOACTFILE: + if (!hasFile) + computeData (type); + + // computeData() creates fDataObjsFile + // fDataObjsFile contains the list of file objects + if (fDataObjsFile != NULL) + { + fDataObjs = fDataObjsFile; + has_data = true; + } + else + has_data = false; + + if (has_data && mode == Hist_data::ALL && hist_data_file_all == NULL) + { + hist_data_file_all = new Hist_data (mlist, type, mode, true); + hist_data = hist_data_file_all; + } + else if (has_data) + hist_data = new Hist_data (mlist, type, mode, false); + else + { + hist_data = new Hist_data (mlist, type, mode, false); + createHistItemTotals (hist_data, mlist, type, true); + return hist_data; + } + break; + case Histable::IOCALLSTACK: + if (!hasCallStack) + computeCallStack (type, viewMode); + + // computeCallStack() creates fDataObjsCallStack + // fDataObjsCallStack contains the list of call stack objects + if (fDataObjsCallStack != NULL) + { + fDataObjs = fDataObjsCallStack; + has_data = true; + } + else + has_data = false; + + if (has_data && (mode == Hist_data::ALL) && (hist_data_callstack_all == NULL)) + { + hist_data_callstack_all = new Hist_data (mlist, type, mode, true); + hist_data = hist_data_callstack_all; + } + else if (has_data) + hist_data = new Hist_data (mlist, type, mode, false); + else + { + hist_data = new Hist_data (mlist, type, mode, false); + createHistItemTotals (hist_data, mlist, type, true); + return hist_data; + } + break; + default: + fprintf (stderr, + "IOActivity cannot process data due to wrong Histable (type=%d) \n", + type); + abort (); + } + + if (mode == Hist_data::ALL || (mode == Hist_data::SELF && selObj->id == 0)) + createHistItemTotals (hist_data, mlist, type, false); + else + computeHistTotals (hist_data, mlist); + computeHistData (hist_data, mlist, mode, selObj); + + // Determine by which metric to sort if any + bool rev_sort = mlist->get_sort_rev (); + int sort_ind = -1; + int nmetrics = mlist->get_items ()->size (); + for (int mind = 0; mind < nmetrics; mind++) + if (mlist->get_sort_ref_index () == mind) + sort_ind = mind; + + hist_data->sort (sort_ind, rev_sort); + hist_data->compute_minmax (); + return hist_data; +} + +void +IOActivity::computeData (Histable::Type type) +{ + bool has_iodata = false; + reset (); + int64_t histableId = 0; // It is used by fDataAggr only + // fData uses vfd for histable id + + fDataHash = new HashMap<char*, FileData*>; + FileData *fData = NULL; + FileData *fDataAggr = NULL; + + fDataTotal = new FileData (TOTAL_FILENAME); + fDataTotal->setHistType (type); + fDataTotal->setVirtualFd (VIRTUAL_FD_TOTAL); + fDataTotal->id = histableId++; + + FileData *fDataStdin = new FileData (STDIN_FILENAME); + fDataStdin->setFileDes (STDIN_FD); + fDataStdin->setHistType (type); + fDataStdin->setFsType ("N/A"); + fDataStdin->id = histableId++; + + FileData *fDataStdout = new FileData (STDOUT_FILENAME); + fDataStdout->setFileDes (STDOUT_FD); + fDataStdout->setHistType (type); + fDataStdout->setFsType ("N/A"); + fDataStdout->id = histableId++; + + FileData *fDataStderr = new FileData (STDERR_FILENAME); + fDataStderr->setFileDes (STDERR_FD); + fDataStderr->setHistType (type); + fDataStderr->setFsType ("N/A"); + fDataStderr->id = histableId++; + + FileData *fDataOtherIO = new FileData (OTHERIO_FILENAME); + fDataOtherIO->setFileDes (OTHERIO_FD); + fDataOtherIO->setHistType (type); + fDataOtherIO->setFsType ("N/A"); + fDataOtherIO->id = histableId++; + + DefaultMap<int64_t, FileData*>* fDataMap; + fDataObjsFile = NULL; + fDataObjsVfd = NULL; + + // get the list of io events from DbeView + int numExps = dbeSession->nexps (); + + for (int k = 0; k < numExps; k++) + { + DataView *ioPkts = dbev->get_filtered_events (k, DATA_IOTRACE); + if (ioPkts == NULL || ioPkts->getSize () <= 0) + continue; + Experiment *exp = dbeSession->get_exp (k); + fDataMap = exp->getFDataMap (); + if (fDataMap == NULL) + continue; + delete fDataVfdMap; + fDataVfdMap = new DefaultMap<long, FileData*>; + + long sz = ioPkts->getSize (); + for (long i = 0; i < sz; ++i) + { + hrtime_t event_duration = ioPkts->getLongValue (PROP_EVT_TIME, i); + int64_t nByte = ioPkts->getLongValue (PROP_IONBYTE, i); + IOTrace_type ioType = (IOTrace_type) ioPkts->getIntValue (PROP_IOTYPE, i); + int64_t vFd = ioPkts->getLongValue (PROP_IOVFD, i); + if (vFd >= 0) + { + fData = fDataMap->get (vFd); + if (fData == NULL) + continue; + } + else + continue; + + if (fDataVfdMap->get (vFd) == NULL) + fDataVfdMap->put (vFd, fData); + + switch (ioType) + { + case READ_TRACE: + fData->addReadEvent (event_duration, nByte); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addReadEvent (event_duration, nByte); + fDataTotal->setReadStat (event_duration, nByte); + break; + case WRITE_TRACE: + fData->addWriteEvent (event_duration, nByte); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addWriteEvent (event_duration, nByte); + fDataTotal->setWriteStat (event_duration, nByte); + break; + case OPEN_TRACE: + fData->addOtherEvent (event_duration); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addOtherEvent (event_duration); + break; + case CLOSE_TRACE: + case OTHERIO_TRACE: + fData->addOtherEvent (event_duration); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addOtherEvent (event_duration); + break; + case READ_TRACE_ERROR: + case WRITE_TRACE_ERROR: + case OPEN_TRACE_ERROR: + case CLOSE_TRACE_ERROR: + case OTHERIO_TRACE_ERROR: + fData->addErrorEvent (event_duration); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addErrorEvent (event_duration); + break; + + case IOTRACETYPE_LAST: + break; + } + + if (type == Histable::IOACTFILE) + { + fDataAggr = fDataHash->get (fData->getFileName ()); + if (fDataAggr == NULL) + { + bool setInfo = false; + if (vFd == VIRTUAL_FD_STDIN) + fDataAggr = fDataStdin; + else if (vFd == VIRTUAL_FD_STDOUT) + fDataAggr = fDataStdout; + else if (vFd == VIRTUAL_FD_STDERR) + fDataAggr = fDataStderr; + else if (vFd == VIRTUAL_FD_OTHERIO) + fDataAggr = fDataOtherIO; + else + { + fDataAggr = new FileData (fData->getFileName ()); + setInfo = true; + } + fDataHash->put (fData->getFileName (), fDataAggr); + + if (setInfo) + { + fDataAggr->setFsType (fData->getFsType ()); + fDataAggr->setHistType (type); + // Set the Histable id for aggregated file name + fDataAggr->id = histableId; + fDataAggr->setVirtualFd (histableId); + histableId++; + } + } + + fDataAggr->setFileDesList (fData->getFileDes ()); + fDataAggr->setVirtualFds (fData->getVirtualFd ()); + switch (ioType) + { + case READ_TRACE: + fDataAggr->addReadEvent (event_duration, nByte); + break; + case WRITE_TRACE: + fDataAggr->addWriteEvent (event_duration, nByte); + break; + case OPEN_TRACE: + fDataAggr->addOtherEvent (event_duration); + break; + case CLOSE_TRACE: + case OTHERIO_TRACE: + fDataAggr->addOtherEvent (event_duration); + break; + case READ_TRACE_ERROR: + case WRITE_TRACE_ERROR: + case OPEN_TRACE_ERROR: + case CLOSE_TRACE_ERROR: + case OTHERIO_TRACE_ERROR: + fDataAggr->addErrorEvent (event_duration); + break; + case IOTRACETYPE_LAST: + break; + } + } + has_iodata = true; + } + if (sz > 0) + { + if (fDataObjsVfd == NULL) + fDataObjsVfd = new Vector<FileData*>; + fDataObjsVfd->addAll (fDataVfdMap->values ()); + hasVfd = true; + } + } + if (has_iodata && type == Histable::IOACTFILE) + { + fDataObjsFile = fDataHash->values ()->copy (); + hasFile = true; + } +} + +void +IOActivity::computeCallStack (Histable::Type type, VMode viewMode) +{ + bool has_data = false; + int64_t stackIndex = 0; + FileData *fData = NULL; + delete fDataCalStkMap; + fDataCalStkMap = new DefaultMap<void*, FileData*>; + delete fDataTotal; + fDataTotal = new FileData (TOTAL_FILENAME); + fDataTotal->setHistType (type); + + // There is no call stack for total, use the index for id + fDataTotal->id = stackIndex++; + + // get the list of io events from DbeView + int numExps = dbeSession->nexps (); + for (int k = 0; k < numExps; k++) + { + DataView *ioPkts = dbev->get_filtered_events (k, DATA_IOTRACE); + if (ioPkts == NULL || ioPkts->getSize () <= 0) + continue; + long sz = ioPkts->getSize (); + for (long i = 0; i < sz; ++i) + { + hrtime_t event_duration = ioPkts->getLongValue (PROP_EVT_TIME, i); + int64_t nByte = ioPkts->getLongValue (PROP_IONBYTE, i); + void *stackId = getStack (viewMode, ioPkts, i); + IOTrace_type ioType = + (IOTrace_type) ioPkts->getIntValue (PROP_IOTYPE, i); + int64_t vFd = ioPkts->getLongValue (PROP_IOVFD, i); + + if (stackId != NULL && vFd > 0) + { + fData = fDataCalStkMap->get (stackId); + if (fData == NULL) + { + char *stkName = dbe_sprintf (GTXT ("Stack 0x%llx"), + (unsigned long long) stackId); + fData = new FileData (stkName); + fDataCalStkMap->put (stackId, fData); + fData->id = (int64_t) stackId; + fData->setVirtualFd (stackIndex); + stackIndex++; + fData->setHistType (type); + } + } + else + continue; + + switch (ioType) + { + case READ_TRACE: + fData->addReadEvent (event_duration, nByte); + fDataTotal->addReadEvent (event_duration, nByte); + fDataTotal->setReadStat (event_duration, nByte); + break; + case WRITE_TRACE: + fData->addWriteEvent (event_duration, nByte); + fDataTotal->addWriteEvent (event_duration, nByte); + fDataTotal->setWriteStat (event_duration, nByte); + break; + case OPEN_TRACE: + fData->addOtherEvent (event_duration); + fDataTotal->addOtherEvent (event_duration); + break; + case CLOSE_TRACE: + case OTHERIO_TRACE: + fData->addOtherEvent (event_duration); + fDataTotal->addOtherEvent (event_duration); + break; + case READ_TRACE_ERROR: + case WRITE_TRACE_ERROR: + case OPEN_TRACE_ERROR: + fData->addErrorEvent (event_duration); + fDataTotal->addErrorEvent (event_duration); + break; + case CLOSE_TRACE_ERROR: + case OTHERIO_TRACE_ERROR: + fData->addErrorEvent (event_duration); + fDataTotal->addErrorEvent (event_duration); + break; + case IOTRACETYPE_LAST: + break; + } + has_data = true; + } + } + if (has_data) + { + fDataObjsCallStack = fDataCalStkMap->values ()->copy (); + hasCallStack = true; + } +} diff --git a/gprofng/src/IOActivity.h b/gprofng/src/IOActivity.h new file mode 100644 index 0000000..35c4517 --- /dev/null +++ b/gprofng/src/IOActivity.h @@ -0,0 +1,86 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _IOACTIVITY_H +#define _IOACTIVITY_H + + +#include <stdio.h> + +#include "vec.h" +#include "Histable.h" +#include "Hist_data.h" +#include "Metric.h" +#include "FileData.h" +#include "DefaultMap.h" +#include "dbe_types.h" + +class Experiment; +class Expression; +class DataView; +class DbeView; + +class IOActivity +{ +public: + + IOActivity (DbeView *_dbev); + void reset (void); + Hist_data *compute_metrics (MetricList *, Histable::Type, Hist_data::Mode, + Histable*); + + ~IOActivity () + { + this->reset (); + } + +private: + void computeData (Histable::Type); + void computeCallStack (Histable::Type, VMode viewMode); + void createHistItemTotals (Hist_data *, MetricList *, Histable::Type, bool); + void computeHistTotals (Hist_data *, MetricList *); + void computeHistData (Hist_data *, MetricList *, Hist_data::Mode, Histable *); + + Vector<FileData*> *fDataObjs; + Vector<FileData*> *fDataObjsFile; + Vector<FileData*> *fDataObjsVfd; + Vector<FileData*> *fDataObjsCallStack; + bool hasFile; + bool hasVfd; + bool hasCallStack; + HashMap<char*, FileData*> *fDataHash; + FileData *fDataTotal; + + // list of FileData objects using the stack id as the key + DefaultMap<void*, FileData*> *fDataCalStkMap; + + // list of FileData objects using the VFD as the key + DefaultMap<long, FileData*> *fDataVfdMap; + + // the cached data for mode=Hist_Data::ALL + Hist_data *hist_data_file_all; + Hist_data *hist_data_vfd_all; + Hist_data *hist_data_callstack_all; + Hist_data *hist_data_callstack; + + DbeView *dbev; +}; + +#endif /* _IOACTIVITY_H */ diff --git a/gprofng/src/IndexMap2D.h b/gprofng/src/IndexMap2D.h new file mode 100644 index 0000000..0f68a3a --- /dev/null +++ b/gprofng/src/IndexMap2D.h @@ -0,0 +1,119 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Index Map2D implementation. + * + * Index Map2D is dynamic two dimensional array + */ + +#ifndef _DBE_INDEXMAP2D_H +#define _DBE_INDEXMAP2D_H + +#include <assert.h> +#include <vec.h> +#include <Map2D.h> + +template <typename Key1_t, typename Key2_t, typename Value_t> +class IndexMap2D : public Map2D<Key1_t, Key2_t, Value_t> +{ +public: + + IndexMap2D (); + ~IndexMap2D (); + + void put (Key1_t key1, Key2_t key2, Value_t val); + Value_t get (Key1_t key1, Key2_t key2); + Value_t get (Key1_t key1, Key2_t key2, + typename Map2D<Key1_t, Key2_t, Value_t>::Relation rel); + Value_t remove (Key1_t key1, Key2_t key2); + +private: + + Vector<Vector<Value_t>*> *map1; +}; + +template <typename Key1_t, typename Key2_t, typename Value_t> +IndexMap2D<Key1_t, Key2_t, Value_t>::IndexMap2D () +{ + map1 = new Vector<Vector<Value_t>*>; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +IndexMap2D<Key1_t, Key2_t, Value_t>::~IndexMap2D () +{ + map1->destroy (); + delete map1; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +void +IndexMap2D<Key1_t, Key2_t, Value_t>::put (Key1_t key1, Key2_t key2, Value_t val) +{ + if (key1 < 0 || key2 < 0) + return; + Vector<Value_t> *map2 = NULL; + if (key1 < map1->size ()) + map2 = map1->fetch ((int) key1); + if (map2 == NULL) + { + map2 = new Vector<Value_t>; + map1->store ((int) key1, map2); + } + map2->store ((int) key2, val); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +IndexMap2D<Key1_t, Key2_t, Value_t>::get (Key1_t key1, Key2_t key2) +{ + if (key1 < 0 || key1 >= map1->size () || key2 < 0) + return (Value_t) 0; + Vector<Value_t> *map2 = map1->fetch ((int) key1); + if (map2 == NULL || key2 >= map2->size ()) + return (Value_t) 0; + return map2->fetch ((int) key2); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +IndexMap2D<Key1_t, Key2_t, Value_t>::get (Key1_t key1, Key2_t key2, + typename Map2D<Key1_t, Key2_t, Value_t>::Relation rel) +{ + if (rel != Map2D<Key1_t, Key2_t, Value_t>::REL_EQEQ) + return (Value_t) 0; + return get (key1, key2); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +IndexMap2D<Key1_t, Key2_t, Value_t>::remove (Key1_t key1, Key2_t key2) +{ + if (key1 < 0 || key1 >= map1->size () || key2 < 0) + return (Value_t) 0; + Vector<Value_t> *map2 = map1->fetch ((int) key1); + if (map2 == NULL || key2 >= map2->size ()) + return (Value_t) 0; + Value_t res = map2->fetch ((int) key2); + map2->store ((int) key2, (Value_t) 0); + return res; +} + +#endif diff --git a/gprofng/src/IndexObject.cc b/gprofng/src/IndexObject.cc new file mode 100644 index 0000000..a7c8a37 --- /dev/null +++ b/gprofng/src/IndexObject.cc @@ -0,0 +1,554 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "util.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "IndexObject.h" +#include "StringBuilder.h" + +IndexObject::IndexObject (int _indextype, uint64_t _index) +{ + indextype = _indextype; + obj = NULL; + id = _index; + name = NULL; + nameIsFinal = false; +} + +IndexObject::IndexObject (int _indextype, Histable *_obj) +{ + indextype = _indextype; + obj = _obj; + id = obj ? obj->id : (uint64_t) - 1; + name = NULL; + nameIsFinal = false; +} + +void +IndexObject::set_name (char * other_name) +{ + if (name == NULL) + { + name = other_name; + nameIsFinal = true; + } +} + +static uint64_t +extractExpgrid (uint64_t id) +{ + return (id >> IndexObject::INDXOBJ_EXPGRID_SHIFT) + & IndexObject::INDXOBJ_EXPGRID_MASK; +} + +static uint64_t +extractExpid (uint64_t id) +{ + return (id >> IndexObject::INDXOBJ_EXPID_SHIFT) + & IndexObject::INDXOBJ_EXPID_MASK; +} + +static uint64_t +extractPayload (uint64_t id) +{ + return (id >> IndexObject::INDXOBJ_PAYLOAD_SHIFT) + & IndexObject::INDXOBJ_PAYLOAD_MASK; +} + +static void +printCompareLabel (StringBuilder *sb, uint64_t grpId); + +static bool +printThread (StringBuilder *sbIn, Expression::Context * ctx, uint64_t id) +{ + uint64_t proc = extractExpid (id); + uint64_t thrid = extractPayload (id); + bool isFinal = true; + bool hasJava = false; + bool javaThread = false; + if (ctx) + { + if (ctx->dview && ctx->dview->getProp (PROP_JTHREAD)) + { + hasJava = true; + uint64_t tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + JThread *jthread = ctx->exp->map_pckt_to_Jthread (thrid, tstamp); + if (jthread != JTHREAD_NONE && jthread != JTHREAD_DEFAULT) + { + sbIn->appendf (GTXT ("Process %llu, Thread %llu, JThread %llu \'%s\', Group \'%s\', Parent \'%s\'"), + (unsigned long long) proc, + (unsigned long long) thrid, + (unsigned long long) jthread->jthr_id, + get_str(jthread->name, ""), + get_str(jthread->group_name, ""), + get_str(jthread->parent_name, "")); + javaThread = true; + } + } + } + if (!javaThread) + { + sbIn->appendf (GTXT ("Process %llu, Thread %llu"), + (unsigned long long) proc, (unsigned long long) thrid); + if (hasJava) + // sometimes threads start as native and later become Java; keep checking + isFinal = false; + } + if (ctx && ctx->dbev && ctx->dbev->comparingExperiments ()) + { + Vector <Histable *> *v = ctx->exp->get_comparable_objs (); + int st = 0; + for (long i = 0, sz = VecSize (v); i < sz; i++) + { + Experiment *exp = (Experiment *) v->get (i); + if (exp) + { + if (st == 0) + { + st = 1; + continue; + } + sbIn->appendf (GTXT (" [ Group %llu Process %llu ]"), + (unsigned long long) exp->groupId - 1, + (unsigned long long) exp->getUserExpId ()); + } + } + } + return isFinal; +} + +static bool +printProcess (StringBuilder *sbIn, Expression::Context * ctx, uint64_t id) +{ + uint64_t proc = id; + if (ctx && ctx->exp) + { + int st = 0; + if (ctx->dbev && ctx->dbev->comparingExperiments ()) + { + Vector <Histable *> *v = ctx->exp->get_comparable_objs (); + for (long i = 0, sz = VecSize (v); i < sz; i++) + { + Experiment *exp = (Experiment *) v->get (i); + if (exp) + { + if (st == 0) + { + st = 1; + sbIn->appendf (GTXT ("%s, Process %3llu, PID %llu"), + get_str (exp->utargname, GTXT ("(unknown)")), + (unsigned long long) proc, + (unsigned long long) exp->getPID ()); + continue; + } + sbIn->appendf (GTXT (" [ Group %llu, Process %llu, PID %llu ]"), + (unsigned long long) exp->groupId - 1, + (unsigned long long) exp->getUserExpId (), + (unsigned long long) exp->getPID ()); + } + } + } + if (st == 0) + sbIn->appendf (GTXT ("%s, Process %3llu, PID %llu"), + get_str (ctx->exp->utargname, GTXT ("(unknown)")), + (unsigned long long) proc, + (unsigned long long) ctx->exp->getPID ()); + } + else + sbIn->appendf (GTXT ("Process %3llu"), (unsigned long long) proc); + return true; //name is final +} + +static bool +printExperiment (StringBuilder *sbIn, Expression::Context * ctx, uint64_t id) +{ + uint64_t grpId = extractExpgrid (id); + uint64_t expid = extractExpid (id); + if (ctx && ctx->dbev->comparingExperiments ()) + printCompareLabel (sbIn, grpId); + if (ctx) + { + Experiment *hasFounder = ctx->exp->founder_exp; + int pid = ctx->exp->getPID (); + uint64_t founderExpid; + if (hasFounder) + founderExpid = hasFounder->getUserExpId (); + else + founderExpid = expid; + sbIn->appendf (GTXT ("Base Experiment %llu, Process %llu, PID %llu, %s"), + (unsigned long long) founderExpid, + (unsigned long long) expid, + (unsigned long long) pid, + get_str (ctx->exp->utargname, GTXT ("(unknown)"))); + } + else + sbIn->appendf (GTXT ("Process %llu"), (unsigned long long) expid); + return true; // name is final +} + +void +IndexObject::set_name_from_context (Expression::Context * ctx) +{ + if (name != NULL) + if (nameIsFinal && strstr (name, GTXT ("(unknown)")) == NULL) + return; + if (ctx == NULL || ctx->dview == NULL || ctx->dbev == NULL) + return; + StringBuilder sb; + switch (indextype) + { + case INDEX_THREADS: + nameIsFinal = printThread (&sb, ctx, id); + break; + case INDEX_PROCESSES: + nameIsFinal = printProcess (&sb, ctx, id); + break; + case INDEX_EXPERIMENTS: + nameIsFinal = printExperiment (&sb, ctx, id); + break; + default: + name = NULL; + return; + } + if (sb.length ()) + name = sb.toString (); +} + +static void +printCompareLabel (StringBuilder *sbIn, uint64_t grpId) +{ + static const char *labels[] = {"", GTXT ("Baseline"), GTXT ("Comparison")}; + static int length; + if (!length) + { + length = strlen (labels[1]); + int length2 = strlen (labels[2]); + if (length < length2) + length = length2; + length += 5; // for open/close brace and grpId number and spaces + } + char *s = NULL; + if (grpId != 0) + { + if (grpId <= 2) + s = dbe_sprintf ("[%s]", labels[grpId]); + else + s = dbe_sprintf ("[%s-%llu]", labels[2], + (unsigned long long) (grpId - 1)); + } + sbIn->appendf ("%-*s", length, get_str (s, "")); + free (s); +} + +char * +IndexObject::get_name (NameFormat fmt) +{ + if (name == NULL) + { + StringBuilder sb; + int64_t upper; + int64_t num1; + int64_t num2; + switch (indextype) + { + case INDEX_THREADS: + printThread (&sb, NULL, id); + break; + + case INDEX_CPUS: + sb.sprintf (GTXT ("CPU %llu"), (unsigned long long) id); + break; + + case INDEX_SAMPLES: + sb.sprintf (GTXT ("Sample %llu"), (unsigned long long) id); + break; + + case INDEX_GCEVENTS: + if (id == 0) + { + sb.sprintf (GTXT ("Not in any GCEvent")); + } + else + { + sb.sprintf (GTXT ("GCEvent %llu"), (unsigned long long) id); + } + break; + + case INDEX_SECONDS: + sb.sprintf (GTXT ("Second of execution %llu"), (unsigned long long) id); + break; + + case INDEX_PROCESSES: + printProcess (&sb, NULL, id); + break; + + case INDEX_EXPERIMENTS: + printExperiment (&sb, NULL, id); + break; + case INDEX_BYTES: + upper = id; + if (id == -1) + { + break; + } + if (id % 2 == 1 && id > 1) + { + upper = id - 1; + if (upper >= 1099511627776) + { + num1 = upper / 1099511627776; + sb.sprintf (GTXT (">= %3llu TB"), (unsigned long long) num1); + } + else + { + // XXXX do nothing, this should not happen + } + } + else + { + if (upper >= 1099511627776) + { + num1 = upper / 1099511627776; + num2 = num1 / 4; + if (num2) + { + sb.sprintf (GTXT ("%3lluTB < n <= %3lluTB"), (unsigned long long) num2, (unsigned long long) num1); + } + else + { + sb.sprintf (GTXT ("256GB < n <= %3lluTB"), (unsigned long long) num1); + } + } + else if (upper >= 1073741824) + { + num1 = upper / 1073741824; + num2 = num1 / 4; + if (num2) + { + sb.sprintf (GTXT ("%3lluGB < n <= %3lluGB"), (unsigned long long) num2, (unsigned long long) num1); + } + else + { + sb.sprintf (GTXT ("256MB < n <= %3lluGB"), (unsigned long long) num1); + } + } + else if (upper >= 1048576) + { + num1 = upper / 1048576; + num2 = num1 / 4; + if (num2) + { + sb.sprintf (GTXT ("%3lluMB < n <= %3lluMB"), (unsigned long long) num2, (unsigned long long) num1); + } + else + { + sb.sprintf (GTXT ("256KB < n <= %3lluMB"), (unsigned long long) num1); + } + } + else if (upper >= 1024) + { + num1 = upper / 1024; + num2 = num1 / 4; + if (num2) + { + sb.sprintf (GTXT ("%3lluKB < n <= %3lluKB"), (unsigned long long) num2, (unsigned long long) num1); + } + else + { + sb.sprintf (GTXT (" 256 < n <= %3lluKB"), (unsigned long long) num1); + } + } + else if (upper > 0) + { + num1 = upper; + num2 = num1 / 4; + if (num1 == 1) + { + sb.sprintf (GTXT (" 1 Byte")); + } + else + { + sb.sprintf (GTXT ("%5llu < n <= %5llu Bytes"), (unsigned long long) num2, (unsigned long long) num1); + } + } + else if (upper == 0) + { + sb.sprintf (GTXT (" 0 Bytes")); + } + else + { + sb.sprintf (GTXT ("<No Data>")); + } + } + break; + case INDEX_DURATION: + if (id == -1) + { + break; + } + + if (id > 10000000000000) + { + sb.sprintf (GTXT ("n > 10000s")); + } + else if (id > 1000000000000) + { + sb.sprintf (GTXT ("1000s < n <= 10000s")); + } + else if (id > 100000000000) + { + sb.sprintf (GTXT (" 100s < n <= 1000s")); + } + else if (id > 10000000000) + { + sb.sprintf (GTXT (" 10s < n <= 100s")); + } + else if (id > 1000000000) + { + sb.sprintf (GTXT (" 1s < n <= 10s")); + } + else if (id > 100000000) + { + sb.sprintf (GTXT ("100ms < n <= 1s")); + } + else if (id > 10000000) + { + sb.sprintf (GTXT (" 10ms < n <= 100ms")); + } + else if (id > 1000000) + { + sb.sprintf (GTXT (" 1ms < n <= 10ms")); + } + else if (id > 100000) + { + sb.sprintf (GTXT ("100us < n <= 1ms")); + } + else if (id > 10000) + { + sb.sprintf (GTXT (" 10us < n <= 100us")); + } + else if (id > 1000) + { + sb.sprintf (GTXT (" 1us < n <= 10us")); + } + else if (id > 0) + { + sb.sprintf (GTXT (" 0s < n <= 1us")); + } + else if (id == 0) + { + sb.sprintf (GTXT (" 0s")); + } + else + { + sb.sprintf (GTXT ("<No Data>")); + } + break; + + // Custom index objects + default: + if (obj) + sb.sprintf (GTXT ("%s from %s"), + dbeSession->getIndexSpaceDescr (indextype), obj->get_name (fmt)); + else + { + IndexObjType_t *indexObj = dbeSession->getIndexSpace (indextype); + if (indexObj->memObj) + { + if (strcasecmp (indexObj->name, NTXT ("Memory_page_size")) == 0) + { + if (id == 0) + sb.append (GTXT ("<Unknown>")); + else + sb.sprintf (NTXT ("%s 0x%16.16llx (%llu)"), indexObj->name, + (unsigned long long) id, (unsigned long long) id); + } + else if (strcasecmp (indexObj->name, NTXT ("Memory_in_home_lgrp")) == 0) + { + if (id == 0 || id == 1) + sb.sprintf (NTXT ("%s: %s"), indexObj->name, + id == 1 ? GTXT ("True") : GTXT ("False")); + else + sb.sprintf (NTXT ("%s %s (0x%llx"), indexObj->name, + GTXT ("<Unknown>"), (unsigned long long) id); + } + else if (strcasecmp (indexObj->name, NTXT ("Memory_lgrp")) == 0) + { + if (id == 0) + sb.append (GTXT ("<Unknown>")); + else + sb.sprintf (NTXT ("%s %llu"), indexObj->name, (unsigned long long) id); + } + else + sb.sprintf (NTXT ("%s 0x%16.16llx"), indexObj->name, (unsigned long long) id); + } + else + sb.sprintf ("%s 0x%16.16llx (%llu)", indexObj->name, + (unsigned long long) id, (unsigned long long) id); + } + } + name = sb.toString (); + nameIsFinal = true; + } + return name; +} + +bool +IndexObject::requires_string_sort () +{ + if (indextype == INDEX_PROCESSES || indextype >= INDEX_LAST) + return true; + return false; +} + +Histable * +IndexObject::convertto (Histable_type type, Histable *ext) +{ + if (type == INDEXOBJ) + return this; + if (obj) + return obj->convertto (type, ext); + return NULL; +} + +IndexObjType_t::IndexObjType_t () +{ + type = 0; + name = NULL; + i18n_name = NULL; + index_expr_str = NULL; + index_expr = NULL; + mnemonic = 0; + short_description = NULL; + long_description = NULL; + memObj = NULL; +} + +IndexObjType_t::~IndexObjType_t () +{ + free (name); + free (i18n_name); + free (index_expr_str); + delete index_expr; + free (short_description); + free (long_description); +} diff --git a/gprofng/src/IndexObject.h b/gprofng/src/IndexObject.h new file mode 100644 index 0000000..23f21d3 --- /dev/null +++ b/gprofng/src/IndexObject.h @@ -0,0 +1,111 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _INDEXOBJECT_H +#define _INDEXOBJECT_H + +#include "Histable.h" +#include "Expression.h" + +class IndexObject : public Histable +{ +public: + IndexObject (int _indextype, uint64_t _index); + IndexObject (int _indextype, Histable *_obj); + bool requires_string_sort (); // name column should be sorted using name text + + virtual Histable_type + get_type () + { + return INDEXOBJ; + } + + virtual char *get_name (NameFormat = NA); + virtual void set_name (char*); + virtual void set_name_from_context (Expression::Context *); + virtual Histable *convertto (Histable_type, Histable* = NULL); + + virtual uint64_t + get_addr () + { + return id; + } + + uint64_t + get_index () + { + return id; + } + + Histable * + get_obj () + { + return obj; + } + + // for use in index object definitions + static const uint64_t INDXOBJ_EXPGRID_SHIFT = 60; + static const uint64_t INDXOBJ_EXPID_SHIFT = 32; + static const uint64_t INDXOBJ_PAYLOAD_SHIFT = 0; + static const uint64_t INDXOBJ_EXPGRID_MASK = + ((1LLU << (64 - INDXOBJ_EXPGRID_SHIFT)) - 1); + static const uint64_t INDXOBJ_EXPID_MASK = + ((1LLU << (INDXOBJ_EXPGRID_SHIFT - INDXOBJ_EXPID_SHIFT)) - 1); + static const uint64_t INDXOBJ_PAYLOAD_MASK = + ((1LLU << (INDXOBJ_EXPID_SHIFT - INDXOBJ_PAYLOAD_SHIFT)) - 1); + +private: + + int indextype; + Histable *obj; + bool nameIsFinal; +}; + +typedef enum IndexObjTypes +{ + INDEX_THREADS = 0, + INDEX_CPUS, + INDEX_SAMPLES, + INDEX_GCEVENTS, + INDEX_SECONDS, + INDEX_PROCESSES, + INDEX_EXPERIMENTS, + INDEX_BYTES, + INDEX_DURATION, + INDEX_LAST // never used; marks the count of precompiled items +} IndexObjTypes_t; + +class IndexObjType_t +{ +public: + IndexObjType_t (); + ~IndexObjType_t (); + int type; + char *name; // used as input + char *i18n_name; // used for output + char *index_expr_str; + Expression *index_expr; + char mnemonic; + char *short_description; + char *long_description; + MemObjType_t *memObj; +}; + +#endif /* _INDEXOBJECT_H */ diff --git a/gprofng/src/IntervalMap.h b/gprofng/src/IntervalMap.h new file mode 100644 index 0000000..8c20474 --- /dev/null +++ b/gprofng/src/IntervalMap.h @@ -0,0 +1,194 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * Interval Map implementation. + * + * Interval Map makes the following assumptions: + * - if duplicate keys, the last one will be stored + * - <TBC> + */ +#ifndef _DBE_INTERVALMAP_H +#define _DBE_INTERVALMAP_H + +#include <assert.h> +#include <vec.h> +#include <Map.h> + +template <typename Key_t, typename Value_t> +class IntervalMap : public Map<Key_t, Value_t> +{ +public: + + IntervalMap (); + ~IntervalMap (); + void put (Key_t key, Value_t val); + Value_t get (Key_t key); + Value_t get (Key_t key, typename Map<Key_t, Value_t>::Relation rel); + Value_t remove (Key_t key); + +private: + + struct Entry + { + Key_t key; + Value_t val; + }; + + static const int CHUNK_SIZE; + + int entries; + int nchunks; + Entry **chunks; + Vector<Entry*> *index; +}; + +template <typename Key_t, typename Value_t> +const int IntervalMap<Key_t, Value_t>::CHUNK_SIZE = 16384; + +template <typename Key_t, typename Value_t> +IntervalMap<Key_t, Value_t>::IntervalMap () +{ + entries = 0; + nchunks = 0; + chunks = NULL; + index = new Vector<Entry*>; +} + +template <typename Key_t, typename Value_t> +IntervalMap<Key_t, Value_t>::~IntervalMap () +{ + for (int i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; + delete index; +} + +template <typename Key_t, typename Value_t> +void +IntervalMap<Key_t, Value_t>::put (Key_t key, Value_t val) +{ + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + Entry *entry = index->fetch (md); + int cmp = entry->key < key ? -1 : entry->key > key ? 1 : 0; + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + entry->val = val; + return; + } + } + + if (entries >= nchunks * CHUNK_SIZE) + { + nchunks++; + // Reallocate Entry chunk array + Entry **new_chunks = new Entry*[nchunks]; + for (int i = 0; i < nchunks - 1; i++) + new_chunks[i] = chunks[i]; + delete chunks; + chunks = new_chunks; + + // Allocate new chunk for entries. + chunks[nchunks - 1] = new Entry[CHUNK_SIZE]; + } + Entry *entry = &chunks[entries / CHUNK_SIZE][entries % CHUNK_SIZE]; + entry->key = key; + entry->val = val; + index->insert (lo, entry); + entries++; +} + +template <typename Key_t, typename Value_t> +Value_t +IntervalMap<Key_t, Value_t>::get (Key_t key) +{ + return get (key, Map<Key_t, Value_t>::REL_EQ); +} + +template <typename Key_t, typename Value_t> +Value_t +IntervalMap<Key_t, Value_t>::get (Key_t key, typename Map<Key_t, Value_t>::Relation rel) +{ + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + Entry *entry = index->fetch (md); + int cmp = entry->key < key ? -1 : entry->key > key ? 1 : 0; + switch (rel) + { + case Map<Key_t, Value_t>::REL_LT: + if (cmp < 0) + lo = md + 1; + else + hi = md - 1; + break; + case Map<Key_t, Value_t>::REL_GT: + if (cmp <= 0) + lo = md + 1; + else + hi = md - 1; + break; + case Map<Key_t, Value_t>::REL_LE: + case Map<Key_t, Value_t>::REL_GE: + case Map<Key_t, Value_t>::REL_EQ: + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + return entry->val; + break; + } + } + switch (rel) + { + case Map<Key_t, Value_t>::REL_LT: + case Map<Key_t, Value_t>::REL_LE: + return hi >= 0 ? index->fetch (hi)->val : (Value_t) 0; + case Map<Key_t, Value_t>::REL_GT: + case Map<Key_t, Value_t>::REL_GE: + return lo < entries ? index->fetch (lo)->val : (Value_t) 0; + case Map<Key_t, Value_t>::REL_EQ: + break; + } + return (Value_t) 0; +} + +template <typename Key_t, typename Value_t> +Value_t +IntervalMap<Key_t, Value_t>::remove (Key_t) +{ + // Not implemented + if (1) + assert (0); + return (Value_t) 0; +} + +#endif diff --git a/gprofng/src/LoadObject.cc b/gprofng/src/LoadObject.cc new file mode 100644 index 0000000..d9ce3e8 --- /dev/null +++ b/gprofng/src/LoadObject.cc @@ -0,0 +1,1242 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <errno.h> + +#include "util.h" +#include "StringBuilder.h" +#include "Application.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "DataObject.h" +#include "Elf.h" +#include "Function.h" +#include "Module.h" +#include "ClassFile.h" +#include "Stabs.h" +#include "LoadObject.h" +#include "dbe_types.h" +#include "DbeFile.h" +#include "ExpGroup.h" + +enum +{ + LO_InstHTableSize = 4096, + HTableSize = 1024 +}; + +LoadObject * +LoadObject::create_item (const char *nm, int64_t chksum) +{ + LoadObject *lo = new LoadObject (nm); + lo->checksum = chksum; + dbeSession->append (lo); + return lo; +} + +LoadObject * +LoadObject::create_item (const char *nm, const char *_runTimePath, DbeFile *df) +{ + LoadObject *lo = new LoadObject (nm); + lo->runTimePath = dbe_strdup (_runTimePath); + lo->dbeFile->orig_location = dbe_strdup (_runTimePath); + if (df) + { + if ((df->filetype & DbeFile::F_JAR_FILE) != 0) + { + if (lo->dbeFile->find_in_jar_file (nm, df->get_jar_file ())) + { + lo->dbeFile->inArchive = df->inArchive; + lo->dbeFile->container = df; + } + } + else + { + lo->dbeFile->set_location (df->get_location ()); + lo->dbeFile->sbuf = df->sbuf; + lo->dbeFile->inArchive = df->inArchive; + } + } + dbeSession->append (lo); + return lo; +} + +LoadObject::LoadObject (const char *loname) +{ + flags = 0; + size = 0; + type = SEG_UNKNOWN; + isReadStabs = false; + need_swap_endian = false; + instHTable = new DbeInstr*[LO_InstHTableSize]; + for (int i = 0; i < LO_InstHTableSize; i++) + instHTable[i] = NULL; + + functions = new Vector<Function*>; + funcHTable = new Function*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + funcHTable[i] = NULL; + + seg_modules = new Vector<Module*>; + modules = new HashMap<char*, Module*>; + platform = Unknown; + noname = dbeSession->createUnknownModule (this); + modules->put (noname->get_name (), noname); + pathname = NULL; + arch_name = NULL; + runTimePath = NULL; + objStabs = NULL; + firstExp = NULL; + seg_modules_map = NULL; + comp_funcs = NULL; + warnq = new Emsgqueue (NTXT ("lo_warnq")); + commentq = new Emsgqueue (NTXT ("lo_commentq")); + elf_lo = NULL; + elf_inited = false; + checksum = 0; + isUsed = false; + h_function = NULL; + h_instr = NULL; + + char *nm = (char *) loname; + if (strncmp (nm, NTXT ("./"), 2) == 0) + nm += 2; + set_name (nm); + dbeFile = new DbeFile (nm); + dbeFile->filetype |= DbeFile::F_LOADOBJ | DbeFile::F_FILE; +} + +LoadObject::~LoadObject () +{ + delete seg_modules_map; + delete functions; + delete[] instHTable; + delete[] funcHTable; + delete seg_modules; + delete modules; + delete elf_lo; + free (pathname); + free (arch_name); + free (runTimePath); + delete objStabs; + delete warnq; + delete commentq; + delete h_instr; +} + +Elf * +LoadObject::get_elf () +{ + if (elf_lo == NULL) + { + if (dbeFile->get_need_refind ()) + elf_inited = false; + if (elf_inited) + return NULL; + elf_inited = true; + char *fnm = dbeFile->get_location (); + if (fnm == NULL) + { + append_msg (CMSG_ERROR, GTXT ("Cannot find file: `%s'"), + dbeFile->get_name ()); + return NULL; + } + Elf::Elf_status st = Elf::ELF_ERR_CANT_OPEN_FILE; + elf_lo = Elf::elf_begin (fnm, &st); + if (elf_lo == NULL) + switch (st) + { + case Elf::ELF_ERR_CANT_OPEN_FILE: + append_msg (CMSG_ERROR, GTXT ("Cannot open ELF file `%s'"), fnm); + break; + case Elf::ELF_ERR_BAD_ELF_FORMAT: + default: + append_msg (CMSG_ERROR, GTXT ("Cannot read ELF header of `%s'"), + fnm); + break; + } + } + return elf_lo; +} + +Stabs * +LoadObject::openDebugInfo (char *fname, Stabs::Stab_status *stp) +{ + if (objStabs == NULL) + { + if (fname == NULL) + return NULL; + objStabs = new Stabs (fname, get_pathname ()); + Stabs::Stab_status st = objStabs->get_status (); + if ((st == Stabs::DBGD_ERR_NONE) && (checksum != 0)) + { + Elf *elf = get_elf (); + if (elf && (checksum != elf->elf_checksum ())) + { + char *buf = dbe_sprintf (GTXT ("*** Note: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored"), + fname); + commentq->append (new Emsg (CMSG_ERROR, buf)); + delete buf; + st = Stabs::DBGD_ERR_CHK_SUM; + } + } + if (stp) + *stp = st; + if (st != Stabs::DBGD_ERR_NONE) + { + delete objStabs; + objStabs = NULL; + } + } + return objStabs; +} + +uint64_t +LoadObject::get_addr () +{ + return MAKE_ADDRESS (seg_idx, 0); +} + +bool +LoadObject::compare (const char *_path, int64_t _checksum) +{ + return _checksum == checksum && dbe_strcmp (_path, get_pathname ()) == 0; +} + +int +LoadObject::compare (const char *_path, const char *_runTimePath, DbeFile *df) +{ + int ret = 0; + if (dbe_strcmp (_path, get_pathname ()) != 0) + return ret; + ret |= CMP_PATH; + if (_runTimePath) + { + if (dbe_strcmp (_runTimePath, runTimePath) != 0) + return ret; + ret |= CMP_RUNTIMEPATH; + } + if (df && dbeFile->compare (df)) + ret |= CMP_CHKSUM; + return ret; +} + +void +LoadObject::set_platform (Platform_t pltf, int wsz) +{ + switch (pltf) + { + case Sparc: + case Sparcv9: + case Sparcv8plus: + platform = (wsz == W64) ? Sparcv9 : Sparc; + break; + case Intel: + case Amd64: + platform = (wsz == W64) ? Amd64 : Intel; + break; + default: + platform = pltf; + break; + } +}; + +void +LoadObject::set_name (char *string) +{ + char *p; + pathname = dbe_strdup (string); + + p = get_basename (pathname); + if (p[0] == '<') + name = dbe_strdup (p); + else // set a short name to "<basename>" + name = dbe_sprintf (NTXT ("<%s>"), p); +} + +void +LoadObject::dump_functions (FILE *out) +{ + int index; + Function *fitem; + char *sname, *mname; + if (platform == Java) + { + JMethod *jmthd; + Vector<JMethod*> *jmethods = (Vector<JMethod*>*)functions; + Vec_loop (JMethod*, jmethods, index, jmthd) + { + fprintf (out, "id %6llu, @0x%llx sz-%lld %s (module = %s)\n", + (unsigned long long) jmthd->id, (long long) jmthd->get_mid (), + (long long) jmthd->size, jmthd->get_name (), + jmthd->module ? jmthd->module->file_name : noname->file_name); + } + } + else + { + Vec_loop (Function*, functions, index, fitem) + { + if (fitem->alias && fitem->alias != fitem) + fprintf (out, "id %6llu, @0x%llx - %s == alias of '%s'\n", + (ull_t) fitem->id, (ull_t) fitem->img_offset, + fitem->get_name (), fitem->alias->get_name ()); + else + { + mname = fitem->module ? fitem->module->file_name : noname->file_name; + sname = fitem->getDefSrcName (); + fprintf (out, + "id %6llu, @0x%llx - 0x%llx [save 0x%llx] o-%lld sz-%lld %s (module = %s)", + (ull_t) fitem->id, (ull_t) fitem->img_offset, + (ull_t) (fitem->img_offset + fitem->size), + (ull_t) fitem->save_addr, (ull_t) fitem->img_offset, + (ll_t) fitem->size, fitem->get_name (), mname); + if (sname && !streq (sname, mname)) + fprintf (out, " (Source = %s)", sname); + fprintf (out, "\n"); + } + } + } +} + +int +LoadObject::get_index (Function *func) +{ + Function *fp; + uint64_t offset; + int x; + int left = 0; + int right = functions->size () - 1; + offset = func->img_offset; + while (left <= right) + { + x = (left + right) / 2; + fp = functions->fetch (x); + + if (left == right) + { + if (offset >= fp->img_offset + fp->size) + return -1; + if (offset >= fp->img_offset) + return x; + return -1; + } + if (offset < fp->img_offset) + right = x - 1; + else if (offset >= fp->img_offset + fp->size) + left = x + 1; + else + return x; + } + return -1; +} + +char * +LoadObject::get_alias (Function *func) +{ + Function *fp, *alias; + int index, nsize; + static char buf[1024]; + if (func->img_offset == 0 || func->alias == NULL) + return NULL; + int fid = get_index (func); + if (fid == -1) + return NULL; + + nsize = functions->size (); + alias = func->alias; + for (index = fid; index < nsize; index++) + { + fp = functions->fetch (index); + if (fp->alias != alias) + { + fid = index; + break; + } + } + + *buf = '\0'; + for (index--; index >= 0; index--) + { + fp = functions->fetch (index); + if (fp->alias != alias) + break; + if (fp != alias) + { + size_t len = strlen (buf); + if (*buf != '\0') + { + snprintf (buf + len, sizeof (buf) - len, NTXT (", ")); + len = strlen (buf); + } + snprintf (buf + len, sizeof (buf) - len, "%s", fp->get_name ()); + } + } + return buf; +} + +DbeInstr* +LoadObject::find_dbeinstr (uint64_t file_off) +{ + int hash = (((int) file_off) >> 2) & (LO_InstHTableSize - 1); + DbeInstr *instr = instHTable[hash]; + if (instr && instr->img_offset == file_off) + return instr; + Function *fp = find_function (file_off); + if (fp == NULL) + fp = dbeSession->get_Unknown_Function (); + uint64_t func_off = file_off - fp->img_offset; + instr = fp->find_dbeinstr (0, func_off); + instHTable[hash] = instr; + return instr; +} + +Function * +LoadObject::find_function (uint64_t foff) +{ + // Look up in the hash table + int hash = (((int) foff) >> 6) & (HTableSize - 1); + Function *func = funcHTable[hash]; + if (func && foff >= func->img_offset && foff < func->img_offset + func->size) + return func->alias ? func->alias : func; + + // Use binary search + func = NULL; + int left = 0; + int right = functions->size () - 1; + while (left <= right) + { + int x = (left + right) / 2; + Function *fp = functions->fetch (x); + assert (fp != NULL); + + if (foff < fp->img_offset) + right = x - 1; + else if (foff >= fp->img_offset + fp->size) + left = x + 1; + else + { + func = fp; + break; + } + } + + // Plug the hole with a static function + char *func_name = NULL; + Size low_bound = 0, high_bound = 0; + if (func == NULL) + { + int last = functions->size () - 1; + uint64_t usize = (uint64_t) size; + if (foff >= usize) + { + // Cannot map to this LoadObject. Probably LoadObject was changed. + if (last >= 0 && functions->fetch (last)->img_offset == usize) + { + // Function is already created + func = functions->fetch (last); + if (func->size < 0 || (uint64_t) func->size < foff - usize) + func->size = foff - usize; + } + else + { + low_bound = size; + high_bound = foff; + func_name = dbe_sprintf (GTXT ("<static>@0x%llx (%s) -- no functions found"), + low_bound, name); + } + } + else if (last < 0) + { + low_bound = 0; + high_bound = size; + func_name = dbe_sprintf (GTXT ("<static>@0x%llx (%s) -- no functions found"), + low_bound, name); + } + else if (foff < functions->fetch (0)->img_offset) + { + low_bound = 0; + high_bound = functions->fetch (0)->img_offset; + } + else + { + Function *fp = functions->fetch (last); + if (foff >= fp->img_offset + fp->size) + { + low_bound = fp->img_offset + fp->size; + high_bound = size; + } + else + { + fp = functions->fetch (left); + if (foff >= fp->img_offset + fp->size) + { + low_bound = fp->img_offset + fp->size; + high_bound = functions->fetch (left + 1)->img_offset; + } + else + { + Function *fp1 = functions->fetch (left - 1); + low_bound = fp1->img_offset + fp1->size; + high_bound = fp->img_offset; + } + } + } + } + + if (func == NULL) + { + func = dbeSession->createFunction (); + func->size = (unsigned) (high_bound - low_bound); + func->module = noname; + func->img_fname = get_pathname (); + func->img_offset = (off_t) low_bound; + noname->functions->append (func); // unordered + if (func_name == NULL) + func_name = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"), low_bound, + name); + func->set_name (func_name); + free (func_name); + + // now insert the function + functions->insert (left, func); + } + + // Update the hash table + funcHTable[hash] = func; + return func->alias ? func->alias : func; +} + +static void +fixFuncAlias (Vector<Function*> *SymLst) +{ + int ind, i, k; + int64_t len, bestLen, maxSize; + Function *sym, *bestAlias; + + // XXXX it is a clone of Stabs::fixSymtabAlias() + ind = SymLst->size () - 1; + for (i = 0; i < ind; i++) + { + bestAlias = SymLst->fetch (i); + if (bestAlias->img_offset == 0) // Ignore this bad symbol + continue; + sym = SymLst->fetch (i + 1); + if (bestAlias->img_offset != sym->img_offset) + { + if (bestAlias->size == 0 + || sym->img_offset < bestAlias->img_offset + bestAlias->size) + bestAlias->size = (int) (sym->img_offset - bestAlias->img_offset); + continue; + } + + // Find a "best" alias + bestLen = strlen (bestAlias->get_name ()); + maxSize = bestAlias->size; + for (k = i + 1; k <= ind; k++) + { + sym = SymLst->fetch (k); + if (bestAlias->img_offset != sym->img_offset) + { // no more aliases + if ((maxSize == 0) || + (sym->img_offset < bestAlias->img_offset + maxSize)) + maxSize = sym->img_offset - bestAlias->img_offset; + break; + } + if (maxSize < sym->size) + maxSize = sym->size; + len = strlen (sym->get_name ()); + if (len < bestLen) + { + bestAlias = sym; + bestLen = len; + } + } + for (; i < k; i++) + { + sym = SymLst->fetch (i); + sym->alias = bestAlias; + sym->size = maxSize; + } + i--; + } +} + +void +LoadObject::post_process_functions () +{ + if (flags & SEG_FLAG_DYNAMIC || platform == Java) + return; + + char *msg = GTXT ("Processing Load Object Data"); + if (dbeSession->is_interactive ()) + theApplication->set_progress (1, msg); + + // First sort the functions + functions->sort (func_compare); + fixFuncAlias (functions); + + Module *mitem; + int index; + Vec_loop (Module*, seg_modules, index, mitem) + { + mitem->functions->sort (func_compare); + } + + // Find any derived functions, and set their derivedNode + Function *fitem; + Vec_loop (Function*, functions, index, fitem) + { + if (dbeSession->is_interactive () && index % 5000 == 0) + { + int percent = (int) (100.0 * index / functions->size ()); + theApplication->set_progress (percent, (percent != 0) ? NULL : msg); + } + fitem->findDerivedFunctions (); + } + + // 4987698: get the alias name for MAIN_ + fitem = find_function (NTXT ("MAIN_")); + if (fitem) + fitem->module->read_stabs (); + fitem = find_function (NTXT ("@plt")); + if (fitem) + fitem->flags |= FUNC_FLAG_PLT; + if (dbeSession->is_interactive ()) + theApplication->set_progress (0, NTXT ("")); +} + +int +LoadObject::func_compare (const void *p1, const void *p2) +{ + Function *f1 = *(Function **) p1; + Function *f2 = *(Function **) p2; + if (f1->img_offset != f2->img_offset) + return f1->img_offset > f2->img_offset ? 1 : -1; + + // annotated source not available for weak symbols. + if ((f1->module->flags & MOD_FLAG_UNKNOWN) != 0) + { + if ((f2->module->flags & MOD_FLAG_UNKNOWN) == 0) + return -1; + } + else if ((f2->module->flags & MOD_FLAG_UNKNOWN) != 0) + return 1; + return strcoll (f1->get_name (), f2->get_name ()); +} + +Function * +LoadObject::find_function (char *fname) +{ + Function *fitem; + int index; + Vec_loop (Function*, functions, index, fitem) + { + if (strcmp (fitem->get_name (), fname) == 0) + return fitem; + } + return (Function *) NULL; +} + +Function * +LoadObject::find_function (char *fname, unsigned int chksum) +{ + Function *fitem; + int index; + Vec_loop (Function*, functions, index, fitem) + { + if (fitem->chksum == chksum && strcmp (fitem->get_name (), fname) == 0) + return fitem; + } + return (Function *) NULL; +} + +Module * +LoadObject::find_module (char *mname) +{ + for (int i = 0, sz = seg_modules ? seg_modules->size () : 0; i < sz; i++) + { + Module *module = seg_modules->fetch (i); + if (strcmp (module->get_name (), mname) == 0) + return module; + } + return (Module *) NULL; +} + +LoadObject::Arch_status +LoadObject::sync_read_stabs () +{ + Arch_status st = ARCHIVE_SUCCESS; + if (!isReadStabs) + { + aquireLock (); + if (!isReadStabs) + { + st = read_stabs (); + post_process_functions (); + isReadStabs = true; + } + releaseLock (); + } + return st; +} + +LoadObject::Arch_status +LoadObject::read_stabs () +{ + if ((dbeFile->filetype & DbeFile::F_FICTION) != 0) + return ARCHIVE_SUCCESS; + Arch_status stabs_status = ARCHIVE_ERR_OPEN; + if (platform == Java) + { + Module *cf = NULL; + for (int i = 0, sz = seg_modules ? seg_modules->size () : 0; i < sz; i++) + { + Module *mod = seg_modules->fetch (i); + if (mod->dbeFile + && (mod->dbeFile->filetype & DbeFile::F_JAVACLASS) != 0) + { + cf = mod; + break; + } + } + if (cf) + { + int status = cf->readFile (); + switch (status) + { + case Module::AE_OK: + stabs_status = ARCHIVE_SUCCESS; + break; + case Module::AE_NOSTABS: + stabs_status = ARCHIVE_NO_STABS; + break; + case Module::AE_NOTREAD: + default: + stabs_status = ARCHIVE_ERR_OPEN; + break; + } + } + } + else if (strchr (pathname, '`')) + return ARCHIVE_SUCCESS; + else + { + Arch_status st = ARCHIVE_WRONG_ARCH; + Elf *elf = get_elf (); + if (elf == NULL) + { + if (read_archive () == 0) + st = ARCHIVE_SUCCESS; + else + { + char *msg = dbe_sprintf (GTXT ("*** Warning: Can't open file: %s"), + dbeFile->get_name ()); + warnq->append (new Emsg (CMSG_ERROR, msg)); + delete msg; + } + } + else if (checksum != 0 && checksum != elf->elf_checksum ()) + { + if (read_archive () == 0) + st = ARCHIVE_SUCCESS; + else + { + char *msg = dbe_sprintf ( + GTXT ("*** Note: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored"), + dbeFile->get_location ()); + commentq->append (new Emsg (CMSG_ERROR, msg)); + delete msg; + } + } + if (st == ARCHIVE_SUCCESS) // An old archive is used + return st; + + Stabs::Stab_status status = Stabs::DBGD_ERR_CANT_OPEN_FILE; + char *location = dbeFile->get_location (true); + if (location == NULL) + return ARCHIVE_ERR_OPEN; + + if (openDebugInfo (location, &status)) + { + status = objStabs->read_archive (this); + isRelocatable = objStabs->is_relocatable (); + size = objStabs->get_textsz (); + platform = objStabs->get_platform (); + wsize = objStabs->get_class (); + } + + switch (status) + { + case Stabs::DBGD_ERR_NONE: + stabs_status = ARCHIVE_SUCCESS; + break; + case Stabs::DBGD_ERR_CANT_OPEN_FILE: + stabs_status = ARCHIVE_ERR_OPEN; + break; + case Stabs::DBGD_ERR_BAD_ELF_LIB: + case Stabs::DBGD_ERR_BAD_ELF_FORMAT: + stabs_status = ARCHIVE_BAD_STABS; + break; + case Stabs::DBGD_ERR_NO_STABS: + stabs_status = ARCHIVE_NO_STABS; + break; + case Stabs::DBGD_ERR_NO_DWARF: + stabs_status = ARCHIVE_NO_DWARF; + break; + default: + stabs_status = ARCHIVE_BAD_STABS; + break; + } + } + return stabs_status; +} + +#define ARCH_STRLEN(s) ((strlen(s) + 4) & ~0x3 ) + +static int +offsetCmp (const void *a, const void *b) +{ + uint32_t o1 = ((inst_info_t *) a)->offset; + uint32_t o2 = ((inst_info_t *) b)->offset; + return o1 == o2 ? 0 : (o1 < o2 ? -1 : 1); +} + +int +LoadObject::read_archive () +{ + if (arch_name == NULL) + return 1; + Module *mod = NULL; + Function *func = NULL; + char *buf; + Data_window *dwin = new Data_window (arch_name); + if (dwin->not_opened ()) + { + delete dwin; + buf = dbe_sprintf (GTXT ("*** Warning: Error opening file for reading: %s: %s"), + arch_name, strerror (errno)); + warnq->append (new Emsg (CMSG_ERROR, buf)); + delete buf; + return 1; + } + dwin->need_swap_endian = need_swap_endian; + + // Prevent reading earlier archive files, which didn't support versioning. + int64_t offset = 0; + ARCH_common *cpkt = (ARCH_common*) dwin->bind (offset, sizeof (ARCH_common)); + uint16_t v16; + if (cpkt) + { + v16 = (uint16_t) cpkt->type; + if (dwin->decode (v16) != ARCH_SEGMENT) + cpkt = NULL; + } + if (cpkt == NULL) + { + buf = dbe_sprintf (GTXT ("archive file malformed %s"), arch_name); + warnq->append (new Emsg (CMSG_WARN, buf)); + delete buf; + return 1; + } + + char *msg = NULL; + unsigned long long pointer_invalid = 0; + for (int64_t last_offset = -5000;;) + { + cpkt = (ARCH_common*) dwin->bind (offset, sizeof (ARCH_common)); + if (cpkt == NULL) + break; + v16 = (uint16_t) cpkt->size; + uint32_t cpktsize = dwin->decode (v16); + cpkt = (ARCH_common*) dwin->bind (offset, cpktsize); + if ((cpkt == NULL) || (cpktsize == 0)) + { + buf = dbe_sprintf (GTXT ("archive file malformed %s"), arch_name); + warnq->append (new Emsg (CMSG_WARN, buf)); + delete buf; + break; + } + + // Update the progress bar + if (dbeSession->is_interactive () && ((offset - last_offset) >= 5000)) + { + last_offset = offset; + int percent = (int) (100.0 * offset / dwin->get_fsize ()); + if (msg == NULL) + msg = dbe_sprintf (GTXT ("Reading Load Object Data: %s"), name); + theApplication->set_progress (percent, (percent != 0) ? NULL : msg); + } + char *ptr = (char *) cpkt; + v16 = (uint16_t) cpkt->type; + switch (dwin->decode (v16)) + { + case ARCH_SEGMENT: + { + ARCH_segment *aseg = (ARCH_segment*) cpkt; + if (dwin->decode (aseg->version) != ARCH_VERSION) + { + buf = dbe_sprintf (GTXT ("Archive file version mismatch for %s"), arch_name); + warnq->append (new Emsg (CMSG_ERROR, buf)); + delete buf; + if (dbeSession->is_interactive ()) + theApplication->set_progress (0, ""); + return 1; + } + if (size == 0) + size = dwin->decode (aseg->textsz); + Platform_t pltf = (Platform_t) dwin->decode (aseg->platform); + if (pltf != Unknown) + { + platform = pltf; // override if known + wsize = (platform == Sparcv9 || platform == Amd64) ? W64 : W32; + } + break; + } + case ARCH_MSG: + { + ARCH_message *amsg = (ARCH_message*) cpkt; + buf = status_str ((Arch_status) dwin->decode (amsg->errcode)); + commentq->append (new Emsg (CMSG_ARCHIVE, buf)); + free (buf); + break; + } + case ARCH_INF: + { + ARCH_info *ainf = (ARCH_info*) cpkt; + Emsg *m = new Emsg (CMSG_ARCHIVE, (char*) (ainf + 1)); + commentq->append (m); + break; + } + case ARCH_MODULE: + { + ARCH_module *amod = (ARCH_module*) cpkt; + char *str = ((char*) amod) + sizeof (ARCH_module); + if (streq (str, SP_UNKNOWN_NAME) && + streq (str + ARCH_STRLEN (str), SP_UNKNOWN_NAME)) + { + mod = noname; + break; + } + mod = dbeSession->createModule (this, str); + mod->lang_code = (Sp_lang_code) dwin->decode (amod->lang_code); + mod->fragmented = dwin->decode (amod->fragmented); + str += ARCH_STRLEN (str); + mod->set_file_name (dbe_strdup (str)); + modules->put (get_basename (str), mod); + break; + } + case ARCH_FUNCTION: + { + if (mod == NULL) + break; + ARCH_function *afnc = (ARCH_function*) cpkt; + func = dbeSession->createFunction (); + func->img_offset = dwin->decode (afnc->offset); + func->size = dwin->decode (afnc->size); + func->save_addr = dwin->decode (afnc->save_addr) + - dwin->decode (afnc->offset); + func->module = mod; + func->set_name (((char*) afnc) + sizeof (ARCH_function)); + mod->functions->append (func); + functions->append (func); + break; + } + case ARCH_LDINSTR: + if (mod == NULL) + break; + Dprintf (DEBUG_LOADOBJ, "LDINSTR list for %s\n", mod->get_name ()); + if (mod->infoList == NULL) + mod->infoList = new Vector<inst_info_t*>; + for (memop_info_t *mp = (memop_info_t*) (ptr + sizeof (ARCH_aninfo)); + (char*) mp < ptr + cpktsize; mp++) + { + memop_info_t *memop = new memop_info_t; + memop->offset = dwin->decode (mp->offset); + memop->id = dwin->decode (mp->id); + memop->signature = dwin->decode (mp->signature); + memop->datatype_id = dwin->decode (mp->datatype_id); + mod->ldMemops.append (memop); + + inst_info_t *instop = new inst_info_t; + instop->type = CPF_INSTR_TYPE_LD; + instop->offset = memop->offset; + instop->memop = memop; + mod->infoList->incorporate (instop, offsetCmp); + Dprintf (DEBUG_LOADOBJ, + "ld: offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n", + memop->offset, memop->id, memop->signature, + memop->datatype_id); + } + Dprintf (DEBUG_LOADOBJ, "LDINSTR list of %lld for %s\n", + (long long) mod->ldMemops.size (), mod->get_name ()); + break; + case ARCH_STINSTR: + if (mod == NULL) + break; + Dprintf (DEBUG_LOADOBJ, NTXT ("STINSTR list for %s\n"), mod->get_name ()); + if (mod->infoList == NULL) + mod->infoList = new Vector<inst_info_t*>; + for (memop_info_t *mp = (memop_info_t*) (ptr + sizeof (ARCH_aninfo)); + ((char *) mp) < ptr + cpktsize; mp++) + { + memop_info_t *memop = new memop_info_t; + memop->offset = dwin->decode (mp->offset); + memop->id = dwin->decode (mp->id); + memop->signature = dwin->decode (mp->signature); + memop->datatype_id = dwin->decode (mp->datatype_id); + mod->stMemops.append (memop); + + inst_info_t *instop = new inst_info_t; + instop->type = CPF_INSTR_TYPE_ST; + instop->offset = memop->offset; + instop->memop = memop; + mod->infoList->incorporate (instop, offsetCmp); + Dprintf (DEBUG_LOADOBJ, + "st: offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n", + memop->offset, memop->id, memop->signature, + memop->datatype_id); + } + Dprintf (DEBUG_LOADOBJ, "STINSTR list of %lld for %s\n", + (long long) mod->stMemops.size (), mod->get_name ()); + break; + case ARCH_PREFETCH: + if (mod == NULL) + break; + Dprintf (DEBUG_LOADOBJ, "PFINSTR list for %s\n", mod->get_name ()); + if (mod->infoList == NULL) + mod->infoList = new Vector<inst_info_t*>; + for (memop_info_t *mp = (memop_info_t*) (ptr + sizeof (ARCH_aninfo)); + ((char*) mp) < ptr + cpkt->size; mp++) + { + memop_info_t *memop = new memop_info_t; + memop->offset = dwin->decode (mp->offset); + memop->id = dwin->decode (mp->id); + memop->signature = dwin->decode (mp->signature); + memop->datatype_id = dwin->decode (mp->datatype_id); + mod->pfMemops.append (memop); + + inst_info_t *instop = new inst_info_t; + instop->type = CPF_INSTR_TYPE_PREFETCH; + instop->offset = memop->offset; + instop->memop = memop; + mod->infoList->incorporate (instop, offsetCmp); + Dprintf (DEBUG_LOADOBJ, + "pf: offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n", + memop->offset, memop->id, memop->signature, + memop->datatype_id); + } + Dprintf (DEBUG_LOADOBJ, "PFINSTR list of %lld for %s\n", + (long long) mod->pfMemops.size (), mod->get_name ()); + break; + case ARCH_BRTARGET: + if (mod == NULL) + break; + for (target_info_t *tp = (target_info_t*) (ptr + sizeof (ARCH_aninfo)); + ((char*) tp) < ptr + cpkt->size; tp++) + { + target_info_t *bTarget = new target_info_t; + bTarget->offset = dwin->decode (tp->offset); + mod->bTargets.append (bTarget); + } + Dprintf (DEBUG_LOADOBJ, "BRTARGET list of %lld for %s\n", + (long long) mod->infoList->size (), mod->get_name ()); + break; + default: + /* Check if the prointer is valid - should be even. */ + pointer_invalid = (unsigned long long) (offset + cpktsize) & 1; + break; // ignore unknown packets + } + if (pointer_invalid) + break; + offset += cpktsize; + } + delete msg; + delete dwin; + + if (dbeSession->is_interactive ()) + theApplication->set_progress (0, NTXT ("")); + return 0; +} + +char * +LoadObject::status_str (Arch_status rv, char */*arg*/) +{ + switch (rv) + { + case ARCHIVE_SUCCESS: + case ARCHIVE_EXIST: + return NULL; + case ARCHIVE_BAD_STABS: + return dbe_sprintf (GTXT ("Error: unable to read symbol table of %s"), + name); + case ARCHIVE_ERR_SEG: + return dbe_sprintf (GTXT ("Error: unable to read load object file %s"), + pathname); + case ARCHIVE_ERR_OPEN: + return dbe_sprintf (GTXT ("Error: unable to open file %s"), + pathname); + case ARCHIVE_ERR_MAP: + return dbe_sprintf (GTXT ("Error: unable to map file %s"), + pathname); + case ARCHIVE_WARN_CHECKSUM: + return dbe_sprintf (GTXT ("Note: checksum differs from that recorded in experiment for %s"), + name); + case ARCHIVE_WARN_MTIME: + return dbe_sprintf (GTXT ("Warning: last-modified time differs from that recorded in experiment for %s"), + name); + case ARCHIVE_WARN_HOST: + return dbe_sprintf (GTXT ("Try running er_archive -F on the experiment, on the host where it was recorded")); + case ARCHIVE_ERR_VERSION: + return dbe_sprintf (GTXT ("Error: Wrong version of archive for %s"), + pathname); + case ARCHIVE_NO_STABS: + return dbe_sprintf (GTXT ("Note: no stabs or dwarf information in %s"), + name); + case ARCHIVE_WRONG_ARCH: +#if ARCH(SPARC) + return dbe_sprintf (GTXT ("Error: file %s is built for Intel, and can't be read on SPARC"), + name); +#else + return dbe_sprintf (GTXT ("Error: file %s is built for SPARC, and can't be read on Intel"), + name); +#endif + case ARCHIVE_NO_LIBDWARF: + return dbe_strdup (GTXT ("Warning: no libdwarf found to read DWARF symbol tables")); + case ARCHIVE_NO_DWARF: + return dbe_sprintf (GTXT ("Note: no DWARF symbol table in %s"), name); + default: + return dbe_sprintf (GTXT ("Warning: unexpected archive error %d"), + (int) rv); + } +} + +uint32_t +LoadObject::get_checksum () +{ + char *errmsg = NULL; + uint32_t crcval = get_cksum (pathname, &errmsg); + if (0 == crcval && errmsg) + { + warnq->append (new Emsg (CMSG_ERROR, errmsg)); + free (errmsg); + } + return crcval; +} + +static char* +get_module_map_key (Module *mod) +{ + return mod->lang_code == Sp_lang_java ? mod->get_name () : mod->file_name; +} + +Module * +LoadObject::get_comparable_Module (Module *mod) +{ + if (mod->loadobject == this) + return mod; + if (get_module_map_key (mod) == NULL) + return NULL; + if (seg_modules_map == NULL) + { + seg_modules_map = new HashMap<char*, Module*>; + for (int i = 0; i < seg_modules->size (); i++) + { + Module *m = seg_modules->fetch (i); + char *key = get_module_map_key (m); + if (key) + { + seg_modules_map->put (m->file_name, m); + char *bname = get_basename (key); + if (bname != key) + seg_modules_map->put (bname, m); + } + } + } + + char *key = get_module_map_key (mod); + Module *cmpMod = seg_modules_map->get (key); + if (cmpMod && cmpMod->comparable_objs == NULL) + return cmpMod; + char *bname = get_basename (key); + if (bname != key) + { + cmpMod = seg_modules_map->get (bname); + if (cmpMod && cmpMod->comparable_objs == NULL) + return cmpMod; + } + return NULL; +} + +Vector<Histable*> * +LoadObject::get_comparable_objs () +{ + update_comparable_objs (); + if (comparable_objs || dbeSession->expGroups->size () <= 1) + return comparable_objs; + comparable_objs = new Vector<Histable*>(dbeSession->expGroups->size ()); + for (int i = 0, sz = dbeSession->expGroups->size (); i < sz; i++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (i); + Histable *h = gr->get_comparable_loadObject (this); + comparable_objs->append (h); + if (h) + h->comparable_objs = comparable_objs; + } + dump_comparable_objs (); + return comparable_objs; +} + +void +LoadObject::append_module (Module *mod) +{ + seg_modules->append (mod); + if (seg_modules_map == NULL) + seg_modules_map = new HashMap<char*, Module*>; + char *key = get_module_map_key (mod); + if (key) + { + seg_modules_map->put (key, mod); + char *bname = get_basename (key); + if (bname != key) + seg_modules_map->put (bname, mod); + } +} + +// LIBRARY_VISIBILITY +Function * +LoadObject::get_hide_function () +{ + if (h_function == NULL) + h_function = dbeSession->create_hide_function (this); + return h_function; +} + +DbeInstr * +LoadObject::get_hide_instr (DbeInstr *instr) +{ + if (h_instr == NULL) + { + Function *hf = get_hide_function (); + h_instr = hf->create_hide_instr (instr); + } + return h_instr; +} diff --git a/gprofng/src/LoadObject.h b/gprofng/src/LoadObject.h new file mode 100644 index 0000000..0c997e3 --- /dev/null +++ b/gprofng/src/LoadObject.h @@ -0,0 +1,210 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _LOADOBJECT_H +#define _LOADOBJECT_H + +// A Segment object represents a segment of the program text. + +#include "Histable.h" +#include "Stabs.h" +#include "DbeLock.h" + +#define JAVA_COMPILED_METHODS "JAVA_COMPILED_METHODS" +#define DYNFUNC_SEGMENT "DYNAMIC_FUNCTIONS" +#define SEG_FLAG_DYNAMIC 0x01 +#define SEG_FLAG_JVM 0x02 +#define SEG_FLAG_OMP 0x04 +#define SEG_FLAG_EXE 0x08 +#define SEG_FLAG_REORDER 0x10 + +/* Hash name for all comparable executions */ +#define COMP_EXE_NAME "<COMP_EXE_NAME>" + +class Emsg; +class Elf; +class Experiment; +class Function; +class Module; +template <typename Key_t, typename Value_t> class HashMap; +template <typename Key_t, typename Value_t> class Map; +template <class ITEM> class Vector; + +enum +{ + CMP_PATH = 1, + CMP_RUNTIMEPATH = 2, + CMP_CHKSUM = 4 +}; + +class LoadObject : public HistableFile, public DbeLock +{ +public: + + // The various segments types. + enum seg_type + { + SEG_TEXT, + SEG_DATA, + SEG_BSS, + SEG_HEAP, + SEG_STACK, + SEG_DEVICE, + SEG_UNKNOWN + }; + + // These codes are stored in *.archive files + enum Arch_status + { + ARCHIVE_SUCCESS, + ARCHIVE_EXIST, + ARCHIVE_BAD_STABS, + ARCHIVE_ERR_SEG, + ARCHIVE_ERR_OPEN, + ARCHIVE_ERR_MAP, + ARCHIVE_WARN_MTIME, + ARCHIVE_WARN_HOST, + ARCHIVE_ERR_VERSION, + ARCHIVE_NO_STABS, + ARCHIVE_WRONG_ARCH, + ARCHIVE_NO_LIBDWARF, + ARCHIVE_NO_DWARF, + ARCHIVE_WARN_CHECKSUM + }; + + LoadObject (const char *loname); + + static LoadObject *create_item (const char *nm, int64_t chksum); + static LoadObject *create_item (const char *nm, const char *_runTimePath, DbeFile *df); + + virtual ~LoadObject (); + virtual void set_name (char *string); + virtual uint64_t get_addr (); + virtual Vector<Histable*> *get_comparable_objs (); + + virtual Histable_type + get_type () + { + return LOADOBJECT; + }; + + virtual int64_t + get_size () + { + return size; + } + + char * + get_pathname () + { + return pathname; + } + + void + set_archname (char *aname) + { + free (arch_name); + arch_name = aname; + } + + bool + is_relocatable () + { + return isRelocatable; + } + + bool compare (const char *nm, int64_t _checksum); + int compare (const char *_path, const char *_runTimePath, DbeFile *df); + void set_platform (Platform_t pltf, int wsz); + void dump_functions (FILE *); + int get_index (Function *func); + char *get_alias (Function *func); + DbeInstr *find_dbeinstr (uint64_t file_off); + Function *find_function (uint64_t offset); + Function *find_function (char *fname); + Function *find_function (char *fname, unsigned int chksum); + Module *find_module (char *mname); + Module *get_comparable_Module (Module *mod); + void append_module (Module *mod); + Elf *get_elf (); + Stabs *openDebugInfo (char *fname, Stabs::Stab_status *stp = NULL); + Arch_status read_stabs (); + Arch_status sync_read_stabs (); + void post_process_functions (); + char *status_str (Arch_status rv, char *arg = NULL); + Function *get_hide_function (); + DbeInstr *get_hide_instr (DbeInstr *instr); + uint32_t get_checksum (); + + Emsg * + fetch_warnings (void) // fetch the queue of warning messages + { + return warnq->fetch (); + } + + Emsg * + fetch_comments (void) // fetch the queue of comment messages + { + return commentq->fetch (); + } + + unsigned int flags; // SEG_FLAG_* + bool isReadStabs; + bool need_swap_endian; + int seg_idx; // for compatibility (ADDRESS) + seg_type type; + int64_t size; // size of loadobject in bytes + int64_t max_size; // Maximum size of loadobject in bytes + int64_t min_size; // Min size of loadobject in bytes. + Vector<Function*> *functions; // Ordered list of functions + Vector<Module*> *seg_modules; // list of modules + HashMap<char*, Module*> *modules; + Module *noname; // Module pointer to unknown name + Platform_t platform; // Sparc, Sparcv9, Intel + WSize_t wsize; // word size: 32,64 + Stabs *objStabs; + HashMap<char*, Function*> *comp_funcs; // list of comparable functions + Experiment *firstExp; + char *runTimePath; + time_t mtime; // file timestamp (in seconds) + int64_t checksum; // file checksum + +private: + Elf *elf_lo; + bool elf_inited; + DbeInstr **instHTable; // hash table for DbeInstr + char *pathname; // User name of object file + ino64_t inode; // inode number of segment file + bool isRelocatable; // is relocatable .o + char *arch_name; // .archive name + Emsgqueue *warnq; + Emsgqueue *commentq; + Function **funcHTable; // hash table for functions + Function *h_function; // hide pseudo function + DbeInstr *h_instr; // hide pseudo instr + HashMap<char*, Module*> *seg_modules_map; // to find a comparable module + + static int func_compare (const void *p1, const void *p2); + int read_archive (); + void init_datatypes (); + void update_datatypes (Module*, Vaddr, uint32_t datatype_id); +}; + +#endif /* _LOADOBJECT_H */ diff --git a/gprofng/src/MachineModel.cc b/gprofng/src/MachineModel.cc new file mode 100644 index 0000000..15f493a --- /dev/null +++ b/gprofng/src/MachineModel.cc @@ -0,0 +1,317 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <string.h> +#include <unistd.h> +#include <dirent.h> + +#include "DbeSession.h" +#include "Command.h" +#include "Application.h" +#include "MemorySpace.h" +#include "i18n.h" + +#define MAXARGS 20 + +static const char *LIBNAME = "../lib/analyzer/lib/machinemodels"; + +char * +DbeSession::find_mach_model (char *name) +{ + // Read current working directory to see if it's there + if (name[0] == '/') + { + // Absolute path given + char *path = dbe_sprintf (NTXT ("%s.ermm"), name); + if (access (path, R_OK | F_OK) == 0) + return path; + free (path); + // Don't try anywhere else + return NULL; + } + + char *path = dbe_sprintf (NTXT ("./%s.ermm"), name); + if (access (path, R_OK | F_OK) == 0) + return path; + free (path); + + + // Read the user's home directory to see if it's there + char *home = getenv (NTXT ("HOME")); + if (home != NULL) + { + path = dbe_sprintf (NTXT ("%s/%s.ermm"), home, name); + if (access (path, R_OK | F_OK) == 0) + return path; + free (path); + } + if (strchr (name, (int) '/') != NULL) + // name has a slash; don't look in system installation directory + return NULL; + + // Read system installation directory to see if it's there + path = dbe_sprintf ("%s/%s/%s.ermm", theApplication->get_run_dir (), + LIBNAME, name); + if (access (path, R_OK | F_OK) == 0) + return path; + free (path); + return NULL; +} + +// Handle the definitions from a machinemodel file +// Return value is NULL if it was OK, or an error message if not +char * +DbeSession::load_mach_model (char *_name) +{ + CmdType cmd_type; + int arg_count, cparam; + char *cmd, *end_cmd; + char *arglist[MAXARGS]; + char *ret = NULL; + char *path = NULL; + FILE *fptr = NULL; + + // Does name have .ermm suffix? If so, strip it away + char *name = dbe_strdup (_name); + size_t len = strlen (name); + if (len > 5 && strcmp (name + len - 5, ".ermm") == 0) + name[len - 5] = 0; + + if ((mach_model_loaded != NULL) && (strcmp (name, mach_model_loaded) == 0)) + { + ret = dbe_sprintf (GTXT ("Machine model %s is already loaded\n"), name); + free (name); + return ret; + } + else if (mach_model_loaded == NULL && len == 0) + { + ret = dbe_sprintf (GTXT ("No Machine model is loaded\n")); + free (name); + return ret; + } + + if (len != 0) + { + // zero-length just means unload any previously loaded model; only look if non-zero + path = find_mach_model (name); + if (path == NULL) + { + ret = dbe_sprintf (GTXT ("Machine model %s not found\n"), name); + free (name); + return ret; + } + fptr = fopen (path, NTXT ("r")); + if (fptr == NULL) + { + ret = dbe_sprintf (GTXT ("Open of Machine model %s, file %s failed\n"), name, path); + free (path); + free (name); + return ret; + } + } + + // We are now committed to make the new machine model the loaded one; + // Delete any MemoryObjects from any previously loaded machinemodel + if (dbeSession->mach_model_loaded != NULL) + { + Vector <char *> *oldobjs = MemorySpace::getMachineModelMemObjs + (dbeSession->mach_model_loaded); + for (int i = 0; i < oldobjs->size (); i++) + MemorySpace::mobj_delete (oldobjs->fetch (i)); + delete oldobjs; + free (mach_model_loaded); + } + if (len == 0) + { + mach_model_loaded = NULL; + free (name); + // and there's no "loading" to do; just return + return NULL; + } + else + mach_model_loaded = name; + + int line_no = 0; + end_cmd = NULL; + + while (!feof (fptr)) + { + char *script = read_line (fptr); + if (script == NULL) + continue; + + line_no++; + strtok (script, NTXT ("\n")); + + // extract the command + cmd = strtok (script, NTXT (" \t")); + if (cmd == NULL || *cmd == '#' || *cmd == '\n') + { + free (script); + continue; + } + + char *remainder = strtok (NULL, NTXT ("\n")); + // now extract the arguments + int nargs = 0; + for (;;) + { + if (nargs >= MAXARGS) + { + ret = dbe_sprintf (GTXT ("Warning: more than %d arguments to %s command, line %d\n"), + MAXARGS, cmd, line_no); + continue; + } + + char *nextarg = strtok (remainder, NTXT ("\n")); + + if (nextarg == NULL || *nextarg == '#') + // either the end of the line, or a comment indicator + break; + arglist[nargs++] = parse_qstring (nextarg, &end_cmd); + remainder = end_cmd; + if (remainder == NULL) + break; + + // skip any blanks or tabs to get to next argument + while ((*remainder == ' ') || (*remainder == '\t')) + remainder++; + } + + cmd_type = Command::get_command (cmd, arg_count, cparam); + // check for extra arguments + if (cmd_type != UNKNOWN_CMD && cmd_type != INDXOBJDEF && nargs > arg_count) + ret = dbe_sprintf (GTXT ("Warning: extra arguments to %s command, line %d\n"), + cmd, line_no); + if (nargs < arg_count) + { + ret = dbe_sprintf (GTXT ("Error: missing arguments to %s command, line %d\n"), + cmd, line_no); + + // ignore this command + free (script); + continue; + } + + switch (cmd_type) + { + case INDXOBJDEF: + { + char *err = dbeSession->indxobj_define (arglist[0], NULL, + arglist[1], (nargs >= 3) ? PTXT (arglist[2]) : NULL, + (nargs >= 4) ? PTXT (arglist[3]) : NULL); + if (err != NULL) + ret = dbe_sprintf (GTXT (" %s: line %d `%s %s %s'\n"), + err, line_no, cmd, arglist[0], arglist[1]); + break; + } + case COMMENT: + // ignore the line + break; + default: + { + // unexpected command in a machinemodel file + ret = dbe_sprintf (GTXT ("Unexpected command in machinemodel file %s on line %d: `%.64s'\n"), + path, line_no, cmd); + break; + } + } + free (script); + } + fclose (fptr); + return ret; +} + +Vector<char*> * +DbeSession::list_mach_models () +{ + Vector<char*> *model_names = new Vector<char*>(); + + // Open the current directory to read the entries there + DIR *dir = opendir (NTXT (".")); + if (dir != NULL) + { + struct dirent *entry = NULL; + while ((entry = readdir (dir)) != NULL) + { + size_t len = strlen (entry->d_name); + if (len < 5 || strcmp (entry->d_name + len - 5, ".ermm") != 0) + continue; + char *model = dbe_strdup (entry->d_name); + + // remove the .ermm suffix + model[len - 5] = 0; + + // add to vector + model_names->append (dbe_strdup (model)); + } + closedir (dir); + } + + // Read the user's home directory to list the models there + char *home = getenv ("HOME"); + if (home != NULL) + { + dir = opendir (home); + if (dir != NULL) + { + struct dirent *entry = NULL; + while ((entry = readdir (dir)) != NULL) + { + size_t len = strlen (entry->d_name); + if (len < 5 || strcmp (entry->d_name + len - 5, ".ermm") != 0) + continue; + char *model = dbe_strdup (entry->d_name); + + // remove the .ermm suffix + model[len - 5] = 0; + + // add to vector + model_names->append (dbe_strdup (model)); + } + closedir (dir); + } + } + + // Read system installation directory to list the models there + char *sysdir = dbe_sprintf ("%s/%s", theApplication->get_run_dir (), LIBNAME); + + dir = opendir (sysdir); + if (dir != NULL) + { + struct dirent *entry = NULL; + while ((entry = readdir (dir)) != NULL) + { + size_t len = strlen (entry->d_name); + if (len < 5 || strcmp (entry->d_name + len - 5, ".ermm") != 0) + continue; + char *model = dbe_strdup (entry->d_name); + + // remove the .ermm suffix + model[len - 5] = 0; + + // add to vector + model_names->append (dbe_strdup (model)); + } + closedir (dir); + } + return model_names; +} diff --git a/gprofng/src/Makefile.am b/gprofng/src/Makefile.am new file mode 100644 index 0000000..b874b5b --- /dev/null +++ b/gprofng/src/Makefile.am @@ -0,0 +1,202 @@ +## Process this file with automake to generate Makefile.in +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I . -I .. -I ../.. + +CCSOURCES = \ + Application.cc \ + BaseMetric.cc \ + BaseMetricTreeNode.cc \ + CallStack.cc \ + CatchOutOfMemory.cc \ + ClassFile.cc \ + Command.cc \ + CompCom.cc \ + DataObject.cc \ + DataSpace.cc \ + Data_window.cc \ + DataStream.cc \ + DbeApplication.cc \ + DbeFile.cc \ + DbeJarFile.cc \ + DbeLock.cc \ + DbeSession.cc \ + DbeThread.cc \ + DbeView.cc \ + DerivedMetrics.cc \ + Disasm.cc \ + Dwarf.cc \ + DwarfLib.cc \ + Elf.cc \ + Emsg.cc \ + Experiment.cc \ + Exp_Layout.cc \ + ExpGroup.cc \ + Expression.cc \ + FileData.cc \ + Filter.cc \ + FilterSet.cc \ + Function.cc \ + HeapMap.cc \ + HeapData.cc \ + HeapActivity.cc \ + Hist_data.cc \ + IndexObject.cc \ + IOActivity.cc \ + LoadObject.cc \ + MachineModel.cc \ + MemObject.cc \ + MemorySpace.cc \ + Metric.cc \ + MetricList.cc \ + Module.cc \ + Ovw_data.cc \ + PRBTree.cc \ + PathTree.cc \ + PreviewExp.cc \ + Print.cc \ + SAXParserFactory.cc \ + Sample.cc \ + Settings.cc \ + SourceFile.cc \ + Stabs.cc \ + Stats_data.cc \ + StringBuilder.cc \ + Table.cc \ + QLParser.tab.cc \ + dbe_collctrl.cc \ + i18n.cc \ + parse.cc \ + UserLabel.cc \ + util.cc \ + Dbe.cc \ + $(NULL) + +CSOURCES = \ + dbe_hwcdrv.c \ + dbe_hwcfuncs.c \ + dbe_hwctable.c \ + dbe_memmgr.c \ + gethrtime.c \ + $(NULL) + +LIBGPROFNG = libgprofng.la + +AM_CPPFLAGS = $(GPROFNG_CPPFLAGS) -DLOCALEDIR=\"@localedir@\" -I.. -I$(srcdir) \ + -I$(srcdir)/../common \ + -I$(srcdir)/../../include -I$(srcdir)/../../opcodes \ + -I../../bfd -I$(srcdir)/../../bfd +AM_CFLAGS = $(GPROFNG_CFLAGS) $(PTHREAD_CFLAGS) -Wno-switch \ + -Wno-format-truncation +AM_CXXFLAGS = $(AM_CFLAGS) + +man_MANS = gprofng.1 \ + gp-archive.1 \ + gp-collect-app.1 \ + gp-display-src.1 \ + gp-display-text.1 + +MAINTAINERCLEANFILES = $(man_MANS) + +EXTRA_DIST = $(man_MANS) + + +lib_LTLIBRARIES = $(LIBGPROFNG) +libgprofng_la_SOURCES = $(CCSOURCES) $(CSOURCES) +libgprofng_la_LDFLAGS = -version-info 0:0:0 +libgprofng_la_LIBADD = $(top_builddir)/../opcodes/libopcodes.la \ + $(top_builddir)/../bfd/libbfd.la \ + $(GPROFNG_LIBADD) \ + $(PTHREAD_LIBS) -ldl + +dbedir = $(prefix)/etc +dbe_DATA = $(srcdir)/gprofng.rc + + +bin_PROGRAMS = gp-archive gp-collect-app gprofng gp-display-text gp-display-src + +gp_archive_SOURCES = gp-archive.cc ArchiveExp.cc +gp_archive_LDADD = $(LIBGPROFNG) + +gp_collect_app_SOURCES = gp-collect-app.cc checks.cc envsets.cc count.cc +gp_collect_app_LDADD = $(LIBGPROFNG) + +gprofng_SOURCES = gprofng.cc +gprofng_LDADD = $(LIBGPROFNG) + +gp_display_src_SOURCES = gp-display-src.cc +gp_display_src_LDADD = $(LIBGPROFNG) + +gp_display_text_SOURCES = gp-display-text.cc ipc.cc ipcio.cc +gp_display_text_LDADD = $(LIBGPROFNG) + + +if BUILD_MAN + +# The man pages depend on the version number and on a help2man include file. +common_mandeps = $(top_srcdir)/../bfd/version.m4 + +# Use -o so that the `missing' program can infer the output file. +# Embolden subcommand names in the output, and include a SEE ALSO. +# Arrange to regenerate the output if we have help2man, but leave the +# disted output there otherwise. +# Some extra annoying complexity is in place so that people without +# help2man dno't accidentally overwrite the manpage. + +INFO_PAGE = "gprofng" +MANUAL = "User Commands" +TEXT_GPROFNG = "the driver for the gprofng tool suite" +TEXT_GP_ARCHIVE = "archive gprofng experiment data" +TEXT_GP_COLLECT_APP = "collect performance data for the target application" +TEXT_GP_DISPLAY_SRC = "display the source code, optionally interleaved with the disassembly of the target object" +TEXT_GP_DISPLAY_TEXT = "display the performance data in plain text format" + +HELP2MAN_OPT = --libtool --no-info --info-page=$(INFO_PAGE) --manual=$(MANUAL) +H2M_FILTER = | sed 's/\.TP/\.TP\n.B/' | sed 's/Commands:/\.SH COMMANDS/' \ + | sed 's/See also:/\.SH SEE ALSO/' | sed 's/Documentation:/.SH DOCUMENTATION/' \ + | sed 's/Limitations:/.SH LIMITATIONS/' + +gprofng.1: $(srcdir)/gprofng.cc $(common_mandeps) | ./gprofng$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GPROFNG) ./gprofng$(EXEEXT) $(H2M_FILTER) > $@ + +gp-archive.1: $(srcdir)/gp-archive.cc $(common_mandeps) | ./gp-archive$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GP_ARCHIVE) ./gp-archive$(EXEEXT) $(H2M_FILTER) > $@ + +gp-collect-app.1: $(srcdir)/gp-collect-app.cc $(common_mandeps) | ./gp-collect-app$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GP_COLLECT_APP) ./gp-collect-app$(EXEEXT) $(H2M_FILTER) > $@ + +gp-display-src.1: $(srcdir)/gp-display-src.cc $(srcdir)/Command.cc \ + $(common_mandeps) | ./gp-display-src$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GP_DISPLAY_SRC) ./gp-display-src$(EXEEXT) $(H2M_FILTER) > $@ + +gp-display-text.1: $(srcdir)/gp-display-text.cc $(srcdir)/Command.cc \ + $(common_mandeps) | ./gp-display-text$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GP_DISPLAY_TEXT) ./gp-display-text$(EXEEXT) $(H2M_FILTER) > $@ + +endif + +# Distribution involves building the binaries to generate the manpage, +# so ensure that the necessary libraries are built at dist time. +dist-hook: $(LIBGPROFNG) + diff --git a/gprofng/src/Makefile.in b/gprofng/src/Makefile.in new file mode 100644 index 0000000..f21671d --- /dev/null +++ b/gprofng/src/Makefile.in @@ -0,0 +1,1171 @@ +# Makefile.in generated by automake 1.15.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2017 Free Software Foundation, Inc. + +# This Makefile.in 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. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = gp-archive$(EXEEXT) gp-collect-app$(EXEEXT) \ + gprofng$(EXEEXT) gp-display-text$(EXEEXT) \ + gp-display-src$(EXEEXT) +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/../libtool.m4 \ + $(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \ + $(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/../config/warnings.m4 \ + $(top_srcdir)/../config/enable.m4 \ + $(top_srcdir)/../config/ax_pthread.m4 \ + $(top_srcdir)/config/bison.m4 $(top_srcdir)/../bfd/version.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(dbedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgprofng_la_DEPENDENCIES = $(top_builddir)/../opcodes/libopcodes.la \ + $(top_builddir)/../bfd/libbfd.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am__objects_1 = Application.lo BaseMetric.lo BaseMetricTreeNode.lo \ + CallStack.lo CatchOutOfMemory.lo ClassFile.lo Command.lo \ + CompCom.lo DataObject.lo DataSpace.lo Data_window.lo \ + DataStream.lo DbeApplication.lo DbeFile.lo DbeJarFile.lo \ + DbeLock.lo DbeSession.lo DbeThread.lo DbeView.lo \ + DerivedMetrics.lo Disasm.lo Dwarf.lo DwarfLib.lo Elf.lo \ + Emsg.lo Experiment.lo Exp_Layout.lo ExpGroup.lo Expression.lo \ + FileData.lo Filter.lo FilterSet.lo Function.lo HeapMap.lo \ + HeapData.lo HeapActivity.lo Hist_data.lo IndexObject.lo \ + IOActivity.lo LoadObject.lo MachineModel.lo MemObject.lo \ + MemorySpace.lo Metric.lo MetricList.lo Module.lo Ovw_data.lo \ + PRBTree.lo PathTree.lo PreviewExp.lo Print.lo \ + SAXParserFactory.lo Sample.lo Settings.lo SourceFile.lo \ + Stabs.lo Stats_data.lo StringBuilder.lo Table.lo \ + QLParser.tab.lo dbe_collctrl.lo i18n.lo parse.lo UserLabel.lo \ + util.lo Dbe.lo +am__objects_2 = dbe_hwcdrv.lo dbe_hwcfuncs.lo dbe_hwctable.lo \ + dbe_memmgr.lo gethrtime.lo +am_libgprofng_la_OBJECTS = $(am__objects_1) $(am__objects_2) +libgprofng_la_OBJECTS = $(am_libgprofng_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgprofng_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libgprofng_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +PROGRAMS = $(bin_PROGRAMS) +am_gp_archive_OBJECTS = gp-archive.$(OBJEXT) ArchiveExp.$(OBJEXT) +gp_archive_OBJECTS = $(am_gp_archive_OBJECTS) +gp_archive_DEPENDENCIES = $(LIBGPROFNG) +am_gp_collect_app_OBJECTS = gp-collect-app.$(OBJEXT) checks.$(OBJEXT) \ + envsets.$(OBJEXT) count.$(OBJEXT) +gp_collect_app_OBJECTS = $(am_gp_collect_app_OBJECTS) +gp_collect_app_DEPENDENCIES = $(LIBGPROFNG) +am_gp_display_src_OBJECTS = gp-display-src.$(OBJEXT) +gp_display_src_OBJECTS = $(am_gp_display_src_OBJECTS) +gp_display_src_DEPENDENCIES = $(LIBGPROFNG) +am_gp_display_text_OBJECTS = gp-display-text.$(OBJEXT) ipc.$(OBJEXT) \ + ipcio.$(OBJEXT) +gp_display_text_OBJECTS = $(am_gp_display_text_OBJECTS) +gp_display_text_DEPENDENCIES = $(LIBGPROFNG) +am_gprofng_OBJECTS = gprofng.$(OBJEXT) +gprofng_OBJECTS = $(am_gprofng_OBJECTS) +gprofng_DEPENDENCIES = $(LIBGPROFNG) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/../depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libgprofng_la_SOURCES) $(gp_archive_SOURCES) \ + $(gp_collect_app_SOURCES) $(gp_display_src_SOURCES) \ + $(gp_display_text_SOURCES) $(gprofng_SOURCES) +DIST_SOURCES = $(libgprofng_la_SOURCES) $(gp_archive_SOURCES) \ + $(gp_collect_app_SOURCES) $(gp_display_src_SOURCES) \ + $(gp_display_text_SOURCES) $(gprofng_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +man1dir = $(mandir)/man1 +NROFF = nroff +MANS = $(man_MANS) +DATA = $(dbe_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/../depcomp \ + $(top_srcdir)/../mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_SUBDIRS = @BUILD_SUBDIRS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPECT = @EXPECT@ +FGREP = @FGREP@ +GPROFNG_CFLAGS = @GPROFNG_CFLAGS@ +GPROFNG_CPPFLAGS = @GPROFNG_CPPFLAGS@ +GPROFNG_LIBADD = @GPROFNG_LIBADD@ +GPROFNG_LIBDIR = @GPROFNG_LIBDIR@ +GREP = @GREP@ +HELP2MAN = @HELP2MAN@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JAVA = @JAVA@ +JAVAC = @JAVAC@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LD_NO_AS_NEEDED = @LD_NO_AS_NEEDED@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WERROR = @WERROR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +gprofng_cflags = @gprofng_cflags@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +jdk_inc = @jdk_inc@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I . -I .. -I ../.. +CCSOURCES = \ + Application.cc \ + BaseMetric.cc \ + BaseMetricTreeNode.cc \ + CallStack.cc \ + CatchOutOfMemory.cc \ + ClassFile.cc \ + Command.cc \ + CompCom.cc \ + DataObject.cc \ + DataSpace.cc \ + Data_window.cc \ + DataStream.cc \ + DbeApplication.cc \ + DbeFile.cc \ + DbeJarFile.cc \ + DbeLock.cc \ + DbeSession.cc \ + DbeThread.cc \ + DbeView.cc \ + DerivedMetrics.cc \ + Disasm.cc \ + Dwarf.cc \ + DwarfLib.cc \ + Elf.cc \ + Emsg.cc \ + Experiment.cc \ + Exp_Layout.cc \ + ExpGroup.cc \ + Expression.cc \ + FileData.cc \ + Filter.cc \ + FilterSet.cc \ + Function.cc \ + HeapMap.cc \ + HeapData.cc \ + HeapActivity.cc \ + Hist_data.cc \ + IndexObject.cc \ + IOActivity.cc \ + LoadObject.cc \ + MachineModel.cc \ + MemObject.cc \ + MemorySpace.cc \ + Metric.cc \ + MetricList.cc \ + Module.cc \ + Ovw_data.cc \ + PRBTree.cc \ + PathTree.cc \ + PreviewExp.cc \ + Print.cc \ + SAXParserFactory.cc \ + Sample.cc \ + Settings.cc \ + SourceFile.cc \ + Stabs.cc \ + Stats_data.cc \ + StringBuilder.cc \ + Table.cc \ + QLParser.tab.cc \ + dbe_collctrl.cc \ + i18n.cc \ + parse.cc \ + UserLabel.cc \ + util.cc \ + Dbe.cc \ + $(NULL) + +CSOURCES = \ + dbe_hwcdrv.c \ + dbe_hwcfuncs.c \ + dbe_hwctable.c \ + dbe_memmgr.c \ + gethrtime.c \ + $(NULL) + +LIBGPROFNG = libgprofng.la +AM_CPPFLAGS = $(GPROFNG_CPPFLAGS) -DLOCALEDIR=\"@localedir@\" -I.. -I$(srcdir) \ + -I$(srcdir)/../common \ + -I$(srcdir)/../../include -I$(srcdir)/../../opcodes \ + -I../../bfd -I$(srcdir)/../../bfd + +AM_CFLAGS = $(GPROFNG_CFLAGS) $(PTHREAD_CFLAGS) -Wno-switch \ + -Wno-format-truncation + +AM_CXXFLAGS = $(AM_CFLAGS) +man_MANS = gprofng.1 \ + gp-archive.1 \ + gp-collect-app.1 \ + gp-display-src.1 \ + gp-display-text.1 + +MAINTAINERCLEANFILES = $(man_MANS) +EXTRA_DIST = $(man_MANS) +lib_LTLIBRARIES = $(LIBGPROFNG) +libgprofng_la_SOURCES = $(CCSOURCES) $(CSOURCES) +libgprofng_la_LDFLAGS = -version-info 0:0:0 +libgprofng_la_LIBADD = $(top_builddir)/../opcodes/libopcodes.la \ + $(top_builddir)/../bfd/libbfd.la \ + $(GPROFNG_LIBADD) \ + $(PTHREAD_LIBS) -ldl + +dbedir = $(prefix)/etc +dbe_DATA = $(srcdir)/gprofng.rc +gp_archive_SOURCES = gp-archive.cc ArchiveExp.cc +gp_archive_LDADD = $(LIBGPROFNG) +gp_collect_app_SOURCES = gp-collect-app.cc checks.cc envsets.cc count.cc +gp_collect_app_LDADD = $(LIBGPROFNG) +gprofng_SOURCES = gprofng.cc +gprofng_LDADD = $(LIBGPROFNG) +gp_display_src_SOURCES = gp-display-src.cc +gp_display_src_LDADD = $(LIBGPROFNG) +gp_display_text_SOURCES = gp-display-text.cc ipc.cc ipcio.cc +gp_display_text_LDADD = $(LIBGPROFNG) + +# The man pages depend on the version number and on a help2man include file. +@BUILD_MAN_TRUE@common_mandeps = $(top_srcdir)/../bfd/version.m4 + +# Use -o so that the `missing' program can infer the output file. +# Embolden subcommand names in the output, and include a SEE ALSO. +# Arrange to regenerate the output if we have help2man, but leave the +# disted output there otherwise. +# Some extra annoying complexity is in place so that people without +# help2man dno't accidentally overwrite the manpage. +@BUILD_MAN_TRUE@INFO_PAGE = "gprofng" +@BUILD_MAN_TRUE@MANUAL = "User Commands" +@BUILD_MAN_TRUE@TEXT_GPROFNG = "the driver for the gprofng tool suite" +@BUILD_MAN_TRUE@TEXT_GP_ARCHIVE = "archive gprofng experiment data" +@BUILD_MAN_TRUE@TEXT_GP_COLLECT_APP = "collect performance data for the target application" +@BUILD_MAN_TRUE@TEXT_GP_DISPLAY_SRC = "display the source code, optionally interleaved with the disassembly of the target object" +@BUILD_MAN_TRUE@TEXT_GP_DISPLAY_TEXT = "display the performance data in plain text format" +@BUILD_MAN_TRUE@HELP2MAN_OPT = --libtool --no-info --info-page=$(INFO_PAGE) --manual=$(MANUAL) +@BUILD_MAN_TRUE@H2M_FILTER = | sed 's/\.TP/\.TP\n.B/' | sed 's/Commands:/\.SH COMMANDS/' \ +@BUILD_MAN_TRUE@ | sed 's/See also:/\.SH SEE ALSO/' | sed 's/Documentation:/.SH DOCUMENTATION/' \ +@BUILD_MAN_TRUE@ | sed 's/Limitations:/.SH LIMITATIONS/' + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cc .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgprofng.la: $(libgprofng_la_OBJECTS) $(libgprofng_la_DEPENDENCIES) $(EXTRA_libgprofng_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libgprofng_la_LINK) -rpath $(libdir) $(libgprofng_la_OBJECTS) $(libgprofng_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +gp-archive$(EXEEXT): $(gp_archive_OBJECTS) $(gp_archive_DEPENDENCIES) $(EXTRA_gp_archive_DEPENDENCIES) + @rm -f gp-archive$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gp_archive_OBJECTS) $(gp_archive_LDADD) $(LIBS) + +gp-collect-app$(EXEEXT): $(gp_collect_app_OBJECTS) $(gp_collect_app_DEPENDENCIES) $(EXTRA_gp_collect_app_DEPENDENCIES) + @rm -f gp-collect-app$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gp_collect_app_OBJECTS) $(gp_collect_app_LDADD) $(LIBS) + +gp-display-src$(EXEEXT): $(gp_display_src_OBJECTS) $(gp_display_src_DEPENDENCIES) $(EXTRA_gp_display_src_DEPENDENCIES) + @rm -f gp-display-src$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gp_display_src_OBJECTS) $(gp_display_src_LDADD) $(LIBS) + +gp-display-text$(EXEEXT): $(gp_display_text_OBJECTS) $(gp_display_text_DEPENDENCIES) $(EXTRA_gp_display_text_DEPENDENCIES) + @rm -f gp-display-text$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gp_display_text_OBJECTS) $(gp_display_text_LDADD) $(LIBS) + +gprofng$(EXEEXT): $(gprofng_OBJECTS) $(gprofng_DEPENDENCIES) $(EXTRA_gprofng_DEPENDENCIES) + @rm -f gprofng$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gprofng_OBJECTS) $(gprofng_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Application.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ArchiveExp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BaseMetric.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BaseMetricTreeNode.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CallStack.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CatchOutOfMemory.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ClassFile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Command.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CompCom.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DataObject.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DataSpace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DataStream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Data_window.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Dbe.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeApplication.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeFile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeJarFile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeLock.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeSession.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeThread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeView.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DerivedMetrics.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Disasm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Dwarf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DwarfLib.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Elf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Emsg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ExpGroup.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Exp_Layout.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Experiment.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Expression.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileData.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Filter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FilterSet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Function.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HeapActivity.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HeapData.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HeapMap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Hist_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IOActivity.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IndexObject.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LoadObject.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MachineModel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MemObject.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MemorySpace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Metric.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetricList.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Module.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Ovw_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PRBTree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PathTree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PreviewExp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Print.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QLParser.tab.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SAXParserFactory.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Sample.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Settings.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SourceFile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Stabs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Stats_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StringBuilder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Table.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UserLabel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checks.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/count.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_collctrl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_hwcdrv.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_hwcfuncs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_hwctable.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_memmgr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/envsets.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gethrtime.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-archive.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-collect-app.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-display-src.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-display-text.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gprofng.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i18n.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipcio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man1: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man1dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.1[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ + done; } + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man1dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.1[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) +install-dbeDATA: $(dbe_DATA) + @$(NORMAL_INSTALL) + @list='$(dbe_DATA)'; test -n "$(dbedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(dbedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dbedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbedir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(dbedir)" || exit $$?; \ + done + +uninstall-dbeDATA: + @$(NORMAL_UNINSTALL) + @list='$(dbe_DATA)'; test -n "$(dbedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(dbedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(MANS) $(DATA) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(dbedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dbeDATA install-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man1 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-dbeDATA \ + uninstall-libLTLIBRARIES uninstall-man + +uninstall-man: uninstall-man1 + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool cscopelist-am ctags ctags-am dist-hook distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dbeDATA install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-man1 install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-dbeDATA \ + uninstall-libLTLIBRARIES uninstall-man uninstall-man1 + +.PRECIOUS: Makefile + + +@BUILD_MAN_TRUE@gprofng.1: $(srcdir)/gprofng.cc $(common_mandeps) | ./gprofng$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GPROFNG) ./gprofng$(EXEEXT) $(H2M_FILTER) > $@ + +@BUILD_MAN_TRUE@gp-archive.1: $(srcdir)/gp-archive.cc $(common_mandeps) | ./gp-archive$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GP_ARCHIVE) ./gp-archive$(EXEEXT) $(H2M_FILTER) > $@ + +@BUILD_MAN_TRUE@gp-collect-app.1: $(srcdir)/gp-collect-app.cc $(common_mandeps) | ./gp-collect-app$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GP_COLLECT_APP) ./gp-collect-app$(EXEEXT) $(H2M_FILTER) > $@ + +@BUILD_MAN_TRUE@gp-display-src.1: $(srcdir)/gp-display-src.cc $(srcdir)/Command.cc \ +@BUILD_MAN_TRUE@ $(common_mandeps) | ./gp-display-src$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GP_DISPLAY_SRC) ./gp-display-src$(EXEEXT) $(H2M_FILTER) > $@ + +@BUILD_MAN_TRUE@gp-display-text.1: $(srcdir)/gp-display-text.cc $(srcdir)/Command.cc \ +@BUILD_MAN_TRUE@ $(common_mandeps) | ./gp-display-text$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GP_DISPLAY_TEXT) ./gp-display-text$(EXEEXT) $(H2M_FILTER) > $@ + +# Distribution involves building the binaries to generate the manpage, +# so ensure that the necessary libraries are built at dist time. +dist-hook: $(LIBGPROFNG) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gprofng/src/Map.h b/gprofng/src/Map.h new file mode 100644 index 0000000..530dcfc --- /dev/null +++ b/gprofng/src/Map.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_MAP_H +#define _DBE_MAP_H + +#include "vec.h" + +template <typename Key_t, typename Value_t> +class Map +{ +public: + + enum Relation + { + REL_LT, + REL_LE, + REL_EQ, + REL_GE, + REL_GT + }; + + virtual ~Map () { }; + virtual void put (Key_t key, Value_t val) = 0; + virtual Value_t get (Key_t key) = 0; + virtual Value_t get (Key_t key, Relation rel) = 0; + virtual Value_t remove (Key_t key) = 0; + + virtual Vector<Key_t> * + keySet () + { + return NULL; + } + + virtual Vector<Value_t> * + values () + { + return NULL; + } +}; + +#define destroy_map(t, p) if (p) { Vector<t> *v = p->values (); Destroy (v); delete p; } + +#endif diff --git a/gprofng/src/Map2D.h b/gprofng/src/Map2D.h new file mode 100644 index 0000000..41b2be2 --- /dev/null +++ b/gprofng/src/Map2D.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_MAP2D_H +#define _DBE_MAP2D_H + +template <typename Key1_t, typename Key2_t, typename Value_t> +class Map2D +{ +public: + + enum MapType + { + Default, + Interval + }; + + // Relation for the first key is always EQUAL + enum Relation + { + REL_EQLT, + REL_EQLE, + REL_EQEQ, + REL_EQGE, + REL_EQGT + }; + + virtual ~Map2D () { }; + virtual void put (Key1_t key1, Key2_t key2, Value_t val) = 0; + virtual Value_t get (Key1_t key1, Key2_t key2) = 0; + virtual Value_t get (Key1_t key1, Key2_t key2, Relation rel) = 0; + virtual Value_t remove (Key1_t key1, Key2_t key2) = 0; + +}; + +#endif diff --git a/gprofng/src/MemObject.cc b/gprofng/src/MemObject.cc new file mode 100644 index 0000000..d207a99 --- /dev/null +++ b/gprofng/src/MemObject.cc @@ -0,0 +1,44 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "Exp_Layout.h" +#include "MemObject.h" +#include "DataSpace.h" +#include "ABS.h" +#include "Dbe.h" +#include "i18n.h" + +MemObj::MemObj (uint64_t _index, char *_name) +{ + id = _index; + name = _name; +} + +MemObj::~MemObj () +{ + free (name); +} + +Histable * +MemObj::convertto (Histable_type type, Histable*) +{ + return type == MEMOBJ ? this : NULL; +} diff --git a/gprofng/src/MemObject.h b/gprofng/src/MemObject.h new file mode 100644 index 0000000..6bc459f --- /dev/null +++ b/gprofng/src/MemObject.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _MEMOBJECT_H +#define _MEMOBJECT_H + +#include "Histable.h" +#include "util.h" + +class MemObj : public Histable +{ +public: + friend class MemorySpace; + + MemObj (uint64_t _index, char *_name); + ~MemObj (); + + virtual Histable *convertto (Histable_type, Histable* = NULL); + + virtual Histable_type + get_type () + { + return MEMOBJ; + } + + virtual char * + get_name (NameFormat = NA) + { + return dbe_strdup (name); + } + + virtual uint64_t + get_addr () + { + return id; + } + + uint64_t + get_index () + { + return id; + } +}; + +#endif /* _MEMOBJECT_H */ diff --git a/gprofng/src/MemorySpace.cc b/gprofng/src/MemorySpace.cc new file mode 100644 index 0000000..3f7c78d --- /dev/null +++ b/gprofng/src/MemorySpace.cc @@ -0,0 +1,452 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> + +#include "util.h" +#include "DbeSession.h" +#include "Application.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "MetricList.h" +#include "MemObject.h" +#include "PathTree.h" +#include "DbeView.h" +#include "Metric.h" +#include "MemorySpace.h" +#include "Table.h" +#include "IndexObject.h" + +MemObjType_t::MemObjType_t () +{ + type = -1; + name = NULL; + index_expr = NULL; + machmodel = NULL; + mnemonic = 0; + short_description = NULL; + long_description = NULL; +} + +MemObjType_t::~MemObjType_t () +{ + free (name); + free (index_expr); + free (machmodel); + free (short_description); + free (long_description); +} + +MemorySpace::MemorySpace (DbeView *_dbev, int _mstype) +{ + char *mname; + dbev = _dbev; + phaseIdx = -1; + + // set up the MemoryObject information + objs = new HashMap<uint64_t, MemObj*>; + mstype = _mstype; + msindex_exp = NULL; + msname = NULL; + msindex_exp_str = NULL; + + // find the memory space in the table + MemObjType_t *mot = findMemSpaceByIndex (mstype); + if (mot) + { + msname = dbe_strdup (mot->name); + if (mot->index_expr != NULL) + { + msindex_exp_str = dbe_strdup (mot->index_expr); + msindex_exp = dbeSession->ql_parse (msindex_exp_str); + if (msindex_exp == NULL) + // this was checked when the definition was created + abort (); + } + } + + // create the Total and Unknown objects + mname = dbe_strdup (NTXT ("<Total>")); + total_memobj = createMemObject ((uint64_t) - 2, mname); + mname = dbe_strdup (GTXT ("<Unknown>")); + unk_memobj = createMemObject ((uint64_t) - 1, mname); + hist_data_all = NULL; + selected_mo_index = (uint64_t) - 3; + sel_ind = -1; +} + +MemorySpace::~MemorySpace () +{ + reset (); + delete objs; + free (msname); + free (msindex_exp); + free (msindex_exp_str); +} + +void +MemorySpace::reset () +{ + if (hist_data_all != NULL) + { + delete hist_data_all; + hist_data_all = NULL; + } + // do not clear the selected object's index + // selected_mo_index = (uint64_t)-3; + + // destroy any existing objects, but keep the vector + // Now that we have a hashmap, which has its own vector, + // safe to delete and reallocate + delete objs; + objs = new HashMap<uint64_t, MemObj*>; +} + +// find a memory object by its memory-object index +int +MemorySpace::findMemObject (uint64_t indx) +{ + int index; + Hist_data::HistItem *hi; + if (indx == (uint64_t) - 3) + return -1; + + Vec_loop (Hist_data::HistItem *, hist_data_all->hist_items, index, hi) + { + if (((uint64_t) ((MemObj *) hi->obj)->id) == indx) + return index; + } + // object does not exist; filter change eliminated it, for example + return -1; +} + +// find the object referenced in the packet +MemObj * +MemorySpace::lookupMemObject (Experiment *exp, DataView *packets, long i) +{ + uint64_t idx; + uint64_t va = (uint64_t) packets->getLongValue (PROP_VADDR, i); + if (va == ABS_UNSUPPORTED) + // return NULL, to ignore the record + return NULL; + if (va < ABS_CODE_RANGE) + // The va is not valid, rather, it's an error code + // return the <Unknown> object + return unk_memobj; + + Expression::Context ctx (dbev, exp, packets, i); + idx = msindex_exp->eval (&ctx); + if (idx == (uint64_t) - 1) + return unk_memobj; + + // do a binary search for the memory object + MemObj *res = objs->get (idx); + if (res == NULL) + { + res = createMemObject (idx, NULL); + objs->put (idx, res); + } + else + return res; + + // recompute range + if (idx < idx_min) + idx_min = idx; + if (idx > idx_max) + idx_max = idx; + return res; +} + +MemObj * +MemorySpace::createMemObject (uint64_t index, char *moname) +{ + MemObj *res; + char *name; + if (moname != NULL) + { + res = new MemObj (index, moname); + return res; + } + + // Custom memory objects + // The memory_page_size is defined in the machine model file such + // as ./machinemodels/t4.ermm. + // Most users prefer to look at the hexadecimal version of virtual + // addresses. Display only the hexadecimal version of virtual addresses + // for all machine model views with an exception of virtual page size. + if (dbe_strcmp (msname, NTXT ("Memory_page_size")) == 0) + name = dbe_sprintf (NTXT ("%s 0x%16.16llx (%llu)"), msname, + (long long) index, (unsigned long long) index); + else if (dbe_strcmp (msname, NTXT ("Memory_in_home_lgrp")) == 0) + name = dbe_sprintf (NTXT ("%s: %s"), msname, + index == 1 ? GTXT ("True") : index == 0 ? GTXT ("False") + : GTXT ("<Unknown>")); + else if (dbe_strcmp (msname, NTXT ("Memory_lgrp")) == 0) + name = dbe_sprintf (NTXT ("%s %llu"), msname, (unsigned long long) index); + else + name = dbe_sprintf (NTXT ("%s 0x%16.16llx"), msname, (long long) index); + + res = new MemObj (index, name); + return res; +} + + +static Vector<MemObjType_t*> dyn_memobj_vec; +static Vector<MemObjType_t*> *dyn_memobj = &dyn_memobj_vec; +static Vector<int> *ordlist; + +// Static function to get a vector of custom memory object definitions + +Vector<void*> * +MemorySpace::getMemObjects () +{ + MemObjType_t *mot; + int ii; + int size = dyn_memobj->size (); + Vector<int> *indx = new Vector<int>(size); + Vector<char*> *name = new Vector<char*>(size); + Vector<char> *mnemonic = new Vector<char>(size); + Vector<char*> *formula = new Vector<char*>(size); + Vector<char*> *machmodel = new Vector<char*>(size); + Vector<int> *order = new Vector<int>(size); + Vector<char*> *sdesc = new Vector<char*>(size); + Vector<char*> *ldesc = new Vector<char*>(size); + + if (size > 0) + { + Vec_loop (MemObjType_t *, dyn_memobj, ii, mot) + { + indx->store (ii, mot->type); + order->store (ii, ii); + name->store (ii, dbe_strdup (mot->name)); + formula->store (ii, dbe_strdup (mot->index_expr)); + mnemonic->store (ii, mot->mnemonic); + sdesc->store (ii, mot->short_description == NULL ? NULL + : dbe_strdup (mot->short_description)); + ldesc->store (ii, mot->long_description == NULL ? NULL + : dbe_strdup (mot->long_description)); + if (mot->machmodel == NULL) + machmodel->store (ii, NULL); + else + machmodel->store (ii, dbe_strdup (mot->machmodel)); + } + } + Vector<void*> *res = new Vector<void*>(8); + res->store (0, indx); + res->store (1, name); + res->store (2, mnemonic); + res->store (3, formula); + res->store (4, machmodel); + res->store (5, order); + res->store (6, sdesc); + res->store (7, ldesc); + return (res); +} + +// Static function to set order of memory object tabs +void +MemorySpace::set_MemTabOrder (Vector<int> *orders) +{ + int size = orders->size (); + ordlist = new Vector<int>(size); + for (int i = 0; i < size; i++) + ordlist->store (i, orders->fetch (i)); +} + +// Static function to define a new memory object type +char * +MemorySpace::mobj_define (char *mname, char *mindex_exp, char *_machmodel, + char *short_description, char *long_description) +{ + MemObjType_t *mot; + + if (mname == NULL) + return dbe_strdup (GTXT ("No memory object name has been specified.")); + if (isalpha ((int) (mname[0])) == 0) + return dbe_sprintf (GTXT ("Memory Object type name %s does not begin with an alphabetic character"), + mname); + char *p = mname; + while (*p != 0) + { + if (isalnum ((int) (*p)) == 0 && *p != '_') + return dbe_sprintf (GTXT ("Memory Object type name %s contains a non-alphanumeric character"), + mname); + p++; + } + + mot = findMemSpaceByName (mname); + if (mot != NULL) + { + if (strcmp (mot->index_expr, mindex_exp) == 0) + // It's a redefinition, but the new definition is the same + return NULL; + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + } + + // make sure the name is not in use + if (dbeSession->findIndexSpaceByName (mname) >= 0) + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + + if (mindex_exp == NULL || *mindex_exp == 0) + return dbe_strdup (GTXT ("No index-expr has been specified.")); + + // verify that the index expression parses correctly + Expression *e = dbeSession->ql_parse (mindex_exp); + if (e == NULL) + return dbe_sprintf (GTXT ("Memory Object index expression is invalid: %s"), + mindex_exp); + delete e; + + // It's OK, create the new table entry + char *s = dbeSession->indxobj_define (mname, NULL, mindex_exp, + short_description, long_description); + if (s) + return s; + IndexObjType_t *indObj = dbeSession->findIndexSpace (mname); + + mot = new MemObjType_t; + mot->type = indObj->type; + indObj->memObj = mot; + mot->name = dbe_strdup (mname); + mot->index_expr = dbe_strdup (mindex_exp); + mot->mnemonic = MemorySpace::pickMnemonic (mname); + mot->machmodel = dbe_strdup (_machmodel); + mot->short_description = dbe_strdup (short_description); + mot->long_description = dbe_strdup (long_description); + + // add it to the list + dyn_memobj->append (mot); + + // tell the session + if (dbeSession != NULL) + dbeSession->mobj_define (mot); + return NULL; +} + +// Static function to delete a new memory object type + +char * +MemorySpace::mobj_delete (char *mname) +{ + if (mname == NULL) + return dbe_strdup (GTXT ("No memory object name has been specified.\n")); + + // search the dynamic types + for (long idx = 0, sz = VecSize (dyn_memobj); idx < sz; idx++) + { + MemObjType_t *mt = dyn_memobj->get (idx); + if (strcasecmp (mt->name, mname) == 0) + { + // delete it from the vector + mt = dyn_memobj->remove (idx); + delete mt; + dbeSession->removeIndexSpaceByName (mname); + return NULL; + } + } + return dbe_sprintf (GTXT ("Memory object `%s' is not defined.\n"), mname); +} + +// Static function to get a list of memory object names from a machine model + +Vector <char*> * +MemorySpace::getMachineModelMemObjs (char *mname) +{ + Vector <char *> *ret = new Vector <char *> (); + if (mname == NULL) + return ret; + + // search the memory objects + int idx; + MemObjType_t *mt; + Vec_loop (MemObjType_t*, dyn_memobj, idx, mt) + { + if (mt->machmodel != NULL && strcmp (mt->machmodel, mname) == 0) + { + char *n = dbe_strdup (mt->name); + ret->append (n); + } + } + return ret; +} + +char +MemorySpace::pickMnemonic (char *name) +{ + return name[0]; +} + +void +MemorySpace::get_filter_keywords (Vector <void*> *res) +{ + Vector <char*> *kwCategory = (Vector<char*>*) res->fetch (0); + Vector <char*> *kwCategoryI18N = (Vector<char*>*) res->fetch (1); + Vector <char*> *kwDataType = (Vector<char*>*) res->fetch (2); + Vector <char*> *kwKeyword = (Vector<char*>*) res->fetch (3); + Vector <char*> *kwFormula = (Vector<char*>*) res->fetch (4); + Vector <char*> *kwDescription = (Vector<char*>*) res->fetch (5); + Vector <void*> *kwEnumDescs = (Vector<void*>*) res->fetch (6); + + char *vtypeNames[] = VTYPE_TYPE_NAMES; + for (int i = 0, sz = dyn_memobj ? dyn_memobj->size () : 0; i < sz; i++) + { + MemObjType_t *obj = dyn_memobj->fetch (i); + kwCategory->append (dbe_strdup (NTXT ("FK_MEMOBJ"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Memory Object Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_INT64])); + kwKeyword->append (dbe_strdup (obj->name)); + kwFormula->append (dbe_strdup (obj->index_expr)); + kwDescription->append (NULL); + kwEnumDescs->append (NULL); + } +} + +MemObjType_t * +MemorySpace::findMemSpaceByName (const char *mname) +{ + int idx; + MemObjType_t *mt; + + // search the dynamic types + Vec_loop (MemObjType_t*, dyn_memobj, idx, mt) + { + if (strcasecmp (mt->name, mname) == 0) + return mt; + } + return NULL; +} + +MemObjType_t * +MemorySpace::findMemSpaceByIndex (int index) +{ + int idx; + MemObjType_t *mt; + + // search the dynamic types + Vec_loop (MemObjType_t*, dyn_memobj, idx, mt) + { + if (mt->type == index) + return mt; + } + return NULL; +} diff --git a/gprofng/src/MemorySpace.h b/gprofng/src/MemorySpace.h new file mode 100644 index 0000000..7d02e5e --- /dev/null +++ b/gprofng/src/MemorySpace.h @@ -0,0 +1,113 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _MEMORYSPACE_H +#define _MEMORYSPACE_H + +#include <stdio.h> + +#include "dbe_structs.h" +#include "vec.h" +#include "Exp_Layout.h" +#include "Histable.h" +#include "Hist_data.h" +#include "Metric.h" +#include "HashMap.h" + +class Experiment; +class Expression; +class DataView; +class DbeView; +class MemObj; + +class MemObjType_t +{ +public: + MemObjType_t (); + ~MemObjType_t (); + int type; + char *name; + char *index_expr; + char *machmodel; + char mnemonic; + char *short_description; + char *long_description; +}; + +class MemorySpace +{ +public: + + MemorySpace (DbeView *_dbev, int subtype); + ~MemorySpace (); + + void reset (void); + + int + getMemObjType (void) + { + return mstype; + } + + char * + getMemObjTypeName (void) + { + return msname; + } + + Expression * + getMemObjDef (void) + { + return msindex_exp; + } + + // static members, used to define or fetch the various MemorySpaces + static void get_filter_keywords (Vector <void*> *res); + static Vector<void*> *getMemObjects (void); + static void set_MemTabOrder (Vector<int> *); + static char *mobj_define (char *, char *, char *, char *, char *); + static char *mobj_delete (char *); + static MemObjType_t *findMemSpaceByName (const char *mname); + static MemObjType_t *findMemSpaceByIndex (int index); + static char pickMnemonic (char *name); + static Vector<char *> *getMachineModelMemObjs (char *); + +private: + HashMap<uint64_t, MemObj*> *objs; + int findMemObject (uint64_t indx); + MemObj *lookupMemObject (Experiment *exp, DataView*, long); + MemObj *createMemObject (uint64_t, char *moname); + + int mstype; // type of this memory space + char *msname; // name of this memory space + Expression *msindex_exp; // index-expression for this memory space + char *msindex_exp_str; // string for index-expression + Hist_data *hist_data_all; // the cached data for mode=Hist_Data::ALL + uint64_t selected_mo_index; // which page, cacheline, etc. + int sel_ind; // index of selected object in list + DbeView *dbev; + int phaseIdx; + uint64_t idx_min; + uint64_t idx_max; + MemObj *unk_memobj; + MemObj *total_memobj; +}; + +#endif /* _MEMORYSPACE_H */ diff --git a/gprofng/src/Metric.cc b/gprofng/src/Metric.cc new file mode 100644 index 0000000..3b026ff --- /dev/null +++ b/gprofng/src/Metric.cc @@ -0,0 +1,1660 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <strings.h> +#include <limits.h> +#include <sys/param.h> + +#include "util.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Expression.h" +#include "Metric.h" + +Metric::Metric (BaseMetric *item, SubType st) : BaseMetric (*item) +{ + name = NULL; + abbr = NULL; + abbr_unit = NULL; + baseMetric = item; + set_subtype (st); + visbits = VAL_NA; + if (item->get_type () == DERIVED) + visbits = VAL_VALUE; +} + +Metric::Metric (const Metric& item) : BaseMetric (item) +{ + baseMetric = item.baseMetric; + subtype = item.subtype; + name = dbe_strdup (item.name); + abbr = dbe_strdup (item.abbr); + abbr_unit = dbe_strdup (item.abbr_unit); + visbits = item.visbits; +} + +Metric::~Metric () +{ + free (name); + free (abbr); + free (abbr_unit); +} + +// Note that BaseMetric::get_vtype() has the base value type. +// Here, we get the value type for the displayed metric, +// which may be different if comparison is used. + +ValueTag +Metric::get_vtype2 () +{ + ValueTag vtype = get_vtype (); + if (visbits & VAL_DELTA) + { + switch (vtype) + { + case VT_ULLONG: return VT_LLONG; + default: return vtype; + } + } + if (visbits & VAL_RATIO) + { + switch (vtype) + { + case VT_INT: + case VT_LLONG: + case VT_ULLONG: + case VT_FLOAT: + case VT_DOUBLE: return VT_DOUBLE; + default: return vtype; + } + } + return vtype; +} + +void +Metric::set_subtype (SubType st) +{ + subtype = st; + free (name); + free (abbr); + free (abbr_unit); + name = NULL; + abbr = NULL; + abbr_unit = NULL; + + switch (get_type ()) + { + case CP_LMS_USER: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive User CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. User CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive User CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. User CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed User CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. User CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_USER metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + abort (); + } + break; + + case CP_LMS_WAIT_CPU: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Wait CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Wait CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Wait CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Wait CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Wait CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Wait CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_WAIT_CPU metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_USER_LOCK: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive User Lock Time")); + abbr = dbe_strdup (GTXT ("Excl. User Lock")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive User Lock Time")); + abbr = dbe_strdup (GTXT ("Incl. User Lock")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed User Lock Time")); + abbr = dbe_strdup (GTXT ("Attr. User Lock")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_USER_LOCK metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_SYSTEM: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive System CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Sys. CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive System CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Sys. CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed System CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Sys. CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_SYSTEM metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case SYNC_WAIT_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Sync Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. Sync Wait")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Sync Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. Sync Wait")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Sync Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. Sync Wait")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected LWT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_TFAULT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Text Page Fault Time")); + abbr = dbe_strdup (GTXT ("Excl. Text Fault")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Text Page Fault Time")); + abbr = dbe_strdup (GTXT ("Incl. Text Fault")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Text Page Fault Time")); + abbr = dbe_strdup (GTXT ("Attr. Text Fault")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_TFAULT metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_DFAULT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Data Page Fault Time")); + abbr = dbe_strdup (GTXT ("Excl. Data Fault")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Data Page Fault Time")); + abbr = dbe_strdup (GTXT ("Incl. Data Fault")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Data Page Fault Time")); + abbr = dbe_strdup (GTXT ("Attr. Data Fault")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_DFAULT metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_KERNEL_CPU: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Kernel CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Kernel CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Kernel CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Kernel CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Kernel CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Kernel CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_KERNEL_CPU metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HWCNTR: + { + char *sstr, *estr1, *estr2; + if (get_hw_ctr () == NULL) + abort (); + sstr = get_username (); + if (st == EXCLUSIVE) + { + estr1 = GTXT ("Exclusive "); + estr2 = GTXT ("Excl. "); + } + else if (st == INCLUSIVE) + { + estr1 = GTXT ("Inclusive "); + estr2 = GTXT ("Incl. "); + } + else if (st == ATTRIBUTED) + { + estr1 = GTXT ("Attributed "); + estr2 = GTXT ("Attr. "); + } + else if (st == DATASPACE) + { + estr1 = GTXT ("Data-derived "); + estr2 = GTXT ("Data. "); + } + else + { + estr1 = dbe_sprintf (GTXT ("Unexpected hwc %s metric subtype %d"), + get_aux (), st); + estr2 = dbe_strdup (NTXT ("??")); + } + name = dbe_sprintf (NTXT ("%s%s"), estr1, sstr); + abbr = dbe_sprintf (NTXT ("%s%s"), estr2, sstr); + break; + } + + case DERIVED: + { + switch (st) + { + case EXCLUSIVE: + name = dbe_sprintf (GTXT ("Exclusive %s"), get_username ()); + abbr = dbe_sprintf (GTXT ("Excl. %s"), get_cmd ()); + break; + case INCLUSIVE: + name = dbe_sprintf (GTXT ("Inclusive %s"), get_username ()); + abbr = dbe_sprintf (GTXT ("Incl. %s"), get_cmd ()); + break; + case ATTRIBUTED: + name = dbe_sprintf (GTXT ("Attributed %s"), get_username ()); + abbr = dbe_sprintf (GTXT ("Attr. %s"), get_cmd ()); + break; + default: + name = dbe_sprintf (GTXT ("Unexpected derived %s metric subtype %d"), + get_username (), st); + abbr = dbe_strdup (NTXT ("??")); + break; + } + break; + } + + case OMP_MASTER_THREAD: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Master Thread Time")); + abbr = dbe_strdup (GTXT ("Excl. Master Thread")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Master Thread Time")); + abbr = dbe_strdup (GTXT ("Incl. Master Thread")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Master Thread Time")); + abbr = dbe_strdup (GTXT ("Attr. Master Thread")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected Master Thread metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_TOTAL: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Total Thread Time")); + abbr = dbe_strdup (GTXT ("Excl. Total Thread")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Total Thread Time")); + abbr = dbe_strdup (GTXT ("Incl. Total Thread")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Total Thread Time")); + abbr = dbe_strdup (GTXT ("Attr. Total Thread")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected TOTAL metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case SYNC_WAIT_COUNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Sync Wait Count")); + abbr = dbe_strdup (GTXT ("Excl. Sync Wait Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Sync Wait Count")); + abbr = dbe_strdup (GTXT ("Incl. Sync Wait Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Sync Wait Count")); + abbr = dbe_strdup (GTXT ("Attr. Sync Wait Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected LWCNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_TOTAL_CPU: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Total CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Total CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Total CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Total CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Total CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Total CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected TOTAL_CPU metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case CP_LMS_TRAP: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Trap CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Trap CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Trap CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Trap CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Trap CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Trap CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_TRAP metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_KFAULT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Kernel Page Fault Time")); + abbr = dbe_strdup (GTXT ("Excl. Kernel Page Fault")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Kernel Page Fault Time")); + abbr = dbe_strdup (GTXT ("Incl. Kernel Page Fault")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Kernel Page Fault Time")); + abbr = dbe_strdup (GTXT ("Attr. Kernel Page Fault")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_KFAULT metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_SLEEP: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Sleep Time")); + abbr = dbe_strdup (GTXT ("Excl. Sleep")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Sleep Time")); + abbr = dbe_strdup (GTXT ("Incl. Sleep")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Sleep Time")); + abbr = dbe_strdup (GTXT ("Attr. Sleep")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_SLEEP metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_STOPPED: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Stopped Time")); + abbr = dbe_strdup (GTXT ("Excl. Stopped")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Stopped Time")); + abbr = dbe_strdup (GTXT ("Incl. Stopped")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Stopped Time")); + abbr = dbe_strdup (GTXT ("Attr. Stopped")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_STOPPED metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HEAP_ALLOC_BYTES: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Bytes Allocated")); + abbr = dbe_strdup (GTXT ("Excl. Bytes Allocated")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Bytes Allocated")); + abbr = dbe_strdup (GTXT ("Incl. Bytes Allocated")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Bytes Allocated")); + abbr = dbe_strdup (GTXT ("Attr. Bytes Allocated")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected BYTES_MALLOCD metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HEAP_ALLOC_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Allocations")); + abbr = dbe_strdup (GTXT ("Excl. Allocations")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Allocations")); + abbr = dbe_strdup (GTXT ("Incl. Allocations")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Allocations")); + abbr = dbe_strdup (GTXT ("Attr. Allocations")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected MALLOCS metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HEAP_LEAK_BYTES: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Bytes Leaked")); + abbr = dbe_strdup (GTXT ("Excl. Bytes Leaked")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Bytes Leaked")); + abbr = dbe_strdup (GTXT ("Incl. Bytes Leaked")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Bytes Leaked")); + abbr = dbe_strdup (GTXT ("Attr. Bytes Leaked")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected BYTES_LEAKED metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HEAP_LEAK_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Leaks")); + abbr = dbe_strdup (GTXT ("Excl. Leaks")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Leaks")); + abbr = dbe_strdup (GTXT ("Incl. Leaks")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Leaks")); + abbr = dbe_strdup (GTXT ("Attr. Leaks")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected LEAKS metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_READ_BYTES: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Read Bytes")); + abbr = dbe_strdup (GTXT ("Excl. Read Bytes")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Read Bytes")); + abbr = dbe_strdup (GTXT ("Incl. Read Bytes")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Read Bytes")); + abbr = dbe_strdup (GTXT ("Attr. Read Bytes")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected READ_BYTES metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_WRITE_BYTES: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Write Bytes")); + abbr = dbe_strdup (GTXT ("Excl. Write Bytes")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Write Bytes")); + abbr = dbe_strdup (GTXT ("Incl. Write Bytes")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Write Bytes")); + abbr = dbe_strdup (GTXT ("Attr. Write Bytes")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected WRITE_BYTES metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_READ_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Read Count")); + abbr = dbe_strdup (GTXT ("Excl. Read Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Read Count")); + abbr = dbe_strdup (GTXT ("Incl. Read Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Read Count")); + abbr = dbe_strdup (GTXT ("Attr. Read Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected READCNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_WRITE_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Write Count")); + abbr = dbe_strdup (GTXT ("Excl. Write Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Write Count")); + abbr = dbe_strdup (GTXT ("Incl. Write Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Write Count")); + abbr = dbe_strdup (GTXT ("Attr. Write Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected WRITECNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_OTHER_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Other I/O Count")); + abbr = dbe_strdup (GTXT ("Excl. Other I/O Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Other I/O Count")); + abbr = dbe_strdup (GTXT ("Incl. Other I/O Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Other I/O Count")); + abbr = dbe_strdup (GTXT ("Attr. Other I/O Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OTHERIOCNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_ERROR_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive I/O Error Count")); + abbr = dbe_strdup (GTXT ("Excl. I/O Error Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive I/O Error Count")); + abbr = dbe_strdup (GTXT ("Incl. I/O Error Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed I/O Error Count")); + abbr = dbe_strdup (GTXT ("Attr. I/O Error Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected IOERRORCNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_READ_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Read Time")); + abbr = dbe_strdup (GTXT ("Excl. Read Time")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Read Time")); + abbr = dbe_strdup (GTXT ("Incl. Read Time")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Read Time")); + abbr = dbe_strdup (GTXT ("Attr. Read Time")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected READ_TIME metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_WRITE_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Write Time")); + abbr = dbe_strdup (GTXT ("Excl. Write Time")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Write Time")); + abbr = dbe_strdup (GTXT ("Incl. Write Time")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Write Time")); + abbr = dbe_strdup (GTXT ("Attr. Write Time")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected WRITE_TIME metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_OTHER_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Other I/O Time")); + abbr = dbe_strdup (GTXT ("Excl. Other I/O Time")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Other I/O Time")); + abbr = dbe_strdup (GTXT ("Incl. Other I/O Time")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Other I/O Time")); + abbr = dbe_strdup (GTXT ("Attr. Other I/O Time")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OTHERIO_TIME metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_ERROR_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive I/O Error Time")); + abbr = dbe_strdup (GTXT ("Excl. I/O Error Time")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive I/O Error Time")); + abbr = dbe_strdup (GTXT ("Incl. I/O Error Time")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed I/O Error Time")); + abbr = dbe_strdup (GTXT ("Attr. I/O Error Time")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected IOERROR_TIME metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case SIZES: + name = dbe_strdup (GTXT ("Size")); + abbr = dbe_strdup (GTXT ("Size")); + abbr_unit = dbe_strdup (GTXT ("bytes")); + break; + + case ADDRESS: + name = dbe_strdup (GTXT ("PC Address")); + abbr = dbe_strdup (GTXT ("PC Addr.")); + break; + + case ONAME: + name = dbe_strdup (GTXT ("Name")); + abbr = dbe_strdup (GTXT ("Name")); + break; + + case OMP_NONE: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Non-OpenMP Time")); + abbr = dbe_strdup (GTXT ("Excl. Non-OMP")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Non-OpenMP Time")); + abbr = dbe_strdup (GTXT ("Incl. Non-OMP")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Non-OpenMP Time")); + abbr = dbe_strdup (GTXT ("Attr. Non-OMP")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected Non-OpenMP metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_OVHD: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Overhead Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP ovhd.")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Overhead Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP ovhd.")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Overhead Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP ovhd.")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Overhead metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_WORK: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Work Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP Work")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Work Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP Work")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Work Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP Work")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Work metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_IBAR: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Implicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP i-barr.")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Implicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP i-barr.")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Implicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP i-barr.")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Implicit Barrier metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_EBAR: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Explicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP e-barr.")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Explicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP e-barr.")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Explicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP e-barr.")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Explicit Barrier metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_WAIT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP Wait")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP Wait")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP Wait")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Wait metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_SERL: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Serial Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP serl")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Serial Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP serl")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Serial Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP serl")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Slave Idle metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_RDUC: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Reduction Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP rduc")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Reduction Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP rduc")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Reduction Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP rduc")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Reduction metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_LKWT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Lock Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP lkwt")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Lock Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP lkwt")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Lock Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP lkwt")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Lock Wait metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_CTWT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Critical Section Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP ctwt")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Critical Section Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP ctwt")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Critical Section Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP ctwt")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Critical Section Wait metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_ODWT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Ordered Section Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP odwt")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Ordered Section Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP odwt")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Ordered Section Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP odwt")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Ordered Section Wait metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_MSTR: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Master Serial Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP ser.")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Master Serial Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP ser.")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Master Serial Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP ser.")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Master Serial metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_SNGL: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Single Region Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP sngl")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Single Region Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP sngl")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Single Region Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP sngl")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Single Region metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_ORDD: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Ordered Region Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP ordd")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Ordered Region Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP ordd")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Ordered Region Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP ordd")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Ordered Region metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case RACCESS: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Race Accesses")); + abbr = dbe_strdup (GTXT ("Excl. Race Accesses")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Race Accesses")); + abbr = dbe_strdup (GTXT ("Incl. Race Accesses")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Race Accesses")); + abbr = dbe_strdup (GTXT ("Attr. Race Accesses")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected Race Access metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case DEADLOCKS: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Deadlocks")); + abbr = dbe_strdup (GTXT ("Excl. Deadlocks")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Deadlocks")); + abbr = dbe_strdup (GTXT ("Incl. Deadlocks")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Deadlocks")); + abbr = dbe_strdup (GTXT ("Attr. Deadlocks")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected Deadlocks metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + default: + abort (); + } +} //Metric::set_subtype + +static bool +is_width_ok (int lines, size_t width, size_t *tlen, int last) +{ + size_t len = 0; + for (int i = 0; i <= last; i++) + { + if (len != 0) + len++; + if (len + tlen[i] > width) + { + if (--lines == 0) + return false; + len = 0; + } + len += tlen[i]; + } + return true; +} + +void +Metric::legend_width (HistMetric *hitem, int gap) +{ + size_t tlen[MAX_LEN]; + char *tok[MAX_LEN], buf[MAX_LEN], unit[MAX_LEN]; + hitem->width = hitem->maxtime_width; + if (hitem->maxvalue_width > 0) + { + if (hitem->width > 0) + hitem->width++; + hitem->width += hitem->maxvalue_width; + } + if (is_pvisible ()) + { + if (hitem->width > 0) + { + hitem->width++; + } + hitem->width += 6; // adjust to change format from xx.yy% + } + snprintf (buf, sizeof (buf), "%s", get_abbr ()); + size_t max_len = hitem->width; + if (legend) + { + size_t legend_len = strlen (legend); + if (max_len < legend_len) + max_len = legend_len; + } + tok[0] = buf; + int last = 0; + for (int i = 0;; i++) + { + if (buf[i] == ' ') + { + buf[i] = '\0'; + while (buf[i + 1] == ' ') + i++; + tlen[last] = strlen (tok[last]); + if (max_len < tlen[last]) + max_len = tlen[last]; + last++; + tok[last] = buf + i + 1; + } + else if (buf[i] == '\0') + { + tlen[last] = strlen (tok[last]); + if (max_len < tlen[last]) + max_len = tlen[last]; + if (tlen[last] == 0 && last > 0) + last--; + break; + } + } + + *unit = '\0'; // get the extra unit tokens + int max_lines = 3; + if (is_tvisible ()) + { + char *s = GTXT ("sec."); + if ((get_visbits () & VAL_DELTA) != 0) + s = GTXT ("delta"); + else if ((get_visbits () & VAL_RATIO) != 0) + s = GTXT ("ratio"); + long len = strlen (s); + if (hitem->maxtime_width < len) + { + hitem->width += len - hitem->maxtime_width; + hitem->maxtime_width = len; + } + snprintf (unit, sizeof (unit), "%*s", (int) hitem->maxtime_width, s); + } + if (is_visible ()) + { + char *s = NTXT (""); + if (!is_tvisible ()) + { + if ((get_visbits () & VAL_DELTA) != 0) + s = GTXT ("delta"); + else if ((get_visbits () & VAL_RATIO) != 0) + s = GTXT ("ratio"); + else if ((get_value_styles () & VAL_TIMEVAL) != 0 && !is_time_val ()) + s = GTXT ("sec."); + } + long len = strlen (s); + if (hitem->maxvalue_width < len) + { + hitem->width += len - hitem->maxvalue_width; + hitem->maxvalue_width = len; + } + if (*unit) + { + max_lines = 2; + len = strlen (unit); + snprintf (unit + len, sizeof (unit) - len, " %*s", + (int) hitem->maxvalue_width, s); + } + else + snprintf (unit, sizeof (unit), "%*s", (int) hitem->maxvalue_width, s); + } + if (is_pvisible ()) + { + max_lines = 2; + if (*unit) + { + size_t len = strlen (unit); + snprintf (unit + len, sizeof (unit) - len, GTXT (" %%")); + } + else + snprintf (unit, sizeof (unit), GTXT (" %%")); + } + for (size_t i = strlen (unit); i > 0;) + { // remove trailing spaces + i--; + if (unit[i] != ' ') + break; + unit[i] = 0; + } + + if (*unit) + { + last++; + tlen[last] = strlen (unit); + tok[last] = unit; + if (max_len < tlen[last]) + max_len = tlen[last]; + if (max_lines == 3 && *unit == ' ') + { + char *str = unit; + while (*str == ' ') + str++; + tlen[last] = strlen (str); + tok[last] = str; + } + } + + int last1 = max_lines == 3 ? last : last - 1; + while (!is_width_ok (max_lines, max_len, tlen, last1)) + max_len++; + hitem->width = max_len + gap; + + char *legends[3]; + legends[0] = hitem->legend1; + legends[1] = hitem->legend2; + legends[2] = hitem->legend3; + for (int i = 0, ind = 0; i < 3; i++) + { + char *str = legends[i]; + *str = 0; + for (; ind <= last; ind++) + { + if (*unit && (ind == last)) + { + // Try to put 'unit' in 'legend3' + if (i != 2) + { + tok[last] = unit; // restore a formated 'unit' + break; + } + } + size_t len = strlen (str); + if (len != 0) + { + if (len + 1 + tlen[ind] > max_len) + break; + snprintf (str + len, MAX_LEN - len, NTXT (" %s"), tok[ind]); + } + else + { + if (len + tlen[ind] > max_len) + break; + snprintf (str + len, MAX_LEN - len, NTXT ("%s"), tok[ind]); + } + } + } +} + +int +Metric::get_real_visbits () +{ + int v = visbits; + if (!is_time_val () && (visbits & (VAL_TIMEVAL | VAL_VALUE)) != 0) + { + v &= ~(VAL_TIMEVAL | VAL_VALUE); + v |= (get_value_styles () & (VAL_TIMEVAL | VAL_VALUE)); + } + return v; +} + +char * +Metric::get_vis_string (int vis) +{ + char *vis_str; + if (subtype == STATIC) + vis_str = NTXT (""); + else + { + int v; + if (is_time_val ()) + v = vis & (VAL_TIMEVAL | VAL_VALUE | VAL_PERCENT); + else + { + v = vis & VAL_PERCENT; + if ((vis & (VAL_TIMEVAL | VAL_VALUE)) != 0) + v |= (get_value_styles () & (VAL_TIMEVAL | VAL_VALUE)); + } + switch (v) + { + case VAL_TIMEVAL: + vis_str = NTXT ("."); + break; + case VAL_VALUE: + vis_str = NTXT ("+"); + break; + case VAL_TIMEVAL | VAL_VALUE: + vis_str = NTXT (".+"); + break; + case VAL_PERCENT: + vis_str = NTXT ("%"); + break; + case VAL_TIMEVAL | VAL_PERCENT: + vis_str = NTXT (".%"); + break; + case VAL_VALUE | VAL_PERCENT: + vis_str = NTXT ("+%"); + break; + case VAL_TIMEVAL | VAL_VALUE | VAL_PERCENT: + vis_str = NTXT (".+%"); + break; + default: + vis_str = NTXT ("!"); + break; + } + } + return vis_str; +} + +char * +Metric::get_vis_str () +{ + char *vis_str = NULL; + if (visbits == -1) + { + // unitialized, return all possible with a trailing - + if (subtype == STATIC) + vis_str = NTXT (".-"); + else if (is_time_val ()) + vis_str = NTXT (".+%-"); + else + vis_str = NTXT (".%-"); + } + else + vis_str = get_vis_string (get_real_visbits ()); + return vis_str; +} + +void +Metric::set_dmetrics_visbits (int dmetrics_visbits) +{ + visbits = VAL_NA; // clear global state + + // process the "show" bits + int _visbits = dmetrics_visbits & ~VAL_HIDE_ALL; + assert (_visbits != -1); + if (_visbits == 0) + return; // done. (Ignore VAL_HIDE_ALL since there's nothing to hide.) + if (get_subtype () == STATIC) + // percent, time value not applicable + visbits = VAL_VALUE; + else + { + // now or in the bits, but manage . and + according to the is_time_val setting + if (is_time_val () == 0) + { + if ((_visbits & VAL_VALUE) || (_visbits & VAL_TIMEVAL)) + visbits |= VAL_VALUE; + } + else + visbits |= (_visbits & (VAL_VALUE | VAL_TIMEVAL)); + visbits |= (_visbits & (VAL_PERCENT | VAL_RATIO | VAL_DELTA)); + } + // process the "hide" bit + if (dmetrics_visbits & VAL_HIDE_ALL) + visbits |= VAL_HIDE_ALL; +} + +void +Metric::set_vvisible (bool set) +{ + if (set) + { + visbits |= VAL_VALUE; + visbits &= ~VAL_HIDE_ALL; + } + else + visbits &= ~VAL_VALUE; +} + +void +Metric::set_tvisible (bool set) +{ + if (set) + { + visbits |= VAL_TIMEVAL; + visbits &= ~VAL_HIDE_ALL; + } + else + visbits &= ~VAL_TIMEVAL; +} + +void +Metric::set_pvisible (bool set) +{ + if (set) + { + visbits |= VAL_PERCENT; + visbits &= ~VAL_HIDE_ALL; + } + else + visbits &= ~VAL_PERCENT; +} + +char * +Metric::get_mcmd (bool allPossible) +{ + char *sc = NTXT (""); // subtype == STATIC + char *hide; + char *vis = get_vis_string (allPossible ? get_value_styles () + : get_real_visbits ()); + if (subtype == INCLUSIVE) + sc = NTXT ("i"); + else if (subtype == EXCLUSIVE) + sc = NTXT ("e"); + else if (subtype == ATTRIBUTED) + sc = NTXT ("a"); + else if (subtype == DATASPACE) + sc = NTXT ("d"); + if (allPossible) + hide = NTXT (""); + else if (visbits == VAL_NA || (visbits & VAL_HIDE_ALL) != 0) + hide = NTXT ("!"); + else + hide = NTXT (""); + + char *mcmd = get_cmd (); + return dbe_sprintf (GTXT ("%s%s%s%s"), sc, hide, vis, mcmd); +} + +char * +Metric::dump () +{ + int len = 4; + BaseMetric *bm = (BaseMetric *) this; + char *s = bm->dump (); + char *msg = dbe_sprintf ("%s\n%*c subtype=%d time_val=%d vis=%d tvis=%d" + " pvis=%d\n%*c abbr='%s' cmd='%s' name='%s'\n", + STR (s), len, ' ', get_subtype (), is_time_val (), + is_visible (), is_tvisible (), is_pvisible (), + len, ' ', STR (get_abbr ()), STR (get_cmd ()), + STR (get_name ())); + free (s); + return msg; +} + diff --git a/gprofng/src/Metric.h b/gprofng/src/Metric.h new file mode 100644 index 0000000..a2b3778 --- /dev/null +++ b/gprofng/src/Metric.h @@ -0,0 +1,188 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _METRIC_H +#define _METRIC_H + +#include "dbe_structs.h" +#include "vec.h" +#include "enums.h" +#include "BaseMetric.h" + +#define MAX_LEN 1024 + +class Expression; + +// The metric class defines the metrics that are available. The metrics are +// registered when the experiment's log file is read. +class Metric : public BaseMetric +{ +public: + + typedef struct HistMetricS + { + int width; + int maxvalue_width; + int maxtime_width; + char legend1[MAX_LEN]; + char legend2[MAX_LEN]; + char legend3[MAX_LEN]; + int indFirstExp; // only for -compare=[delta|ratio] + int indTimeVal; // only for HWC time-converted metrics + void update_max (struct HistMetricS *hm); + void init (); + } HistMetric; + + Metric (const Metric& item); // copy constructor + Metric (BaseMetric *item, SubType st); + Metric (char *_name, SubType st); // for derived metrics + virtual ~Metric (); + + char *get_mcmd (bool); // e.user, a.total, etc. NOTI18N + int get_real_visbits (); // methods for managing visibility + ValueTag get_vtype2 (); // takes comparison visbits into account + void set_dmetrics_visbits (int _dmetrics_visbits); + + // fetch various fields from a Metric + SubType + get_subtype () + { + return subtype; + } + + char * + get_name () + { + return name; + } + + char * + get_abbr () + { + return abbr; + } + + char * + get_abbr_unit () + { + return abbr_unit; + } + + BaseMetric * + get_base_metric () + { + return baseMetric; + } + + int + get_visbits () + { + return visbits; + } + + void + set_raw_visbits (int _visbits) + { + visbits = _visbits; + } + + void + clear_all_visbits () + { + visbits = VAL_NA; + } + + void + enable_all_visbits () + { + visbits = get_value_styles (); + } + + +#define VAL_IS_HIDDEN(n) ((n) == -1 || (n) == VAL_NA || ((n) & VAL_HIDE_ALL) != 0) + + bool + is_any_visible () + { + return !VAL_IS_HIDDEN (visbits) + && (visbits & (VAL_VALUE | VAL_TIMEVAL | VAL_PERCENT)); + } + + bool + is_value_visible () + { + return (visbits & VAL_VALUE) != 0 + || (!is_time_val () && (visbits & VAL_TIMEVAL) != 0); + } + + bool + is_time_visible () + { + return is_time_val () && (visbits & VAL_TIMEVAL) != 0; + } + + bool + is_visible () + { + return !VAL_IS_HIDDEN (visbits) && is_value_visible (); + } + + bool + is_tvisible () + { + return !VAL_IS_HIDDEN (visbits) && is_time_visible (); + } + + bool + is_pvisible () + { + return !VAL_IS_HIDDEN (visbits) && (visbits & VAL_PERCENT) != 0; + } + + bool + is_time_val () + { + int v = VAL_TIMEVAL | VAL_VALUE; + return (get_value_styles () & v) == v; + } + + // per-bit handling of visbits + // Note: Forces VAL_HIDE_ALL to zero. Use only on temporary Metric objects. + void set_vvisible (bool set); + void set_tvisible (bool set); + void set_pvisible (bool set); + + void set_subtype (SubType st); + void legend_width (HistMetric *hitem, int gap); + char *get_vis_str (); + char *get_vis_string (int vis); + char *dump (); + + +private: + BaseMetric *baseMetric; + SubType subtype; // specific variant for this Metric + char *name; + char *abbr; + char *abbr_unit; + int visbits; // ValueType, e.g. VAL_VALUE|VAL_TIMEVAL +}; + +#endif /* _METRIC_H */ diff --git a/gprofng/src/MetricList.cc b/gprofng/src/MetricList.cc new file mode 100644 index 0000000..7596524 --- /dev/null +++ b/gprofng/src/MetricList.cc @@ -0,0 +1,1075 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "util.h" +#include "Command.h" +#include "DbeSession.h" +#include "MetricList.h" +#include "StringBuilder.h" + +// Build a metric reference list +MetricList::MetricList (Vector<BaseMetric*> *base_metrics, MetricType _mtype) +{ + mtype = _mtype; + items = new Vector<Metric*>; + sort_ref_index = 0; + sort_reverse = false; + + Metric *mitem; + // loop over the base_metrics, and add in all the appropriate subtypes + for (long i = 0, sz = base_metrics ? base_metrics->size () : 0; i < sz; i++) + { + BaseMetric *mtr = base_metrics->get (i); + if (mtr->is_internal ()) + continue; + switch (mtype) + { + case MET_DATA: + if ((mtr->get_flavors () & BaseMetric::DATASPACE) != 0) + { + mitem = new Metric (mtr, BaseMetric::DATASPACE); + items->append (mitem); + } + break; + + case MET_INDX: + { + if ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0 + || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0) + { + int index2; + Metric *item2 = NULL; + bool found = false; + Vec_loop (Metric*, items, index2, item2) + { + if (item2->get_subtype () == BaseMetric::EXCLUSIVE + && dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0) + { + found = true; + break; + } + } + if (found == false) + { + mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); + items->append (mitem); + } + } + } + break; + + case MET_CALL: + case MET_CALL_AGR: + if ((mtr->get_flavors () & BaseMetric::ATTRIBUTED) != 0) + { + mitem = new Metric (mtr, BaseMetric::ATTRIBUTED); + items->append (mitem); + } + // now fall through to add exclusive and inclusive + + case MET_NORMAL: + case MET_COMMON: + if (mtr->get_flavors () & BaseMetric::EXCLUSIVE) + { + mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); + items->append (mitem); + } + if (mtr->get_flavors () & BaseMetric::INCLUSIVE) + { + mitem = new Metric (mtr, BaseMetric::INCLUSIVE); + items->append (mitem); + } + break; + case MET_SRCDIS: + if (mtr->get_flavors () & BaseMetric::INCLUSIVE) + { + mitem = new Metric (mtr, BaseMetric::INCLUSIVE); + items->append (mitem); + } + break; + case MET_IO: + { + if (mtr->get_packet_type () == DATA_IOTRACE + && ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0 + || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0)) + { + int index2; + Metric *item2 = NULL; + bool found = false; + Vec_loop (Metric*, items, index2, item2) + { + if (item2->get_subtype () == BaseMetric::EXCLUSIVE + && dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0) + { + found = true; + break; + } + } + if (found == false) + { + mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); + items->append (mitem); + } + } + } + break; + case MET_HEAP: + { + if (mtr->get_packet_type () == DATA_HEAP + && ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0 + || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0)) + { + int index2; + Metric *item2 = NULL; + bool found = false; + Vec_loop (Metric*, items, index2, item2) + { + if ((item2->get_subtype () == BaseMetric::EXCLUSIVE) && + (dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0)) + { + found = true; + break; + } + } + if (found == false) + { + mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); + items->append (mitem); + } + } + } + break; + } + + // add the static + if (mtr->get_flavors () & BaseMetric::STATIC) + { + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + case MET_CALL: + case MET_CALL_AGR: + case MET_SRCDIS: + mitem = new Metric (mtr, BaseMetric::STATIC); + items->append (mitem); + break; + default: + if (mtr->get_type () == BaseMetric::ONAME) + { + mitem = new Metric (mtr, BaseMetric::STATIC); + items->append (mitem); + } + break; + } + } + } + // set all metrics visible + for (long i = 0, sz = items ? items->size () : 0; i < sz; i++) + items->get (i)->enable_all_visbits (); +} + +// Constructor for an empty list -- items will be added one at a time +MetricList::MetricList (MetricType _mtype) +{ + mtype = _mtype; + items = new Vector<Metric*>; + sort_ref_index = 0; + sort_reverse = false; +} + +MetricList::~MetricList () +{ + Destroy (items); +} + +// Duplicate a metric list +MetricList::MetricList (MetricList *old) +{ + mtype = old->mtype; + + // get an empty vector + items = new Vector<Metric*>; + Metric *item; + Metric *nitem; + int index; + sort_ref_index = old->get_sort_ref_index (); + sort_reverse = old->get_sort_rev (); + Vec_loop (Metric*, old->items, index, item) + { + nitem = new Metric (*item); + items->append (nitem); + } +} + +// set_metrics: +// Sets the particular metric list, according to the metric spec +// If fromRcFile, updates dbeSession->get_reg_metrics_tree() with new defaults. +char * +MetricList::set_metrics (const char *mspec, bool fromRcFile, + DerivedMetrics * /* derived_metrics */) +{ + BaseMetric::SubType subtypes[10]; + int nsubtypes; + int dmetrics_vis; // literal translation of metrics/dmetrics %.+ + bool parseOK = false; + char *errbuf; + Vector<Metric*> *old_items = items; + items = new Vector<Metric*>; + Vector<BaseMetric*> *base_items = dbeSession->get_base_reg_metrics (); + + // and copy the input specification + char *buf = dbe_strdup (mspec); + + // append metric items from parsing the string + for (char *mcmd = strtok (buf, NTXT (":")); mcmd != NULL; + mcmd = strtok (NULL, NTXT (":"))) + { + // parse the single metric_spec, based on the type of list being constructed, into: + // a vector of SubTypes (any of [iead] or STATIC) + // a integer mask for the visibility bits + // and the string name of the base metric + // it might be "all", "any", or "hwc" or it should match a metric in the list + // it might also be "bit", meaning any bit-computed metric + char *mname = parse_metric_spec (mcmd, subtypes, &nsubtypes, + &dmetrics_vis, &parseOK); + if (!parseOK) + { + // error parsing the metric specification + // not from an rc file, it's an error + if (!fromRcFile) + { + delete base_items; + items->destroy (); + delete items; + items = old_items; + free (buf); + return mname; + } + continue; + } + + // loop over subtypes requested + // set the visibility of and sort order according to the vis bits, + // and the order of encounter in the processing + int ret = add_matching_dmetrics (base_items, mname, subtypes, nsubtypes, + dmetrics_vis, fromRcFile); + if (ret != 0 && !fromRcFile) + { + if (ret == 1) + errbuf = dbe_sprintf (GTXT ("No data recorded to support metric specification: %s\n"), + mcmd); + else + errbuf = dbe_sprintf (GTXT ("Metric specification for `%s' has appeared before in %s"), + mcmd, mspec); + delete base_items; + items->destroy (); + delete items; + items = old_items; + free (buf); + return errbuf; + } + } // we've processed the entire spec + + // update metric defaults + if (fromRcFile) + { + for (long i = 0, sz = items->size (); i < sz; i++) + { + Metric *m = items->get (i); + int visbits = m->get_visbits (); + BaseMetric::SubType subtype = m->get_subtype (); + BaseMetric *reg_bm = m->get_base_metric (); + reg_bm->set_default_visbits (subtype, visbits); + BaseMetricTreeNode *mtree = dbeSession->get_reg_metrics_tree (); + BaseMetricTreeNode *bmtnode = mtree->register_metric (m); + BaseMetric *tree_bm = bmtnode->get_BaseMetric (); + tree_bm->set_default_visbits (subtype, visbits); + } + } + + // ensure that name is present, remove hidden metrics + nsubtypes = 1; + for (long i = items->size () - 1; i >= 0; i--) + { + Metric *m = items->fetch (i); + if (!m->is_any_visible ()) + { + delete m; + items->remove (i); + continue; + } + if (m->get_type () == BaseMetric::ONAME) + nsubtypes = 0; + } + + // did we get at least one valid match? + if (items->size () == 0 && !fromRcFile) + { + errbuf = dbe_sprintf (GTXT ("No valid metrics specified in `%s'\n"), mspec); + delete base_items; + items->destroy (); + delete items; + items = old_items; + free (buf); + return errbuf; + } + + if (nsubtypes == 1) + { + subtypes[0] = BaseMetric::STATIC; + (void) add_matching_dmetrics (base_items, NTXT ("name"), subtypes, 1, VAL_VALUE, true); + } + + // replace the old list of items, with the new set + if (old_items) + { + old_items->destroy (); + delete old_items; + } + set_fallback_sort (); + free (buf); + delete base_items; + return NULL; +} + +void +MetricList::set_fallback_sort () +{ + // sort by first visible of the appropriate flavor + char *sortcmd = NULL; + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + sortcmd = NTXT ("ei.any:name"); + break; + case MET_SRCDIS: + sortcmd = NTXT ("i.any:name"); + break; + case MET_CALL: + case MET_CALL_AGR: + sortcmd = NTXT ("a.any:name"); + break; + case MET_DATA: + sortcmd = NTXT ("d.any:name"); + break; + case MET_INDX: + sortcmd = NTXT ("e.any:name"); + break; + case MET_IO: + sortcmd = NTXT ("e.any:name"); + break; + case MET_HEAP: + sortcmd = NTXT ("e.any:name"); + break; + } + if (NULL != sortcmd) + (void) set_sort (sortcmd, true); +} + +void +MetricList::set_metrics (MetricList *mlist) +{ + // verify that the type is appropriate for the call + if (mtype == MET_NORMAL || mtype == MET_COMMON + || (mlist->mtype != MET_NORMAL && mlist->mtype != MET_COMMON)) + abort (); + + Vector<Metric*> *mlist_items = mlist->get_items (); + items->destroy (); + items->reset (); + + int sort_ind = mlist->get_sort_ref_index (); + for (int i = 0, mlist_sz = mlist_items->size (); i < mlist_sz; i++) + { + Metric *mtr = mlist_items->fetch (i); + if (!mtr->is_any_visible ()) + continue; + + // Add a new Metric with probably a new sub_type to this->items: + // for MET_CALL and MET_CALL_AGR the matching entry to an e. or i. is itself + // for MET_DATA, the matching entry to an e. or i. is the d. metric + // for MET_INDX, the matching entry to an e. or i. is the e. metric + // for MET_IO, the matching entry to an e. or i. is the e. metric + // for MET_HEAP, the matching entry to an e. or i. is the e. metric + // Save static entries (SIZES and ADDRESS) only for MET_NORMAL, MET_CALL, MET_CALL_AGR, MET_SRCDIS + switch (mtr->get_type ()) + { + case BaseMetric::SIZES: + case BaseMetric::ADDRESS: + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + case MET_CALL: + case MET_CALL_AGR: + case MET_SRCDIS: + break; + default: + continue; + } + break; + default: + break; + } + + BaseMetric::SubType st = mtr->get_subtype (); + if (st != BaseMetric::STATIC) + { + if (mtype == MET_CALL || mtype == MET_CALL_AGR) + { + if ((mtr->get_flavors () & BaseMetric::ATTRIBUTED) == 0) + continue; + st = BaseMetric::ATTRIBUTED; + } + else if (mtype == MET_DATA) + { + if ((mtr->get_flavors () & BaseMetric::DATASPACE) == 0) + continue; + st = BaseMetric::DATASPACE; + } + else if (mtype == MET_INDX) + { + if ((mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0) + continue; + st = BaseMetric::EXCLUSIVE; + } + else if (mtype == MET_IO) + { + if (mtr->get_packet_type () != DATA_IOTRACE || + (mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0) + continue; + st = BaseMetric::EXCLUSIVE; + } + else if (mtype == MET_HEAP) + { + if (mtr->get_packet_type () != DATA_HEAP || + (mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0) + continue; + st = BaseMetric::EXCLUSIVE; + } + else if (mtype == MET_SRCDIS) + { + if ((mtr->get_flavors () & BaseMetric::INCLUSIVE) == 0) + continue; + st = BaseMetric::INCLUSIVE; + } + } + + bool found = false; + for (int i1 = 0, items_sz = items->size (); i1 < items_sz; i1++) + { + Metric *m1 = items->fetch (i1); + if (mtr->get_id () == m1->get_id () && st == m1->get_subtype ()) + { + if (sort_ind == i) + sort_ind = i1; + found = true; + break; + } + } + if (found) + continue; + Metric *m = new Metric (*mtr); + m->set_subtype (st); + m->set_raw_visbits (mtr->get_visbits ()); + if (sort_ind == i) + sort_ind = items->size (); + items->append (m); + } + if (sort_ind >= items->size ()) + sort_ind = 0; + if (mtype == MET_IO) + sort_ind = 0; + if (mtype == MET_HEAP) + sort_ind = 0; + sort_ref_index = sort_ind; + +} + + +// set_sort: +// Sets the sort for the metric list to the first metric +// in mspec that is present; if fromRcFile is false, then +// only one metric may be specified. The requested sort +// metric must be visible, or it won't be in the metric list + +char * +MetricList::set_sort (const char *mspec, bool fromRcFile) +{ + char *mcmd; + BaseMetric::SubType subtypes[10]; + int nsubtypes; + int vis; + bool parseOK = false; + bool reverse = false; + char buf[BUFSIZ]; + char *list = buf; + char *mname; + + // copy the input specification + snprintf (buf, sizeof (buf), NTXT ("%s"), mspec); + char *listp = list; + if (*listp == '-') + { + // reverse sort specified + reverse = true; + listp++; + } + + // search for metric items from parsing the string + while ((mcmd = strtok (listp, NTXT (":"))) != NULL) + { + listp = NULL; // let strtok keep track + + // parse the single metric_spec, based on the type of list being constructed, into: + // a vector of SubTypes (any of [iead] or STATIC) + // a integer mask for the visibility bits + // and the string name of the base metric + mname = parse_metric_spec (mcmd, subtypes, &nsubtypes, &vis, &parseOK); + if (!parseOK) + { + // error parsing the metric specification + // not from an rc file, it's an error + if (!fromRcFile) + return (mname); + continue; + } + if (VAL_IS_HIDDEN (vis)) + continue; + + // loop over subtypes requested to find metric + // add a metric of that subtype, with specified vis.bits + for (int i = 0; i < nsubtypes; i++) + { + // make sure the subtype is acceptable + if ((mtype == MET_CALL || mtype == MET_CALL_AGR) + && subtypes[i] != BaseMetric::ATTRIBUTED + && subtypes[i] != BaseMetric::STATIC) + return dbe_sprintf (GTXT ("Inclusive, Exclusive, or Data metrics cannot be specified for caller-callee sort: %s\n"), + mcmd); + if (mtype == MET_DATA && subtypes[i] != BaseMetric::DATASPACE + && subtypes[i] != BaseMetric::STATIC) + return dbe_sprintf (GTXT ("Inclusive, Exclusive, or Attributed metrics cannot be specified for data-derived sort: %s\n"), + mcmd); + if (mtype == MET_INDX && subtypes[i] != BaseMetric::EXCLUSIVE + && subtypes[i] != BaseMetric::STATIC) + return dbe_sprintf (GTXT ("Inclusive, Data or Attributed metrics cannot be specified for index sort: %s\n"), + mcmd); + if ((mtype == MET_NORMAL || mtype == MET_COMMON + || mtype == MET_SRCDIS) + && (subtypes[i] == BaseMetric::DATASPACE + || subtypes[i] == BaseMetric::ATTRIBUTED)) + return dbe_sprintf (GTXT ("Data or Attributed metrics cannot be specified for sort: %s\n"), mcmd); + if (set_sort_metric (mname, subtypes[i], reverse)) + return NULL; + } + // continue looking at entries + } + + // not found on the list at all + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + case MET_SRCDIS: + return dbe_sprintf (GTXT ("Invalid sort specification: %s\n"), mspec); + case MET_CALL: + case MET_CALL_AGR: + return dbe_sprintf (GTXT ("Invalid caller-callee sort specification: %s\n"), + mspec); + case MET_DATA: + return dbe_sprintf (GTXT ("Invalid data-derived sort specification: %s\n"), + mspec); + case MET_INDX: + return dbe_sprintf (GTXT ("Invalid index sort specification: %s\n"), + mspec); + case MET_IO: + return dbe_sprintf (GTXT ("Invalid I/O sort specification: %s\n"), mspec); + case MET_HEAP: + return dbe_sprintf (GTXT ("Invalid heap sort specification: %s\n"), + mspec); + } + return NULL; +} + +// set_sort to the metric with the given visible index + +void +MetricList::set_sort (int visindex, bool reverse) +{ + Metric *mitem; + if (visindex < items->size ()) + { + mitem = items->fetch (visindex); + if (mitem->is_any_visible ()) + { + sort_ref_index = visindex; + sort_reverse = reverse; + return; + } + } + set_fallback_sort (); +} + +bool +MetricList::set_sort_metric (char *mname, BaseMetric::SubType mst, bool reverse) +{ + bool any = false, hwc = false, bit = false; + + // check keywords 'any', 'all', 'bit' and 'hwc' + if (!strcasecmp (mname, Command::ANY_CMD)) + any = true; + else if (!strcasecmp (mname, Command::ALL_CMD)) + any = true; + else if (!strcasecmp (mname, Command::HWC_CMD)) + hwc = true; + else if (!strcasecmp (mname, Command::BIT_CMD)) + bit = true; + + for (int i = 0, items_sz = items->size (); i < items_sz; i++) + { + Metric *m = items->fetch (i); + if (mst == m->get_subtype () + && (any || (hwc && m->get_type () == BaseMetric::HWCNTR) + || (bit && m->get_cmd () + && strncmp (Command::BIT_CMD, m->get_cmd (), + strlen (Command::BIT_CMD)) == 0) + || dbe_strcmp (mname, m->get_cmd ()) == 0)) + { + sort_ref_index = i; + sort_reverse = reverse; + return true; + } + } + return false; +} + +// Print to a file of a list of metrics from a supplied vector +// Debug flag = 1, prints the short name and address of the list +// Debug flag = 2, prints the details of the list +void +MetricList::print_metric_list (FILE *dis_file, char *leader, int debug) +{ + Metric *item; + int index; + char fmt_name[64]; + fprintf (dis_file, NTXT ("%s"), leader); + if (items == NULL) + { + fprintf (dis_file, GTXT ("NULL metric list can not be printed; aborting")); + abort (); + } + + if (items->size () == 0) + { + fprintf (dis_file, GTXT ("metric list is empty; aborting\n")); + abort (); + } + + // if debugging, print list address and string, and sort name + if (debug != 0) + { + char *s = get_metrics (); + fprintf (dis_file, "\tmetriclist at 0x%lx: %s, %lld metrics; sort by %s\n", + (unsigned long) this, s, (long long) items->size (), + get_sort_name ()); + free (s); + if (debug == 1) + return; + } + + // Find the longest metric name & command + size_t max_len = 0; + size_t max_len2 = 0; + + Vec_loop (Metric*, items, index, item) + { + // get the name + char *mn = item->get_name (); + size_t len = strlen (mn); + if (max_len < len) + max_len = len; + + mn = item->get_mcmd (true); + len = strlen (mn); + if (max_len2 < len) + max_len2 = len; + free (mn); + + } + if (debug == 2) + snprintf (fmt_name, sizeof (fmt_name), "%%%ds: %%-%ds", (int) max_len, + (int) max_len2); + else + snprintf (fmt_name, sizeof (fmt_name), "%%%ds: %%s", (int) max_len); + + Vec_loop (Metric*, items, index, item) + { + char *mcmd = item->get_mcmd (true); + fprintf (dis_file, fmt_name, item->get_name (), mcmd); + free (mcmd); + if (debug == 2) + fprintf (dis_file, "\t[st %2d, VT %d, vis = %4s, T=%d, sort = %c]", + item->get_subtype (), item->get_vtype (), + item->get_vis_str (), item->is_time_val (), + sort_ref_index == index ? 'Y' : 'N'); + fputc ('\n', dis_file); + } + + fputc ('\n', dis_file); + fflush (dis_file); +} + +// Return a string formatted from a vector of metrics +// string is in the form suitable for a "metrics <string>" command +char * +MetricList::get_metrics () +{ + Metric *item; + int index; + StringBuilder sb; + Vec_loop (Metric*, items, index, item) + { + if (sb.length () != 0) + sb.append (':'); + char *mcmd = item->get_mcmd (false); + sb.append (mcmd); + free (mcmd); + } + return sb.toString (); +} + +int +MetricList::get_listorder (Metric *mtr) +{ + for (int i = 0, items_sz = items->size (); i < items_sz; i++) + { + Metric *m = items->fetch (i); + if (m->get_subtype () == mtr->get_subtype () + && m->get_id () == mtr->get_id ()) + return i; + } + return -1; +} + +int +MetricList::get_listorder (char *cmd, BaseMetric::SubType st, const char *expr) +{ + for (long i = 0, items_sz = items->size (); i < items_sz; i++) + { + Metric *m = items->fetch (i); + if (m->get_subtype () == st && dbe_strcmp (m->get_cmd (), cmd) == 0 + && dbe_strcmp (m->get_expr_spec (), expr) == 0) + return (int) i; + } + return -1; +} + +Metric * +MetricList::find_metric_by_name (char *cmd) +{ + for (long i = 0, items_sz = items->size (); i < items_sz; i++) + { + Metric *m = items->fetch (i); + if (dbe_strcmp (m->get_cmd (), cmd) == 0) + return m; + } + return NULL; +} + +// find a metric by name and subtype +Metric * +MetricList::find_metric (char *cmd, BaseMetric::SubType st) +{ + int i = get_listorder (cmd, st); + if (i < 0) + return NULL; + return items->fetch (i); +} + +// Get the sort metric from a list; forces sort by first if not set +Metric * +MetricList::get_sort_metric () +{ + int i = get_sort_ref_index (); + return i >= 0 ? items->fetch (i) : NULL; +} + +char * +MetricList::get_sort_name () +{ + Metric *item = get_sort_metric (); + if (item == NULL) + return dbe_strdup (NTXT ("")); + char *n = item->get_name (); + return sort_reverse ? dbe_sprintf ("-%s", n) : dbe_strdup (n); +} + +char * +MetricList::get_sort_cmd () +{ + char *buf; + Metric *item = get_sort_metric (); + if (item == NULL) + return dbe_strdup (NTXT ("")); + char *n = item->get_mcmd (false); + if (sort_reverse) + { + buf = dbe_sprintf (NTXT ("-%s"), n); + free (n); + } + else + buf = n; + return buf; +} + +Metric * +MetricList::append (BaseMetric *bm, BaseMetric::SubType st, int visbits) +{ + for (long i = 0, sz = items->size (); i < sz; i++) + { + Metric *m = items->get (i); + if (m->get_id () == bm->get_id () && m->get_subtype () == st) + return NULL; + } + Metric *met = new Metric (bm, st); + met->set_dmetrics_visbits (visbits); + items->append (met); + return met; +} + +int +MetricList::add_matching_dmetrics (Vector<BaseMetric*> *base_items, + char *mcmd, BaseMetric::SubType *_subtypes, + int nsubtypes, int dmetrics_visbits, + bool fromRcFile) +{ + bool any = false, hwc = false, bit = false; + int got_metric = 1; + + // check keywords 'any', 'all', 'bit', and 'hwc' + if (!strcasecmp (mcmd, Command::ANY_CMD)) + any = true; + else if (!strcasecmp (mcmd, Command::ALL_CMD)) + any = true; + else if (!strcasecmp (mcmd, Command::HWC_CMD)) + hwc = true; + else if (!strcasecmp (mcmd, Command::BIT_CMD)) + bit = true; + + BaseMetric::SubType *subtypes = _subtypes; + BaseMetric::SubType all_subtypes[2] = + { BaseMetric::EXCLUSIVE, BaseMetric::INCLUSIVE }; + + if (nsubtypes == 0 || (nsubtypes == 1 && subtypes[0] == BaseMetric::STATIC)) + { + // user did not specify ei; treat as wildcard and supply both. + subtypes = all_subtypes; + nsubtypes = 2; + } + + // scan the metrics to find all matches + for (int i = 0, base_sz = base_items->size (); i < base_sz; i++) + { + BaseMetric *item = base_items->fetch (i); + if (!(any || (hwc && item->get_type () == BaseMetric::HWCNTR) + || (bit && item->get_cmd () + && strncmp (item->get_cmd (), Command::BIT_CMD, + strlen (Command::BIT_CMD)) == 0) + || dbe_strcmp (item->get_cmd (), mcmd) == 0)) + continue; + if (item->is_internal ()) + continue; + if (item->get_flavors () & BaseMetric::STATIC) + { + got_metric = 0; + int vis = item->get_type () != BaseMetric::ONAME ? + dmetrics_visbits : VAL_VALUE; + if (append (item, BaseMetric::STATIC, vis) == NULL && !fromRcFile) + return 2; + continue; + } + + // special case for omp metrics: make visible only if + // omp data has been collected + if (!dbeSession->is_omp_available () + && (strcasecmp (mcmd, "ompwork") == 0 + || strcasecmp (mcmd, "ompwait") == 0)) + continue; + + for (int j = 0; j < nsubtypes; j++) + { + if (append (item, subtypes[j], dmetrics_visbits) == NULL + && !fromRcFile) + return 2; + } + got_metric = 0; + if (!(any || hwc || bit)) + break; + } + return got_metric; +} + +// parse a single metric specification, to give: +// a vector of subtypes, and a count of the number of them +// an integer visibility +// return the string for the metric name + +char * +MetricList::parse_metric_spec (char *mcmd, BaseMetric::SubType *subtypes, + int *nsubtypes, int *dmetrics_visb, bool *isOK) +{ + size_t len_vtype; + int index; + int vis; + bool got_e, got_i, got_a, got_d; + char *str = mcmd; + char *str2; + + *isOK = true; + + // For dynamic metrics, each keyword is of the form <flavor><visibility><metric-name> + // For static metrics, each keyword is of the form [<visibility>]<metric-name> + // <flavor> can be either "i" for inclusive or "e" for exclusive + // <visibility> can be any combination of "." (to show the metric as a time), + // "%" (to show it as a percentage), "+" (to show it as a count), and "!" (turn off the metric) + + // find subtype + index = 0; + size_t len_subtype = strspn (str, NTXT ("eiad")); + str2 = str + len_subtype; + + // find vis + if (len_subtype == 0) + { + // only a . or ! is possible if no subtypes + len_vtype = strspn (str2, NTXT (".!")); + vis = VAL_VALUE; + } + else + { + len_vtype = strspn (str2, NTXT (".+%!")); + vis = VAL_NA; + } + + // if no visibility bits, there can't be a subtype + if (len_vtype == 0) + len_subtype = 0; + + if (len_subtype == 0) + { + // must be a static metric + subtypes[index++] = BaseMetric::STATIC; + vis = VAL_VALUE; + } + else + { + // figure out which subtypes are specified + got_e = got_i = got_a = got_d = false; + for (size_t i = 0; i < len_subtype; i++) + { + str += len_subtype; + if (mcmd[i] == 'e') + { // exclusive + if (mtype == MET_DATA) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for data metrics\n"), + mcmd); + } + if (!got_e) + { + got_e = true; + subtypes[index++] = BaseMetric::EXCLUSIVE; + } + } + else if (mcmd[i] == 'i') + { // inclusive + if (mtype == MET_DATA) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for data metrics\n"), + mcmd); + } + if (mtype == MET_INDX) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for index metrics\n"), + mcmd); + } + if (!got_i) + { + got_i = true; + subtypes[index++] = BaseMetric::INCLUSIVE; + } + } + else if (mcmd[i] == 'a') + { // attributed + if (mtype != MET_CALL && mtype != MET_CALL_AGR) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s applicable for caller-callee metrics only\n"), + mcmd); + } + if (!got_a) + { + got_a = true; + subtypes[index++] = BaseMetric::ATTRIBUTED; + } + } + else if (mcmd[i] == 'd') + { // data-space + if (mtype != MET_DATA) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s applicable for data-derived metrics only\n"), + mcmd); + } + if (!got_d) + { + got_d = true; + subtypes[index++] = BaseMetric::DATASPACE; + } + } + } + } + *nsubtypes = index; + + // now determine the visiblity bits + if (len_vtype > 0) + { + for (size_t i = 0; i < len_vtype; i++) + { + if (str2[i] == '+') + vis = (vis | VAL_VALUE); + else if (str2[i] == '.') + vis = (vis | VAL_TIMEVAL); + else if (str2[i] == '%') + vis = (vis | VAL_PERCENT); + else if (str2[i] == '!') + vis = (vis | VAL_HIDE_ALL); + } + } + *dmetrics_visb = vis; + return mcmd + len_subtype + len_vtype; +} diff --git a/gprofng/src/MetricList.h b/gprofng/src/MetricList.h new file mode 100644 index 0000000..b8486c1 --- /dev/null +++ b/gprofng/src/MetricList.h @@ -0,0 +1,163 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _METRICLIST_H +#define _METRICLIST_H + +#include "dbe_structs.h" +#include "vec.h" +#include "enums.h" +#include "Metric.h" +#include "DerivedMetrics.h" +#include <stdio.h> + +// +// The MetricList class is used to manage a list of metrics + +class MetricList +{ +public: + + MetricList (Vector<BaseMetric*> *base_metrics, MetricType type); + MetricList (MetricList *old); + MetricList (MetricType _mtype); + ~MetricList (); + + // Methods concerning a list of metrics + // set metrics -- text, caller-callee, data, and index flavors + // flavor depends on mtype in the list + // returns NULL if OK, or an error string if not + // always returns NULL if fromRcFile is TRUE + char *set_metrics (const char *metric_cmd, bool fromRcFile, DerivedMetrics *derived_metrics); + + // update the caller-callee or dataspace metrics to match normal metrics + // It is assumed that this->mtype is MET_CALL, MET_DATA, or MET_INDX and + // that metrics_list->mtype is MET_NORMAL + void set_metrics (MetricList *metrics_list); + + // produce a string for the metrics from a vector + char *get_metrics (); + + // set the sort metric for a list from a metric string + // returns NULL if OK, or an error string if not + char *set_sort (const char *metric_cmd, bool fromRcFile); + + // set the sort metric for a list from the first visible index + void set_fallback_sort (); + + // set the sort metric for a list from a visible index + void set_sort (int visindex, bool reverse); + + char *get_sort_name (); // get the name of the sort metric from a vector + + bool + get_sort_rev () // get the boolean reverse for the sort metric + { + return sort_reverse; + } + + void + set_sort_rev (bool v) + { + sort_reverse = v; + } + + int + get_sort_ref_index () + { + return sort_ref_index; + } + + void + set_sort_ref_index (int ind) + { + sort_ref_index = ind; + } + + bool set_sort_metric (char *metric_cmd, BaseMetric::SubType mst, bool reverse); + Metric *find_metric (char *cmd, BaseMetric::SubType st); + Metric *find_metric_by_name (char *cmd); + int get_listorder (char *cmd, BaseMetric::SubType st, const char *expr = NULL); + int get_listorder (Metric *mtr); + Metric *get_sort_metric (); // get the sort metric from a vector + char *get_sort_cmd (); // get the command name of the sort metric + + MetricType + get_type () + { + return mtype; + } + + Vector<Metric*> * + get_items () // get the vector of metrics from the list + { + return items; + } + + Metric * + get (long i) + { + return items->get (i); + } + + void + put (long i, Metric *m) + { + items->put (i, m); + } + + void + append (Metric *m) + { + items->append (m); + } + + long + size () + { + return items ? items->size () : 0; + } + + Metric *append (BaseMetric *bm, BaseMetric::SubType st, int visbits); + + // produce a list of all metrics from a vector + void print_metric_list (FILE *dis_file, char *leader, int debug); + + // Add any and all matching metrics to the growing list + // return value is zero for OK, 1 for no match + int add_matching_dmetrics (Vector<BaseMetric*> *base_items, char *cmd, + BaseMetric::SubType *subtypes, int nsubtypes, + int dmetrics_vis, // literal translation of dmetrics +. etc. + bool fromRcFile); + +private: + // parse a metric specification substring, based on type of list + char *parse_metric_spec (char *cmd, BaseMetric::SubType *subtypes, + int *nsubtypes, int *dmetrics_visb, bool *isOK); + + Vector<Metric*> *items; + MetricType mtype; + + // the sort reference index + int sort_ref_index; + bool sort_reverse; +}; + +#endif /* _METRICLIST_H */ diff --git a/gprofng/src/Module.cc b/gprofng/src/Module.cc new file mode 100644 index 0000000..1e61b42 --- /dev/null +++ b/gprofng/src/Module.cc @@ -0,0 +1,1840 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <unistd.h> +#include <ar.h> +#include <ctype.h> +#include <sys/param.h> + +#include "util.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "DataObject.h" +#include "Function.h" +#include "DbeView.h" +#include "MetricList.h" +#include "Module.h" +#include "ClassFile.h" +#include "LoadObject.h" +#include "Disasm.h" +#include "CompCom.h" +#include "Dwarf.h" +#include "DbeFile.h" +#include "PathTree.h" +#include "Elf.h" + +Module::Module () +{ + lang_code = Sp_lang_unknown; + flags = 0; + status = AE_NOTREAD; + openSourceFlag = AE_NOTREAD; + hexVisible = false; + disPath = NULL; + stabsPath = NULL; + stabsTmp = NULL; + disName = NULL; + stabsName = NULL; + indexStabsLink = NULL; + file_name = NULL; + functions = new Vector<Function*>; + loadobject = NULL; + dot_o_file = NULL; + main_source = dbeSession->get_Unknown_Source (); + srcContext = main_source; + includes = new Vector<SourceFile*>; + includes->append (main_source); + curr_inc = NULL; + fragmented = 0; + hwcprof = 0; + hdrOffset = 0; + hasDwarf = false; + hasStabs = false; + readStabs = false; + comComs = NULL; + infoList = NULL; + datatypes = NULL; + objStabs = NULL; + disasm = NULL; + comp_flags = NULL; + comp_dir = NULL; + linkerStabName = NULL; + disMTime = (time_t) 0; + stabsMTime = (time_t) 0; + real_timestamp = 0; + curr_timestamp = 0; + src_items = NULL; + dis_items = NULL; + data_items = NULL; + cur_dbev = NULL; + maximum = NULL; + maximum_inc = NULL; + empty = NULL; + inlinedSubr = NULL; +} + +Module::~Module () +{ + removeStabsTmp (); + delete includes; + if (comComs != NULL) + { + comComs->destroy (); + delete comComs; + } + free (comp_flags); + free (comp_dir); + free (linkerStabName); + free (disPath); + free (stabsPath); + free (disName); + free (stabsName); + delete functions; + free (file_name); + if (indexStabsLink) + // Remove a link to the current module + indexStabsLink->indexStabsLink = NULL; + + if (dot_o_file) + { + delete dot_o_file->dbeFile; + delete dot_o_file; + } + delete src_items; + delete dis_items; + delete disasm; + free (inlinedSubr); + if (lang_code != Sp_lang_java) + delete dbeFile; + +} + +Stabs * +Module::openDebugInfo () +{ + setFile (); + objStabs = loadobject->openDebugInfo (disPath); + return objStabs; +} + +void +Module::removeStabsTmp () +{ + // Remove temporary *.o (got from *.a) after reading Stabs + if (stabsTmp != NULL) + { + unlink (stabsTmp); + free (stabsTmp); + stabsTmp = NULL; + } +} + +int64_t +Module::get_size () +{ + Function *fp; + int index; + int64_t result = 0; + Vec_loop (Function*, functions, index, fp) + { + result += fp->size; + } + return result; +} + +bool +Module::is_fortran () +{ + return Stabs::is_fortran (lang_code); +} + +SourceFile * +Module::findSource (const char *fname, bool create) +{ + SourceFile *sf = NULL; + if (loadobject && loadobject->firstExp) + sf = loadobject->firstExp->get_source (fname); + if (sf == NULL) + sf = dbeSession->createSourceFile (fname); + for (int i = 0, sz = includes ? includes->size () : 0; i < sz; i++) + { + SourceFile *sf1 = includes->fetch (i); + if (sf == sf1) + return sf; + } + if (create) + { + if (includes == NULL) + includes = new Vector<SourceFile*>; + includes->append (sf); + return sf; + } + return NULL; +} + +SourceFile * +Module::setIncludeFile (char *includeFile) +{ + curr_inc = NULL; + if (includeFile) + curr_inc = findSource (includeFile, true); + return curr_inc; +} + +char * +Module::anno_str (char *fnm) +{ + char timebuf1[26], timebuf2[26]; + const time_t real_time = (time_t) (unsigned int) real_timestamp; + const time_t curr_time = (time_t) (unsigned int) curr_timestamp; + + switch (status) + { + case AE_OK: + case AE_NOTREAD: + return NULL; + case AE_NOSRC: + return dbe_sprintf (GTXT ("Source file `%s' not readable"), + fnm ? fnm : file_name); + case AE_NOOBJ: + if (lang_code == Sp_lang_java) + { + Emsg *emsg = get_error (); + if (emsg) + { + char *s = dbe_strdup (emsg->get_msg ()); + remove_msg (emsg); + return s; + } + return dbe_sprintf (GTXT ("Object file `%s.class' not readable"), + name); + } + return dbe_sprintf (GTXT ("Object file `%s' not readable"), get_name ()); + case AE_NOLOBJ: + if (lang_code == Sp_lang_java) + return dbe_sprintf (GTXT ("Object file `%s' not readable"), + dbeFile ? dbeFile->get_name () : name); + return dbe_sprintf (GTXT ("Object file `%s' not readable"), loadobject->get_pathname ()); + case AE_NOSTABS: + return dbe_sprintf (GTXT ("Error reading line-number information in object `%s'; source annotation not available"), + stabsPath ? stabsPath : NTXT ("")); + case AE_NOSYMTAB: + return dbe_sprintf (GTXT ("Error reading symbol table in object `%s'; disassembly annotation not available"), + disPath ? disPath : NTXT ("")); + case AE_TIMESRC: + return dbe_sprintf (GTXT ("Warning! Source file `%s' is newer than the experiment data"), + main_source->dbeFile->getResolvedPath ()); + case AE_TIMEDIS: + return dbe_sprintf (GTXT ("Warning! Object file `%s' is newer than the experiment data"), + disName ? disName : NTXT ("")); + case AE_TIMESTABS: + return dbe_sprintf (GTXT ("Warning! Object file `%s' is newer than the experiment data"), + stabsName ? stabsName : NTXT ("")); + case AE_TIMESTABS_DIFF: + snprintf (timebuf1, sizeof (timebuf1), NTXT ("%s"), ctime (&curr_time)); + snprintf (timebuf2, sizeof (timebuf2), NTXT ("%s"), ctime (&real_time)); + timebuf1[24] = timebuf2[24] = '\0'; + return dbe_sprintf (GTXT ("Warning! Object file `%s' is not the same one that was linked into executable.\n" + "\tObject file: `%s'\n\tcompiled on: %s\n" + "\tExecutable contains object file compiled on: %s"), + getResolvedObjectPath (), getResolvedObjectPath (), + timebuf1, timebuf2); + case AE_OTHER: + default: + return dbe_strdup (GTXT ("Annotation computation error")); + } +}//anno_str + +LoadObject * +Module::createLoadObject (const char *lo_name) +{ + LoadObject *lo = new LoadObject (lo_name); + lo->dbeFile->filetype |= DbeFile::F_DOT_O; + return lo; +} + +static bool +tsIsNewer (time_t t1, time_t t2) +{ + return t1 != 0 && t2 != 0 && t1 < t2; +} + +Module::Anno_Errors +Module::checkTimeStamp (bool chkDis) +{ + /* Check the linked and the real object timestamps due to bug #4796329 */ + if (real_timestamp && curr_timestamp && real_timestamp != curr_timestamp) + return AE_TIMESTABS_DIFF; + + time_t srctime = main_source->getMTime (); + for (int index = 0; index < dbeSession->nexps (); index++) + { + time_t mtime = dbeSession->get_exp (index)->get_mtime (); + if (tsIsNewer (mtime, srctime)) + return AE_TIMESRC; + if (tsIsNewer (mtime, stabsMTime)) + return AE_TIMESTABS; + if (chkDis && tsIsNewer (mtime, disMTime)) + return AE_TIMEDIS; + } + return AE_OK; +}//checkTimeStamp + +static size_t +get_ar_size (char *s, size_t len) +{ + size_t sz = 0; + for (size_t i = 0; i < len; i++) + { + if (s[i] < '0' || s[i] > '9') + break; + sz = sz * 10 + (s[i] - '0'); + } + return sz; +} + +static void +dump_hdr_field (char *nm, char *s, size_t len) +{ + Dprintf (DEBUG_READ_AR, NTXT (" %s "), nm); + for (size_t i = 0; i < len; i++) + Dprintf (DEBUG_READ_AR, "%c", isprint (s[i]) ? s[i] : '?'); + Dprintf (DEBUG_READ_AR, NTXT (" ")); + for (size_t i = 0; i < len; i++) + Dprintf (DEBUG_READ_AR, NTXT (" %d"), s[i]); + Dprintf (DEBUG_READ_AR, NTXT (" \n")); +} + +static void +dump_ar_hdr (int lineNum, struct ar_hdr *hdr) +{ + if (DEBUG_READ_AR) + { + Dprintf (DEBUG_READ_AR, NTXT ("Module::read_ar %d\n"), lineNum); + dump_hdr_field (NTXT ("ar_name"), hdr->ar_name, sizeof (hdr->ar_name)); + dump_hdr_field (NTXT ("ar_date"), hdr->ar_date, sizeof (hdr->ar_date)); + dump_hdr_field (NTXT ("ar_uid"), hdr->ar_uid, sizeof (hdr->ar_uid)); + dump_hdr_field (NTXT ("ar_gid"), hdr->ar_gid, sizeof (hdr->ar_gid)); + dump_hdr_field (NTXT ("ar_mode"), hdr->ar_mode, sizeof (hdr->ar_mode)); + dump_hdr_field (NTXT ("ar_size"), hdr->ar_size, sizeof (hdr->ar_size)); + dump_hdr_field (NTXT ("ar_fmag"), hdr->ar_fmag, sizeof (hdr->ar_fmag)); + } +} + +bool +Module::read_ar (int ar, int obj, char *obj_base) +{ + struct ar_hdr hdr; // Archive header + char magic[SARMAG]; // Magic string from archive + Dprintf (DEBUG_READ_AR, "Module::read_ar %d %p %s %s \n", __LINE__, + this, STR (obj_base), STR (get_name ())); + // Check the magic string + if ((read_from_file (ar, magic, SARMAG) != SARMAG) + || strncmp (magic, ARMAG, SARMAG)) + return false; + + // Read and skip the first file in the archive (index file to ld) + if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr)) + return false; + DEBUG_CODE dump_ar_hdr (__LINE__, &hdr); + if (lseek (ar, get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)), SEEK_CUR) + == -1) + return false; + + // Read the string file where it keeps long file names (if exist) + if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr)) + return false; + DEBUG_CODE dump_ar_hdr (__LINE__, &hdr); + char *longnames = NULL; // Area with names longer than ~13 + size_t longnames_size = 0; + if (!strncmp (hdr.ar_name, NTXT ("//"), 2)) + { + longnames_size = get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)); + longnames = (char *) malloc (longnames_size + 1); + int64_t cnt = read_from_file (ar, longnames, longnames_size); + if (cnt != (int64_t) longnames_size) + { + free (longnames); + return false; + } + longnames[longnames_size] = 0; + } + else + // back out, no long file names + lseek (ar, -(sizeof (hdr)), SEEK_CUR); + + // Search the ar for the object file name + char ar_buf[sizeof (hdr.ar_name) + 1]; + ar_buf[sizeof (hdr.ar_name)] = 0; + while (1) + { + if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr)) + break; + DEBUG_CODE dump_ar_hdr (__LINE__, &hdr); + char *ar_name; + if (hdr.ar_name[0] != '/') + { // Name is in the header + for (size_t i = 0; i < sizeof (hdr.ar_name); i++) + { + if (hdr.ar_name[i] == '/') + { + ar_buf[i] = 0; + break; + } + ar_buf[i] = hdr.ar_name[i]; + } + ar_name = ar_buf; + } + else if (hdr.ar_name[1] == ' ') + { // Name is blank + ar_buf[0] = 0; + ar_name = ar_buf; + } + else + { // Name is in the string table + if (longnames == NULL) + break; + size_t offset = get_ar_size (hdr.ar_name + 1, + sizeof (hdr.ar_name) - 1); + if (offset >= longnames_size) + break; + for (size_t i = offset; i < longnames_size; i++) + { + if (longnames[i] == '/') + { + longnames[i] = 0; + break; + } + } + ar_name = longnames + offset; + } + Dprintf (DEBUG_READ_AR, "Module::read_ar %d ar_name=%s\n", __LINE__, + ar_name); + + if (streq (ar_name, obj_base)) + { // create object file + free (longnames); + for (size_t objsize = get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)); + objsize > 0;) + { + char buf[MAXPATHLEN]; + size_t n = objsize < sizeof (buf) ? objsize : sizeof (buf); + int64_t cnt = read_from_file (ar, buf, n); + if (cnt != (int64_t) n) + return false; + cnt = write (obj, buf, n); + if (cnt != (int64_t) n) + return false; + objsize -= n; + } + return true; + } + if (lseek (ar, get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)), + SEEK_CUR) == -1) + break; + } + free (longnames); + return false; +} + +static char * +get_obj_name_from_lib (char *nm) +{ + char *base = strrchr (nm, '('); + if (base) + { + size_t last = strlen (base) - 1; + if (base[last] == ')') + return base; + } + return NULL; +} + +bool +Module::setFile () +{ + if ((loadobject->flags & SEG_FLAG_DYNAMIC) != 0) + return true; + if ((loadobject->dbeFile->filetype & DbeFile::F_FICTION) != 0) + return false; + if ((flags & MOD_FLAG_UNKNOWN) != 0) + return true; + + if (lang_code == Sp_lang_java) + { + if (dbeFile->get_need_refind ()) + { + char *fnm = dbeFile->get_location (); + stabsPath = dbe_strdup (fnm); + stabsName = dbe_strdup (fnm); + disPath = dbe_strdup (fnm); + disName = dbe_strdup (fnm); + stabsMTime = dbeFile->sbuf.st_mtime; + } + return dbeFile->get_location () != NULL; + } + + if (dbeFile == NULL) + { + char *objname = get_obj_name_from_lib (name); + if (objname) + { + // in the format of libpath(obj) + objname = dbe_strdup (objname + 1); + size_t last = strlen (objname) - 1; + objname[last] = '\0'; + } + dbeFile = new DbeFile (objname ? objname : name); + free (objname); + dbeFile->filetype |= DbeFile::F_DOT_O; + } + if (dbeFile->get_need_refind ()) + { + disMTime = (time_t) 0; + stabsMTime = (time_t) 0; + free (disName); + free (stabsName); + disName = NULL; + stabsName = NULL; + + // Find the Executable/Shared-Object file of module + char *path = loadobject->dbeFile->get_location (); + if (path) + { + disPath = strdup (path); + disName = strdup (path); + disMTime = loadobject->dbeFile->sbuf.st_mtime; + } + + char *objname = get_obj_name_from_lib (name); + if (objname) + { + // in the format of libpath(obj) + char *namebuf = dbe_strdup (name); + char *base = namebuf + (objname - name); + *base = '\0'; + base++; + size_t last = strlen (base) - 1; + base[last] = '\0'; + stabsTmp = dbeSession->get_tmp_file_name (base, false); + dbeSession->tmp_files->append (strdup (stabsTmp)); + + DbeFile *dbf = dbeSession->getDbeFile (namebuf, + DbeFile::F_DOT_A_LIB | DbeFile::F_FILE); + path = dbf->get_location (); + int ar = -1, obj = -1; + if (path != NULL) + { + ar = open64 (path, O_RDONLY | O_LARGEFILE); + if (ar != -1) + obj = open64 (stabsTmp, O_CREAT | O_WRONLY | O_LARGEFILE, 0600); + } + if (ar != -1 && obj != -1 && read_ar (ar, obj, base)) + { + dbeFile->set_location (stabsTmp); + dbeFile->check_access (stabsTmp); // init 'sbuf' + dbeFile->sbuf.st_mtime = 0; // Don't check timestamps + dbeFile->container = dbf; + stabsPath = strdup (stabsTmp); + stabsName = strdup (path); + stabsMTime = dbeFile->sbuf.st_mtime; + } + else + { + removeStabsTmp (); + objname = NULL; + } + if (ar != -1) + close (ar); + if (obj != -1) + close (obj); + free (namebuf); + } + if (objname == NULL) + { + path = dbeFile->get_location (); + if (path != NULL) + { + stabsPath = strdup (path); + stabsName = strdup (path); + stabsMTime = hasDwarf ? 0 : dbeFile->sbuf.st_mtime; + } + } + + // First, try to access the symbol table of the module itself + // If failed, access the symbol table of the executable + if (stabsPath == NULL) + { + if (disPath == NULL) + return false; + stabsPath = strdup (disPath); + stabsName = strdup (disName); + stabsMTime = disMTime; + } + else if (disPath == NULL) + { + disPath = strdup (stabsPath); + disName = strdup (stabsName); + disMTime = stabsMTime; + } + } + return stabsPath != NULL; +} + +// openStabs -- open mappings from PCs to source lines +bool +Module::openStabs (bool all) +{ + if ((loadobject->flags & SEG_FLAG_DYNAMIC) != 0 + || (flags & MOD_FLAG_UNKNOWN) != 0) + return true; + if (loadobject->platform == Java) + { + setIncludeFile (NULL); + readFile (); + return ( status == AE_OK); + } + if (readStabs) + return true; + + // Read Stabs info. + int64_t Inode = main_source->getInode (); + char *fname = strrchr (file_name, (int) '/'); + char *mname = strrchr (main_source->get_name (), (int) '/'); + if (fname && mname && !streq (fname, mname)) + { + SourceFile *sf = findSource (file_name, false); + if (sf != NULL) + Inode = sf->getInode (); + } + + comComs = new Vector<ComC*>; + Stabs *stabs = openDebugInfo (); + if (stabs == NULL) + return false; + int st = stabs->read_stabs (Inode, this, comComs, true); + if (!hasDwarf && hasStabs && !streq (stabsPath, disPath)) + { + // Read stabs from .o file + if (dot_o_file == NULL) + { + if (dbeFile->get_location ()) + { + dot_o_file = createLoadObject (dbeFile->get_name ()); + dot_o_file->dbeFile->set_location (dbeFile->get_location ()); + dot_o_file->dbeFile->sbuf = dbeFile->sbuf; + dot_o_file->dbeFile->container = dbeFile->container; + } + } + if (dot_o_file + && dot_o_file->sync_read_stabs () == LoadObject::ARCHIVE_SUCCESS) + { + Stabs *stabs_o = dot_o_file->objStabs; + if (stabs_o) + { + st = stabs_o->read_stabs (Inode, this, + comComs->size () > 0 ? NULL : comComs); + Elf *elf_o = stabs_o->openElf (false); + if (elf_o->dwarf) + stabs->read_dwarf_from_dot_o (this); + } + } + } + if (all) + read_hwcprof_info (); + + readStabs = true; + return st == Stabs::DBGD_ERR_NONE; +} + +char * +Module::get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t address, int64_t &inst_size) +{ + return disasm->get_disasm (inst_address, end_address, start_address, + address, inst_size); +} + +void +Module::read_stabs (bool all) +{ + if (openSourceFlag == AE_NOTREAD) + { + openSourceFlag = AE_OK; + if (lang_code == Sp_lang_java) + { + char *clpath = file_name; + if (clpath == NULL || strcmp (clpath, "<Unknown>") == 0) + clpath = ClassFile::get_java_file_name (name, false); + main_source = findSource (clpath, true); + main_source->dbeFile->filetype |= DbeFile::F_JAVA_SOURCE; + if (clpath != file_name) + free (clpath); + } + else + main_source = findSource (file_name, true); + if (setFile ()) + openStabs (all); + } +} + +bool +Module::openDisPC () +{ + if (disasm == NULL) + { + if (!(loadobject->flags & SEG_FLAG_DYNAMIC) && loadobject->platform != Java) + { + // Read Stabs & Symbol tables + if (openDebugInfo () == NULL) + return false; + if (!objStabs->read_symbols (functions)) + return false; + } + disasm = new Disasm (loadobject->platform, objStabs); + } + return true; +} + +static SourceFile *cmpSrcContext; // Use only for func_cmp + +static int +func_cmp (const void *a, const void *b) +{ + Function *fp1 = *((Function **) a); + Function *fp2 = *((Function **) b); + return fp1->func_cmp (fp2, cmpSrcContext); +} + +bool +Module::computeMetrics (DbeView *dbev, Function *func, MetricList *metrics, + Histable::Type type, bool src_metric, + bool func_scope, SourceFile *source) +{ + name_idx = metrics->get_listorder (NTXT ("name"), Metric::STATIC); + if (name_idx < 0) + { + metrics->print_metric_list (stderr, + GTXT ("Fatal: no name metric in Module::computeMetrics mlist:\n"), + 1); + abort (); + } + + // Now find the metrics for size and address, if present + size_index = metrics->get_listorder (NTXT ("size"), Metric::STATIC); + addr_index = metrics->get_listorder (NTXT ("address"), Metric::STATIC); + + // free the old cached data for both src and disassembly + // If it's disassembly with visible source metrics, we use both + if (dis_items) + { + delete dis_items; + dis_items = NULL; + } + if (src_items) + { + delete src_items; + src_items = NULL; + } + + // ask the DbeView to generate new data to be cached + if (src_metric || type == Histable::LINE) + { + Histable *obj = (func_scope) ? (Histable*) func : (Histable*)this; + if (lang_code == Sp_lang_java) + obj = func_scope ? (Histable *) func : + (source && source->get_type () == Histable::SOURCEFILE ? + (Histable *) source : (Histable *) this); + src_items = dbev->get_hist_data (metrics, Histable::LINE, 0, + Hist_data::MODL, obj, source); + } + if (type == Histable::INSTR) + dis_items = dbev->get_hist_data (metrics, Histable::INSTR, 0, + Hist_data::MODL, + func_scope ? (Histable*) func : (Histable*) this, + source); + + Hist_data *cur_hist_data; + if (type == Histable::INSTR) + cur_hist_data = dis_items; + else + cur_hist_data = src_items; + + Vector<Metric*> *items = cur_hist_data->get_metric_list ()->get_items (); + long sz = items->size (); + empty = new TValue[sz]; + memset (empty, 0, sizeof (TValue) * sz); + for (long i = 0; i < sz; i++) + empty[i].tag = items->get (i)->get_vtype (); + return true; +} + +// Method to get annotated source or disassembly for the module +// or a function within it +Hist_data * +Module::get_data (DbeView *dbev, MetricList *mlist, Histable::Type type, + TValue *ftotal, SourceFile *srcFile, Function *func, + Vector<int> *marks, int threshold, int vis_bits, + int src_visible, bool hex_vis, bool func_scope, + bool /*src_only*/, Vector<int_pair_t> *marks2d, + Vector<int_pair_t> *marks2d_inc) +{ + cur_dbev = dbev; + srcContext = srcFile ? srcFile : main_source; + read_stabs (); + status = AE_OK; + dbev->warning_msg = NULL; + dbev->error_msg = NULL; + if (type == Histable::LINE) + { + if (!srcContext->readSource ()) + { + status = AE_NOSRC; + dbev->error_msg = anno_str (srcContext->get_name ()); + return NULL; + } + if (!computeMetrics (dbev, func, mlist, type, false, func_scope, srcContext)) + { + status = AE_OTHER; + dbev->error_msg = anno_str (); + return NULL; + } + status = checkTimeStamp (false); + } + else + { // Histable::INSTR + Anno_Errors src_status = AE_OK; + if (!srcContext->readSource ()) + { + src_status = AE_NOSRC; + dbev->error_msg = anno_str (srcContext->get_name ()); + } + if (!setFile ()) + status = AE_NOLOBJ; + else + { + if (!openStabs ()) + src_status = AE_NOSTABS; + if (!openDisPC ()) + status = AE_NOSYMTAB; + } + if (status != AE_OK) + { + dbev->error_msg = anno_str (); + return NULL; + } + if (src_status != AE_OK && func != NULL) + { + if (loadobject->platform == Java && (func->flags & FUNC_FLAG_NATIVE) != 0) + { + append_msg (CMSG_ERROR, + GTXT ("`%s' is a native method; byte code not available\n"), + func->get_name ()); + status = AE_NOOBJ; + dbev->error_msg = anno_str (); + return NULL; + } + func_scope = true; + } + // get the disassembly-line metric data + if (!computeMetrics (dbev, func, mlist, type, + (src_visible & SRC_METRIC) != 0, + func_scope, srcContext)) + { + status = AE_OTHER; + dbev->error_msg = anno_str (); + return NULL; + } + status = checkTimeStamp (true); + } + total = ftotal; + + // initialize line number + init_line (); + + // initialize data -- get duplicate metric list for the line texts + // pick up the metric list from the computed data + MetricList *nmlist = NULL; + if (type == Histable::INSTR) + { + mlist = dis_items->get_metric_list (); + nmlist = new MetricList (mlist); + data_items = new Hist_data (nmlist, Histable::INSTR, Hist_data::MODL); + data_items->set_status (dis_items->get_status ()); + set_dis_data (func, vis_bits, dbev->get_cmpline_visible (), + src_visible, hex_vis, func_scope, + dbev->get_funcline_visible ()); + } + else + { + mlist = src_items->get_metric_list (); + nmlist = new MetricList (mlist); + data_items = new Hist_data (nmlist, Histable::LINE, Hist_data::MODL); + data_items->set_status (src_items->get_status ()); + set_src_data (func_scope ? func : NULL, vis_bits, + dbev->get_cmpline_visible (), + dbev->get_funcline_visible ()); + } + data_items->compute_minmax (); + + Metric *mitem; + int index; + Hist_data::HistItem *max_item; + TValue *value; + Hist_data::HistItem *max_item_inc; + TValue *value_inc; + double dthreshold = threshold / 100.0; + + int sz = data_items->get_metric_list ()->get_items ()->size (); + maximum = new TValue[sz]; + maximum_inc = new TValue[sz]; + memset (maximum, 0, sizeof (TValue) * sz); + memset (maximum_inc, 0, sizeof (TValue) * sz); + max_item = data_items->get_maximums (); + max_item_inc = data_items->get_maximums_inc (); + + Vec_loop (Metric*, data_items->get_metric_list ()->get_items (), index, mitem) + { + maximum_inc[index].tag = maximum[index].tag = mitem->get_vtype (); + + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (!mitem->is_visible () && !mitem->is_tvisible () + && !mitem->is_pvisible ()) + continue; + + value = &max_item->value[index]; + value_inc = &max_item_inc->value[index]; + + double dthresh; + if (mitem->is_zeroThreshold () == true) + dthresh = 0; + else + dthresh = dthreshold; + switch (value->tag) + { + case VT_INT: + maximum[index].i = (int) (dthresh * (double) value->i); + maximum_inc[index].i = (int) (dthresh * (double) value_inc->i); + break; + case VT_DOUBLE: + maximum[index].d = dthresh * value->d; + maximum_inc[index].d = dthresh * value_inc->d; + break; + case VT_LLONG: + maximum[index].ll = (unsigned long long) (dthresh * (double) value->ll); + maximum_inc[index].ll = (unsigned long long) + (dthresh * (double) value_inc->ll); + break; + case VT_ULLONG: + maximum[index].ull = (unsigned long long) + (dthresh * (double) value->ull); + maximum_inc[index].ull = (unsigned long long) + (dthresh * (double) value_inc->ull); + break; + default: + // not needed for non-numerical metrics + break; + } + } + + // mark all high values + for (int index1 = 0; index1 < data_items->size (); index1++) + { + Hist_data::HistItem *hi = data_items->fetch (index1); + int index2; + Vec_loop (Metric*, nmlist->get_items (), index2, mitem) + { + bool mark = false; + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (!mitem->is_visible () && !mitem->is_tvisible () + && !mitem->is_pvisible ()) + continue; + + switch (hi->value[index2].tag) + { + case VT_DOUBLE: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].d > maximum_inc[index2].d) + mark = true; + break; + } + if (hi->value[index2].d > maximum[index2].d) + mark = true; + break; + case VT_INT: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].i > maximum_inc[index2].i) + mark = true; + break; + } + if (hi->value[index2].i > maximum[index2].i) + mark = true; + break; + case VT_LLONG: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].ll > maximum_inc[index2].ll) + mark = true; + break; + } + if (hi->value[index2].ll > maximum[index2].ll) + mark = true; + break; + case VT_ULLONG: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].ull > maximum_inc[index2].ull) + mark = true; + break; + } + if (hi->value[index2].ull > maximum[index2].ull) + mark = true; + break; + // ignoring the following cases (why?) + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + if (mark) + { + marks->append (index1); + break; + } + } + } + + // mark all high values to marks2d + if (marks2d != NULL && marks2d_inc != NULL) + { + for (int index1 = 0; index1 < data_items->size (); index1++) + { + Hist_data::HistItem *hi = data_items->fetch (index1); + int index2; + Vec_loop (Metric*, nmlist->get_items (), index2, mitem) + { + Metric::SubType subType = mitem->get_subtype (); + if (subType == Metric::STATIC) + continue; + if (!mitem->is_visible () && !mitem->is_tvisible () + && !mitem->is_pvisible ()) + continue; + switch (hi->value[index2].tag) + { + case VT_DOUBLE: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].d > maximum_inc[index2].d) + { + int_pair_t pair = {index1, index2}; + marks2d_inc->append (pair); + } + break; + } + if (hi->value[index2].d > maximum[index2].d) + { + int_pair_t pair = {index1, index2}; + marks2d->append (pair); + } + break; + case VT_INT: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].i > maximum_inc[index2].i) + { + int_pair_t pair = {index1, index2}; + marks2d_inc->append (pair); + } + break; + } + if (hi->value[index2].i > maximum[index2].i) + { + int_pair_t pair = {index1, index2}; + marks2d->append (pair); + } + break; + case VT_LLONG: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].ll > maximum_inc[index2].ll) + { + int_pair_t pair = {index1, index2}; + marks2d_inc->append (pair); + } + break; + } + if (hi->value[index2].ll > maximum[index2].ll) + { + int_pair_t pair = {index1, index2}; + marks2d->append (pair); + } + break; + case VT_ULLONG: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].ull > maximum_inc[index2].ull) + { + int_pair_t pair = {index1, index2}; + marks2d_inc->append (pair); + } + break; + } + if (hi->value[index2].ull > maximum[index2].ull) + { + int_pair_t pair = {index1, index2}; + marks2d->append (pair); + } + break; + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + } + } + } + + // free memory used by Computing & Printing metrics + delete[] maximum; + delete[] maximum_inc; + delete[] empty; + maximum = NULL; + maximum_inc = NULL; + empty = NULL; + dbev->warning_msg = anno_str (); + return data_items; +} + +Vector<uint64_t> * +Module::getAddrs (Function *func) +{ + uint64_t start_address = func->img_offset; + uint64_t end_address = start_address + func->size; + int64_t inst_size = 0; + + // initialize "disasm" if necessary + if (!openDisPC ()) + return NULL; + + Vector<uint64_t> *addrs = new Vector<uint64_t>; + for (uint64_t inst_address = start_address; inst_address < end_address;) + { + char *s = disasm->get_disasm (inst_address, end_address, start_address, + func->img_offset, inst_size); + free (s); + addrs->append (inst_address - start_address); + inst_address += inst_size; + if (inst_size == 0) + break; + } + return addrs; +} + +void +Module::init_line () +{ + // initialize the compiler commentary data + cindex = 0; + if (comComs != NULL && comComs->size () > 0) + cline = comComs->fetch (cindex)->line; + else + cline = -1; + + sindex = 0; + if (src_items && src_items->size () > 0) + sline = ((DbeLine*) src_items->fetch (0)->obj)->lineno; + else + sline = -1; + + dindex = 0; + mindex = 0; + mline = -1; + if (dis_items && dis_items->size () > 0) + { + daddr = (DbeInstr*) dis_items->fetch (0)->obj; + + // After sorting all HistItems with PCLineFlag appear + // at the end of the list. Find the first one. + for (mindex = dis_items->size () - 1; mindex >= 0; mindex--) + { + Hist_data::HistItem *item = dis_items->fetch (mindex); + if (!(((DbeInstr*) item->obj)->flags & PCLineFlag)) + break; + mline = (unsigned) (((DbeInstr*) item->obj)->addr); + } + mindex++; + } + else + daddr = NULL; +} + +void +Module::set_src_data (Function *func, int vis_bits, int cmpline_visible, + int funcline_visible) +{ + Function *curr_func = NULL; + + // start at the top of the file, and loop over all lines in the file (source context) + for (curline = 1; curline <= srcContext->getLineCount (); curline++) + { + // Before writing the line, see if there's compiler commentary to insert + if (cline == curline) + set_ComCom (vis_bits); + + // Find out if we need to print zero metrics with the line + DbeLine *dbeline = srcContext->find_dbeline (NULL, curline); + Anno_Types type = AT_SRC_ONLY; + if (dbeline->dbeline_func_next) + { + if (func) + for (DbeLine *dl = dbeline->dbeline_func_next; dl; dl = dl->dbeline_func_next) + { + if (dl->func == func) + { + type = AT_SRC; + break; + } + } + else + type = AT_SRC; + } + + if (funcline_visible) + { // show red lines + // is there a function index line to insert? + Function *func_next = NULL; + for (DbeLine *dl = dbeline; dl; dl = dl->dbeline_func_next) + { + Function *f = dl->func; + if (f && f->line_first == curline + && f->getDefSrc () == srcContext) + { + if (lang_code == Sp_lang_java + && (f->flags & FUNC_FLAG_DYNAMIC)) + continue; + if (cur_dbev && cur_dbev->get_path_tree ()->get_func_nodeidx (f)) + { + func_next = f; + break; + } + else if (func_next == NULL) + func_next = f; + } + } + if (func_next && curr_func != func_next) + { + curr_func = func_next; + char *func_name = curr_func->get_name (); + if (is_fortran () && streq (func_name, NTXT ("MAIN_"))) + func_name = curr_func->get_match_name (); + Hist_data::HistItem *item = + src_items->new_hist_item (curr_func, AT_FUNC, empty); + item->value[name_idx].l = dbe_sprintf (GTXT ("<Function: %s>"), + func_name); + data_items->append_hist_item (item); + } + } // end of red line + set_src (type, dbeline); // add the source line + } // end of loop over source lines + + // See if compiler flags are set; if so, append them + if (cmpline_visible && comp_flags) + { + Hist_data::HistItem *item = src_items->new_hist_item (NULL, AT_EMPTY, + empty); + item->value[name_idx].l = strdup (NTXT ("")); + data_items->append_hist_item (item); + item = src_items->new_hist_item (NULL, AT_COM, empty); + item->value[name_idx].l = dbe_sprintf (GTXT ("Compile flags: %s"), + comp_flags); + data_items->append_hist_item (item); + } +} + +void +Module::set_dis_data (Function *func, int vis_bits, int cmpline_visible, + int src_visible, bool hex_vis, bool func_scope, + int funcline_visible) +{ + bool nextFile = false; + + // initialize the source output, if any + curline = (srcContext->getLineCount () > 0) ? 1 : -1; + if (func) + nextFile = srcContext != func->getDefSrc (); + curr_inc = srcContext; + + bool src_code = (src_visible & SRC_CODE); + Anno_Types src_type = (src_visible & SRC_METRIC) ? AT_SRC : AT_SRC_ONLY; + + char *img_fname = func ? func->img_fname : NULL; + + // Build a new Function list + Vector<Function*> *FuncLst = new Vector<Function*>; + if (func_scope) + { + if (func) + FuncLst->append (func); + } + else + { + for (int i = 0, sz = functions ? functions->size () : 0; i < sz; i++) + { + Function *fitem = functions->fetch (i); + if (fitem != fitem->cardinal ()) + continue; + if (img_fname == NULL) + img_fname = fitem->img_fname; + if (fitem->img_fname == NULL || strcmp (fitem->img_fname, img_fname)) + continue; + FuncLst->append (fitem); + } + } + if (FuncLst->size () == 0) + { // no function is good + delete FuncLst; + return; + } + cmpSrcContext = srcContext; + FuncLst->sort (func_cmp); + + disasm->set_hex_visible (hex_vis); + for (int index = 0, sz = FuncLst->size (); index < sz; index++) + { + Function *fitem = FuncLst->fetch (index); + uint64_t start_address, end_address; + int64_t inst_size; + if (fitem->getDefSrc () != srcContext && curline > 0) + { + // now flush the left source line, if available + for (; curline <= srcContext->getLineCount (); curline++) + { + // see if there's a compiler comment line to dump + if (cline == curline) + set_ComCom (vis_bits); + if (src_code) + set_src (src_type, srcContext->find_dbeline (curline)); + } + curline = -1; + } + + curr_inc = NULL; + // disassemble one function + start_address = objStabs ? + objStabs->mapOffsetToAddress (fitem->img_offset) : 0; + end_address = start_address + fitem->size; + inst_size = 0; + + disasm->set_addr_end (end_address); + if ((loadobject->flags & SEG_FLAG_DYNAMIC) + && loadobject->platform != Java) + disasm->set_img_name (img_fname); + + for (uint64_t inst_address = start_address; inst_address < end_address;) + { + uint64_t address = inst_address - start_address; + DbeInstr *instr = fitem->find_dbeinstr (0, address); + DbeLine *dbeline = (DbeLine *) (instr->convertto (Histable::LINE)); + if (instr->lineno == -1 && dbeline && dbeline->lineno > 0) + instr->lineno = dbeline->lineno; + + // now write the unannotated source line, if available + if (curline > 0) + { // source is present + int lineno = curline - 1; + if (instr->lineno != -1) + { + if (dbeline && streq (dbeline->sourceFile->get_name (), + srcContext->get_name ())) + lineno = instr->lineno; + } + else if (curr_inc == NULL && srcContext == fitem->def_source + && fitem->line_first > 0) + lineno = fitem->line_first; + + for (; curline <= lineno; curline++) + { + // see if there's a compiler comment line to dump + if (cline == curline) + set_ComCom (vis_bits); + if (mline == curline) + set_MPSlave (); + if (src_code) + set_src (src_type, srcContext->find_dbeline (curline)); + if (curline >= srcContext->getLineCount ()) + { + curline = -1; + break; + } + } + } + + if (funcline_visible) + { // show red lines + if (!curr_inc || (dbeline && curr_inc != dbeline->sourceFile)) + { + Hist_data::HistItem *item = dis_items->new_hist_item (dbeline, AT_FUNC, empty); + curr_inc = dbeline ? dbeline->sourceFile : srcContext; + char *str; + if (curr_inc != srcContext) + { + char *fileName = curr_inc->dbeFile->getResolvedPath (); + str = dbe_sprintf (GTXT ("<Function: %s, instructions from source file %s>"), + fitem->get_name (), fileName); + } + else + str = dbe_sprintf (GTXT ("<Function: %s>"), + fitem->get_name ()); + item->value[name_idx].l = str; + data_items->append_hist_item (item); + } + } + + char *dis_str = get_disasm (inst_address, end_address, start_address, + fitem->img_offset, inst_size); + if (inst_size == 0) + break; + else if (instr->size == 0) + instr->size = (unsigned int) inst_size; + inst_address += inst_size; + + // stomp out control characters + for (size_t i = 0, len = strlen (dis_str); i < len; i++) + { + if (dis_str[i] == '\t') + dis_str[i] = ' '; + } + + for (int i = 0; i < bTargets.size (); i++) + { + target_info_t *bTarget = bTargets.fetch (i); + if (bTarget->offset == fitem->img_offset + address) + { + // insert a new line for the bTarget + size_t colon = strcspn (dis_str, NTXT (":")); + char *msg = GTXT ("* <branch target>"); + size_t len = colon + strlen (msg); + len = (len < 50) ? (50 - len) : 1; + char *new_dis_str = dbe_sprintf ("%.*s%s%*c <===----<<<", + (int) colon, dis_str, msg, + (int) len, ' '); + DbeInstr *bt = fitem->find_dbeinstr (PCTrgtFlag, address); + bt->lineno = instr->lineno; + bt->size = 0; + set_dis (bt, AT_DIS, nextFile, new_dis_str); + break; + } + } + + // AnalyzerInfo/Datatype annotations + if (infoList != NULL) + { + inst_info_t *info = NULL; + int pinfo; + Vec_loop (inst_info_t*, infoList, pinfo, info) + { + if (info->offset == fitem->img_offset + address) break; + } + if (info != NULL) + { // got a matching memop + char typetag[400]; + typetag[0] = '\0'; + long t; + datatype_t *dtype = NULL; + Vec_loop (datatype_t*, datatypes, t, dtype) + { + if (dtype->datatype_id == info->memop->datatype_id) + break; + } + if (datatypes != NULL) + { + size_t len = strlen (typetag); + if (dtype == NULL || t == datatypes->size ()) + snprintf (typetag + len, sizeof (typetag) - len, "%s", + PTXT (DOBJ_UNSPECIFIED)); + else if (dtype->dobj == NULL) + snprintf (typetag + len, sizeof (typetag) - len, "%s", + PTXT (DOBJ_UNDETERMINED)); + else + snprintf (typetag + len, sizeof (typetag) - len, "%s", + dtype->dobj->get_name ()); + } + if (strlen (typetag) > 1) + { + char *new_dis_str; + new_dis_str = dbe_sprintf ("%-50s %s", dis_str, typetag); + free (dis_str); + dis_str = new_dis_str; + } + } + } + set_dis (instr, AT_DIS, nextFile, dis_str); + } + } + + // now flush the left source line, if available + if (curline > 0) + { // source is present + for (; curline <= srcContext->getLineCount (); curline++) + { + // see if there's a compiler comment line to dump + if (cline == curline) + set_ComCom (vis_bits); + + if (src_code) + set_src (src_type, srcContext->find_dbeline (curline)); + } + } + + // See if compiler flags are set; if so, append them + if (cmpline_visible && comp_flags) + { + Hist_data::HistItem *item = dis_items->new_hist_item (NULL, AT_EMPTY, + empty); + item->value[name_idx].l = dbe_strdup (NTXT ("")); + data_items->append_hist_item (item); + item = dis_items->new_hist_item (NULL, AT_COM, empty); + item->value[name_idx].l = dbe_sprintf (GTXT ("Compile flags: %s"), + comp_flags); + data_items->append_hist_item (item); + } + delete FuncLst; +} + +// set_src -- inserts one or more lines into the growing data list +void +Module::set_src (Anno_Types type, DbeLine *dbeline) +{ + Hist_data::HistItem *item; + + // Flush items that are not represented in source + while (sline >= 0 && sline < curline) + { + item = src_items->fetch (sindex); + if (((DbeLine*) item->obj)->lineno > 0) + set_one (item, AT_QUOTE, item->obj->get_name ()); + + if (++sindex < src_items->size ()) // get next line with metrics + sline = ((DbeLine*) src_items->fetch (sindex)->obj)->lineno; + else + sline = -1; + } + + // write values in the metric fields for the given source line + if (curline == sline) + { // got metrics for this line + item = src_items->fetch (sindex); + if (((DbeLine*) item->obj)->lineno > 0) + set_one (item, AT_SRC, srcContext->getLine (curline)); + + if (++sindex < src_items->size ()) // get next line metric index + sline = ((DbeLine*) src_items->fetch (sindex)->obj)->lineno; + else + sline = -1; + } + else + { + item = data_items->new_hist_item (dbeline, type, empty); + if (size_index != -1) + item->value[size_index].ll = dbeline->get_size (); + if (addr_index != -1) + item->value[addr_index].ll = dbeline->get_addr (); + item->value[name_idx].l = dbe_strdup (srcContext->getLine (curline)); + data_items->append_hist_item (item); + } +} + +void +Module::set_dis (DbeInstr *instr, Anno_Types type, bool nextFile, char *dis_str) +{ + // Flush items that are not represented in disassembly + while (daddr && daddr->pc_cmp (instr) < 0) + { + if (!nextFile) + set_one (dis_items->fetch (dindex), AT_QUOTE, daddr->get_name ()); + if (++dindex < dis_items->size ()) // get next line metric index + daddr = (DbeInstr*) dis_items->fetch (dindex)->obj; + else + daddr = NULL; + } + + // Write values in the metric fields for the given pc index value + if (instr->inlinedInd >= 0) + { + StringBuilder sb; + sb.append (dis_str); + instr->add_inlined_info (&sb); + free (dis_str); + dis_str = sb.toString (); + } + if (daddr && daddr->pc_cmp (instr) == 0) + { + Hist_data::HistItem *item = data_items->new_hist_item (instr, type, + dis_items->fetch (dindex)->value); + item->value[name_idx].tag = VT_LABEL; + item->value[name_idx].l = dis_str; + data_items->append_hist_item (item); + if (dis_items->get_callsite_mark ()->get (dis_items->fetch (dindex)->obj)) + data_items->get_callsite_mark ()->put (item->obj, 1); + + if (++dindex < dis_items->size ()) // get next line metric index + daddr = (DbeInstr*) dis_items->fetch (dindex)->obj; + else + daddr = NULL; + } + else + { + // create a new item for this PC + Hist_data::HistItem *item = dis_items->new_hist_item (instr, type, empty); + if (size_index != -1) + item->value[size_index].ll = instr->size; + if (addr_index != -1) + item->value[addr_index].ll = instr->get_addr (); + item->value[name_idx].tag = VT_LABEL; + item->value[name_idx].l = dis_str; + data_items->append_hist_item (item); + } +} + +void +Module::set_MPSlave () +{ + Hist_data::HistItem *item; + Function *fp; + int index; + + // write the inclusive metrics for slave threads + while (mline == curline) + { + item = dis_items->fetch (mindex); + DbeInstr *instr = (DbeInstr *) item->obj; + Vec_loop (Function*, functions, index, fp) + { + if (fp->derivedNode == instr) + { + set_one (item, AT_QUOTE, (fp->isOutlineFunction) ? + GTXT ("<inclusive metrics for outlined functions>") : + GTXT ("<inclusive metrics for slave threads>")); + break; + } + } + + mindex++; + if (mindex < dis_items->size ()) + mline = (unsigned) ((DbeInstr*) (dis_items->fetch (mindex)->obj))->addr; + else + mline = -1; + } +}//set_MPSlave + +void +Module::set_one (Hist_data::HistItem *org_item, Anno_Types type, + const char *text) +{ + if (org_item == NULL) + return; + Hist_data::HistItem *item = data_items->new_hist_item (org_item->obj, type, + org_item->value); + item->value[name_idx].tag = VT_LABEL; + item->value[name_idx].l = dbe_strdup (text); + data_items->append_hist_item (item); + if (org_item != NULL && src_items != NULL + && src_items->get_callsite_mark ()->get (org_item->obj)) + data_items->get_callsite_mark ()->put (item->obj, 1); +}//set_one + +void +Module::set_ComCom (int vis_bits) +{ + Hist_data::HistItem *item; + Function *func = dbeSession->get_Unknown_Function (); + + if (vis_bits) + { + // precede the compiler commentary with a blank line + item = data_items->new_hist_item (func, AT_EMPTY, empty); + item->value[name_idx].l = dbe_strdup (NTXT ("")); + data_items->append_hist_item (item); + } + while (cline == curline) + { + ComC *comm = comComs->fetch (cindex); + if (comm->visible & vis_bits) + { + // write the compiler commentary + item = data_items->new_hist_item (func, AT_COM, empty); + item->value[name_idx].l = dbe_strdup (comm->com_str); + data_items->append_hist_item (item); + } + if (++cindex < comComs->size ()) + cline = comComs->fetch (cindex)->line; + else + cline = -1; + } +} + +void +Module::dump_dataobjects (FILE *out) +{ + int index; + datatype_t *dtype; + Vec_loop (datatype_t*, datatypes, index, dtype) + { + fprintf (out, NTXT ("[0x%08X,%6lld] %4d %6d %s "), dtype->datatype_id, + dtype->dobj ? dtype->dobj->id : 0LL, + dtype->memop_refs, dtype->event_data, + (dtype->dobj != NULL ? (dtype->dobj->get_name () ? + dtype->dobj->get_name () : "<NULL>") : "<no object>")); +#if DEBUG + Histable* scope = dtype->dobj ? dtype->dobj->get_scope () : NULL; + if (scope != NULL) + { + switch (scope->get_type ()) + { + case Histable::LOADOBJECT: + case Histable::FUNCTION: + fprintf (out, NTXT ("%s"), scope->get_name ()); + break; + case Histable::MODULE: + { + char *filename = get_basename (scope->get_name ()); + fprintf (out, NTXT ("%s"), filename); + break; + } + default: + fprintf (out, NTXT ("\tUnexpected scope %d:%s"), + scope->get_type (), scope->get_name ()); + } + } +#endif + fprintf (out, NTXT ("\n")); + } +} + +void +Module::set_name (char *str) +{ + free (name); + name = str; +} + +void +Module::read_hwcprof_info () +{ + if (hwcprof == 0) + { + hwcprof = 1; + Stabs *stabs = openDebugInfo (); + if (stabs) + stabs->read_hwcprof_info (this); + } +} + +void +Module::reset_datatypes () +{ + for (int i = 0, sz = datatypes ? datatypes->size () : -1; i < sz; i++) + { + datatype_t *t = datatypes->fetch (i); + t->event_data = 0; + } +} + +DataObject * +Module::get_dobj (uint32_t dtype_id) +{ + read_hwcprof_info (); + for (int i = 0, sz = datatypes ? datatypes->size () : -1; i < sz; i++) + { + datatype_t *t = datatypes->fetch (i); + if (t->datatype_id == dtype_id) + { + t->event_data++; + return t->dobj; + } + } + return NULL; +} + +int +Module::readFile () +{ + return AE_OK; +} + +Vector<Histable*> * +Module::get_comparable_objs () +{ + update_comparable_objs (); + if (comparable_objs || dbeSession->expGroups->size () <= 1 || loadobject == NULL) + return comparable_objs; + Vector<Histable*> *comparableLoadObjs = loadobject->get_comparable_objs (); + if (comparableLoadObjs == NULL) + return NULL; + comparable_objs = new Vector<Histable*>(comparableLoadObjs->size ()); + for (int i = 0, sz = comparableLoadObjs->size (); i < sz; i++) + { + Module *mod = NULL; + LoadObject *lo = (LoadObject*) comparableLoadObjs->fetch (i); + if (lo) + { + mod = lo->get_comparable_Module (this); + if (mod) + mod->comparable_objs = comparable_objs; + } + comparable_objs->store (i, mod); + } + dump_comparable_objs (); + return comparable_objs; +} + +JMethod * +Module::find_jmethod (const char *nm, const char *sig) +{ + // Vladimir: Probably we should not use linear search + for (long i = 0, sz = VecSize (functions); i < sz; i++) + { + JMethod *jmthd = (JMethod*) functions->get (i); + char *jmt_name = jmthd->get_name (Histable::SHORT); + if (strcmp (jmt_name, nm) == 0 + && strcmp (jmthd->get_signature (), sig) == 0) + return jmthd; + } + return NULL; +} diff --git a/gprofng/src/Module.h b/gprofng/src/Module.h new file mode 100644 index 0000000..39c4322 --- /dev/null +++ b/gprofng/src/Module.h @@ -0,0 +1,284 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _MODULE_H +#define _MODULE_H + +// A Module object represents a .o file that was used to build up a segement. +// Its main function is to compute source and disassembly annotations +// Text reordering and/or function outlining imply that a module may +// not be contiguous. + +#include "Histable.h" +#include "Hist_data.h" + +#define MOD_FLAG_UNKNOWN 0x01 + +class LoadObject; +class MetricList; +class ComC; +class Disasm; +class Hist_data; +class Stabs; +class SourceFile; +class DataObject; +class JMethod; +template <class ITEM> class Vector; + +class InlinedSubr +{ +public: + InlinedSubr (); + DbeLine *dbeLine; + Function *func; + char *fname; + uint64_t low_pc; + uint64_t high_pc; + int level; + + bool + contains (InlinedSubr *p) + { + return low_pc <= p->low_pc && high_pc >= p->high_pc; + } + + bool + contains (uint64_t pc) + { + return low_pc <= pc && high_pc > pc; + } +}; + +class Module : public HistableFile +{ +public: + // Annotated Source or Disassembly + enum Anno_Errors + { + AE_OK, + AE_NOTREAD, + AE_NOSRC, + AE_NOOBJ, + AE_NOLOBJ, + AE_NOSTABS, + AE_NOSYMTAB, + AE_TIMESRC, + AE_TIMEDIS, + AE_TIMESTABS, + AE_TIMESTABS_DIFF, + AE_OTHER + }; + + // The following enums are duplicated in Java + enum Anno_Types + { + AT_LIST = 0, + AT_SRC, + AT_SRC_ONLY, + AT_DIS, + AT_COM, + AT_QUOTE, + AT_FUNC, + AT_EMPTY, + AT_DIS_ONLY + }; + + Module (); + virtual ~Module (); + virtual int64_t get_size (); + virtual void set_name (char *str); + virtual Vector<Histable*> *get_comparable_objs (); + virtual int readFile (); + + virtual Histable_type + get_type () + { + return MODULE; + } + + inline Anno_Errors + get_status () + { + return status; + } + + inline void + set_file_name (char *fnm) + { + free (file_name); + file_name = fnm; + } + + // get error string + char *anno_str (char *fnm = NULL); + + // generate annotated source/disassembly data + Hist_data *get_data (DbeView *dbev, MetricList *mlist, + Histable::Type type, TValue *ftotal, SourceFile *srcFile, + Function *func, Vector<int> *marks, int threshold, + int vis_bits, int src_visible, bool hex_visible, + bool func_scope, bool src_only, + Vector<int_pair_t> *marks2d = NULL, + Vector<int_pair_t> *marks2d_inc = NULL); + + Vector<uint64_t> *getAddrs (Function *func); + SourceFile *setIncludeFile (char *includeFile); + + SourceFile * + getIncludeFile () + { + return curr_inc; + } + + SourceFile * + getMainSrc () + { + return main_source; + } + + char * + getResolvedObjectPath () + { + return stabsPath ? stabsPath : get_name (); + } + + char * + getDebugPath () + { + setFile (); + return stabsPath; + } + + void read_stabs (bool all = true); + void dump_dataobjects (FILE *out); + DataObject *get_dobj (uint32_t dtype_id); + void reset_datatypes (); + void read_hwcprof_info (); + bool is_fortran (); + SourceFile *findSource (const char *fname, bool create); + bool openStabs (bool all = true); + LoadObject *createLoadObject (const char *lo_name); + JMethod *find_jmethod (const char *nm, const char *sig); + + unsigned int flags; // flags used for marking traversals + Sp_lang_code lang_code; // What is source lang. in module + char *file_name; // Full path to actual source file + Vector<Function*> *functions; // Unordered list of functions + LoadObject *loadobject; // Parent loadobject + LoadObject *dot_o_file; // The .o file with debug information + unsigned fragmented; // -xF used when compiling module + int real_timestamp; // Linked timestamp from N_OPT stab + int curr_timestamp; // Current object timestamp from N_OPT stab + char *comp_flags; // compiler flags used to compile module + char *comp_dir; // directory used to compile module + char *linkerStabName; // Name from 'N_UNDF' stab + Stabs *objStabs; // stabs of object file + bool readStabs; + bool hasStabs; + bool hasDwarf; + uint64_t hdrOffset; // offset in .debug_info + unsigned hwcprof; // hwcprof info status + Vector<inst_info_t*> *infoList; // merged list + Vector<memop_info_t*> ldMemops; // load instructions + Vector<memop_info_t*> stMemops; // store instructions + Vector<memop_info_t*> pfMemops; // prefetch instructions + Vector<target_info_t*> bTargets; // branch targets + Vector<datatype_t*> *datatypes; // object type descriptors + Vector<SourceFile*> *includes; + Module *indexStabsLink; // correspondent module for the .o file + InlinedSubr *inlinedSubr; + +protected: + void removeStabsTmp (); // Remove temporary *.o (got from *.a) + + // Check timestamp, warn users if src/dis/stabs later than exp. + Anno_Errors checkTimeStamp (bool chkDis); + + // Set paths for reading Stabs and Symbols + bool read_ar (int ar, int obj, char *obj_base); + bool setFile (); + + // Open appropriate symbol tables, construct set of PC ranges, + // and maps to source lines for each PC + Stabs *openDebugInfo (); + + // Construct PC index table + bool openDisPC (); + + // Compute data--scan data to compute metrics per-src-line/dis-line + bool computeMetrics (DbeView *dbev, Function *func, MetricList *mlist, + Histable::Type type, bool src_metric, + bool func_scope, SourceFile *source); + void init_line (); + void init_index (Hist_data *witems, int &wlindex, int &wmsize, int &wmindex); + + void set_src_data (Function *func, int vis_bits, int cmpline_visible, + int funcline_visible); + void set_dis_data (Function *func, int vis_bits, int cmpline_visible, + int src_visible, bool hex_vis, bool func_scope, + int funcline_visible); + void set_src (Anno_Types type, DbeLine *dbeline); + void set_dis (DbeInstr *instr, Anno_Types type, bool nextFile, char *dis_str); + void set_MPSlave (); + void set_one (Hist_data::HistItem *org_item, Anno_Types type, const char *text); + void set_ComCom (int vis_bits); + + virtual char *get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, + int64_t &inst_size); + + Anno_Errors status; + Anno_Errors openSourceFlag; + bool hexVisible; // show hex code in disasm + time_t disMTime; // Creating time for disassembly + time_t stabsMTime; // Creating time for stabs + SourceFile *main_source; + SourceFile *curr_inc; // pointer to include file or NULL + SourceFile *srcContext; + Vector<ComC*> *comComs; // table of compiler comments + Disasm *disasm; + Hist_data *src_items; + Hist_data *dis_items; + Hist_data *data_items; + DbeView * cur_dbev; + TValue *total; + TValue *maximum; + TValue *maximum_inc; + TValue *empty; + int name_idx; // index of name metric in list for src/dis + int size_index; // index of size metric in list for src/dis + int addr_index; // index of address metric in list for src/dis + + int curline; // line# of next source line to be processed + int cindex, cline; // index and src line of next compiler-comment + int sindex, sline; // index and src line of next item in src_items + int dindex; + DbeInstr *daddr; // pointer to next DbeInstr with metrics + int mindex; // MP index and src line of next metric-value + int mline; // MP line to be processed by source + + char *disPath; // path for disassembly + char *stabsPath; // path for reading stabs + char *stabsTmp; // temporary *.o from *.a + char *disName; // library/path for disassembly + char *stabsName; // library/path for stabs +}; + +#endif /* _MODULE_H */ diff --git a/gprofng/src/Ovw_data.cc b/gprofng/src/Ovw_data.cc new file mode 100644 index 0000000..2cd5718 --- /dev/null +++ b/gprofng/src/Ovw_data.cc @@ -0,0 +1,242 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <memory.h> +#include <values.h> +#include <assert.h> +#include "Data_window.h" +#include "Exp_Layout.h" +#include "Table.h" +#include "Ovw_data.h" +#include "Sample.h" +#include "data_pckts.h" +#include "util.h" +#include "i18n.h" + +void +Ovw_data::sum (Ovw_data *data) +{ + Ovw_item data_totals = data->get_totals (); + if (totals == NULL) + { + totals = reset_item (new Ovw_item); + *totals = data_totals; + totals->start.tv_sec = totals->end.tv_sec = -1; + totals->start.tv_nsec = totals->end.tv_nsec = 0; + } + else + { + tsadd (&totals->duration, &data_totals.duration); + tsadd (&totals->tlwp, &data_totals.tlwp); + if (tstodouble (totals->duration) != 0) + totals->nlwp = tstodouble (totals->tlwp) / tstodouble (totals->duration); + + for (int i = 0, size = totals->size; i < size; i++) + tsadd (&totals->values[i].t, &data_totals.values[i].t); + } +} + +Ovw_data::Ovw_item * +Ovw_data::reset_item (Ovw_data::Ovw_item *item) +{ + memset (item, 0, sizeof (*item)); + return item; +} + +Ovw_data::Ovw_item +Ovw_data::get_totals () +{ + // This routine will return the totals values for item in the sample. + // Compute maximums and totals only once, and save the result. + // On subsequent calls, just return the saved result. + // If maximums is NULL, then totals is also NULL + if (totals != NULL) + return *totals; + + timestruc_t zero = {0, 0}; + totals = reset_item (new Ovw_item); + totals->start.tv_sec = MAXINT; // new + totals->start.tv_nsec = MAXINT; // new + totals->start_label = totals->end_label = NTXT ("Total"); + totals->type = VT_HRTIME; + + int nsampsel = 0; + for (int index = 0; index < size (); index++) + { + Ovw_item item = fetch (index); + nsampsel++; + + // Compute totals + for (int i = 0; i < OVW_NUMVALS + 1; i++) + tsadd (&totals->values[i].t, &item.values[i].t); + + int_max (&totals->states, item.states); + tsadd (&totals->total.t, &item.total.t); + int_max (&totals->size, item.size); + tsadd (&totals->duration, &item.duration); + tsadd (&totals->tlwp, &item.tlwp); + totals->number += item.number; + if (tscmp (&totals->start, &item.start) > 0) + totals->start = item.start; + if (tscmp (&totals->end, &item.end) < 0) + totals->end = item.end; + } + + if (totals->start.tv_sec == MAXINT && totals->start.tv_nsec == MAXINT) + totals->start = zero; + totals->nlwp = tstodouble (totals->tlwp) / tstodouble (totals->duration); + + if (nsampsel == 0) + { + totals->size = OVW_NUMVALS + 1; + totals->start.tv_sec = totals->end.tv_sec = -1; + totals->start.tv_nsec = totals->end.tv_nsec = 0; + totals->nlwp = -1; + } + return *totals; +} + +Ovw_data::Ovw_item +Ovw_data::get_labels () +{ + Ovw_item ovw_item; + Value *values; + memset (&ovw_item, 0, sizeof (Ovw_item)); + values = &ovw_item.values[0]; + + char *stateUNames[/*LMS_NUM_STATES*/] = LMS_STATE_USTRINGS; + values[0].l = dbe_strdup (GTXT ("Leftover")); + values[OVW_LMS_USER + 1].l = stateUNames[LMS_USER]; + values[OVW_LMS_SYSTEM + 1].l = stateUNames[LMS_SYSTEM]; + values[OVW_LMS_WAIT_CPU + 1].l = stateUNames[LMS_WAIT_CPU]; + values[OVW_LMS_USER_LOCK + 1].l = stateUNames[LMS_USER_LOCK]; + values[OVW_LMS_TFAULT + 1].l = stateUNames[LMS_TFAULT]; + values[OVW_LMS_DFAULT + 1].l = stateUNames[LMS_DFAULT]; + values[OVW_LMS_KFAULT + 1].l = stateUNames[LMS_KFAULT]; + values[OVW_LMS_SLEEP + 1].l = stateUNames[LMS_SLEEP]; + values[OVW_LMS_STOPPED + 1].l = stateUNames[LMS_STOPPED]; + values[OVW_LMS_TRAP + 1].l = stateUNames[LMS_TRAP]; + + ovw_item.size = OVW_NUMVALS + 1; + ovw_item.states = 0; + ovw_item.type = VT_LABEL; + return ovw_item; +} + +Ovw_data::Ovw_data () +{ + packets = NULL; + ovw_items = new Vector<Ovw_item*>; + totals = NULL; +} + +Ovw_data::Ovw_data (DataView *_packets, hrtime_t exp_start) +{ + packets = _packets; + ovw_items = new Vector<Ovw_item*>; + totals = NULL; + long npackets = packets->getSize (); + for (long index = 0; index < npackets; index++) + { + Ovw_item *ovw_item = new Ovw_item; + memset (ovw_item, 0, sizeof (Ovw_item)); + Sample *sample = (Sample*) packets->getObjValue (PROP_SMPLOBJ, index); + extract_data (ovw_item, sample); + hr2timestruc (&ovw_item->start, sample->get_start_time () - exp_start); + hr2timestruc (&ovw_item->end, sample->get_end_time () - exp_start); + // No need to check for duration, as duration has to be > 0. + // If not, it would have been found out in yyparse. + tssub (&ovw_item->duration, &ovw_item->end, &ovw_item->start); + ovw_item->number = sample->get_number (); + ovw_item->start_label = sample->get_start_label (); + ovw_item->end_label = sample->get_end_label (); + + int size = ovw_item->size; + for (int j = 0; j < size; j++) + tsadd (&ovw_item->tlwp, &ovw_item->values[j].t); + if (tstodouble (ovw_item->duration) != 0) + ovw_item->nlwp = tstodouble (ovw_item->tlwp) / + tstodouble (ovw_item->duration); + ovw_items->append (ovw_item); + } +} + +Ovw_data::~Ovw_data () +{ + ovw_items->destroy (); + delete ovw_items; + delete totals; +} + +void +Ovw_data::extract_data (Ovw_data::Ovw_item *ovw_item, Sample *sample) +{ + // This routine break out the data in "data" into buckets in "ovw_item" + int index; + int states; + timestruc_t sum, rtime; + timestruc_t zero = {0, 0}; + Value *values; + PrUsage *prusage = sample->get_usage (); + if (prusage == NULL) + prusage = new PrUsage; + + values = &ovw_item->values[0]; + hr2timestruc (&values[OVW_LMS_USER + 1].t, prusage->pr_utime); + hr2timestruc (&values[OVW_LMS_SYSTEM + 1].t, prusage->pr_stime); + hr2timestruc (&values[OVW_LMS_WAIT_CPU + 1].t, prusage->pr_wtime); + hr2timestruc (&values[OVW_LMS_USER_LOCK + 1].t, prusage->pr_ltime); + hr2timestruc (&values[OVW_LMS_TFAULT + 1].t, prusage->pr_tftime); + hr2timestruc (&values[OVW_LMS_DFAULT + 1].t, prusage->pr_dftime); + hr2timestruc (&values[OVW_LMS_TRAP + 1].t, prusage->pr_ttime); + hr2timestruc (&values[OVW_LMS_KFAULT + 1].t, prusage->pr_kftime); + hr2timestruc (&values[OVW_LMS_SLEEP + 1].t, prusage->pr_slptime); + hr2timestruc (&values[OVW_LMS_STOPPED + 1].t, prusage->pr_stoptime); + ovw_item->size = OVW_NUMVALS + 1; + + //XXX: Compute values[0] as rtime - sum_of(other_times) + sum = zero; + states = 0; + for (index = 1; index < ovw_item->size; index++) + { + if (values[index].t.tv_sec != 0 || values[index].t.tv_nsec != 0) + states++; + tsadd (&sum, &values[index].t); + } + + // If the sum of all times is greater than rtime then adjust + // rtime to be equal to sum and also adjust the pr_rtime field + hr2timestruc (&rtime, prusage->pr_rtime); + if (tscmp (&sum, &rtime) > 0) + { + ovw_item->total.t = sum; + values[0].t = zero; + } + else + { + ovw_item->total.t = rtime; + tssub (&rtime, &rtime, &sum); + tsadd (&values[0].t, &rtime); + states++; + } + ovw_item->type = VT_HRTIME; + ovw_item->states = states; +} diff --git a/gprofng/src/Ovw_data.h b/gprofng/src/Ovw_data.h new file mode 100644 index 0000000..0c3372c --- /dev/null +++ b/gprofng/src/Ovw_data.h @@ -0,0 +1,102 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _OVW_DATA_H +#define _OVW_DATA_H + +// An Ovw_data object is used to supply data for constructing an overview +// display. + +#include "dbe_structs.h" + +class Sample; +class DataView; + +class Ovw_data +{ +public: + + enum OVW_LMS_STORAGE + {// in display order, not LMS_* order + // Note: use same display order of LMS_* in: er.rc, TimelineVariable.java, + // Ovw_data.h, BaseMetricTreeNode.cc and Experiment.cc metric registration + OVW_LMS_USER, + OVW_LMS_SYSTEM, + OVW_LMS_TRAP, + OVW_LMS_USER_LOCK, + OVW_LMS_DFAULT, + OVW_LMS_TFAULT, + OVW_LMS_KFAULT, + OVW_LMS_STOPPED, + OVW_LMS_WAIT_CPU, + OVW_LMS_SLEEP, + OVW_NUMVALS // must be last + }; + + // Ovw_item contains one slice of data + struct Ovw_item + { + Value values [OVW_NUMVALS + 1]; // Value list (value[0] is left over) + int states; // Number of non-zero states + Value total; // Total of all values + int size; // Number of values + timestruc_t start; // Start time of sample + timestruc_t duration; // Duration of sample + timestruc_t end; // End time of sample + timestruc_t tlwp; // Total LWP time + double nlwp; // Average number of LWPs + ValueTag type; // Type of value + int number; // Sample number + char *start_label; // Sample start label + char *end_label; // Sample end label + }; + + Ovw_data (DataView *, hrtime_t exp_start); + Ovw_data (); + ~Ovw_data (); + void sum (Ovw_data *data); + Ovw_item get_totals (); + Ovw_item get_labels (); + + // zero out contents of Ovw_item + static Ovw_item *reset_item (Ovw_item *item); + + int + size () + { + return ovw_items->size (); + } + + Ovw_item + fetch (int index) + { + return *ovw_items->fetch (index); + } + +private: + // Compute the values for "ovw_item" from "sample". + void extract_data (Ovw_item *ovw_item, Sample *sample); + + Vector<Ovw_item*> *ovw_items; + Ovw_item *totals; // Item to cache totals + DataView *packets; +}; + +#endif /* _OVW_DATA_H */ diff --git a/gprofng/src/PRBTree.cc b/gprofng/src/PRBTree.cc new file mode 100644 index 0000000..5046a91 --- /dev/null +++ b/gprofng/src/PRBTree.cc @@ -0,0 +1,480 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +// The Persistent Red-Black Tree +// +// Implementation is based on an algorithm described in +// Sarnak, N., Tarjan, R., "Planar point location using +// persistent search trees", in Communications of the ACM, +// 1986, Vol.29, Number 7. +// + +#include "config.h" +#include <memory.h> +#include <string.h> + +#include "vec.h" +#include "PRBTree.h" + +#define ASSERT(x) + +#define IS_BLACK(x) ((x)==NULL || (x)->color == Black) +#define IS_RED(x) ((x)!=NULL && (x)->color == Red) +#define SET_BLACK(x) if(x) x->color = Black +#define SET_RED(x) (x)->color = Red + +#define D_OPPOSITE(x) (((x)==Left) ? Right : Left ) + +PRBTree::LMap::LMap (Key_t _key, void *_item) +{ + key = _key; + item = _item; + color = Red; + parent = NULL; + for (int i = 0; i < NPTRS; i++) + { + dir[i] = None; + chld[i] = NULL; + time[i] = 0; + } +}; + +PRBTree::LMap::LMap (const LMap& lm) +{ + key = lm.key; + item = lm.item; + color = lm.color; + parent = lm.parent; + for (int i = 0; i < NPTRS; i++) + { + dir[i] = None; + chld[i] = NULL; + time[i] = 0; + } +}; + +PRBTree::PRBTree () +{ + roots = new Vector<LMap*>; + root = NULL; + times = new Vector<Time_t>; + rtts = (Time_t) 0; + curts = (Time_t) 0; + mlist = NULL; + vals = NULL; +} + +PRBTree::~PRBTree () +{ + while (mlist) + { + LMap *lm = mlist; + mlist = mlist->next; + delete lm; + } + delete times; + delete roots; + delete vals; +} + +Vector<void *> * +PRBTree::values () +{ + if (vals == NULL) + { + vals = new Vector<void*>; + for (LMap *lm = mlist; lm; lm = lm->next) + vals->append (lm->item); + } + return vals; +} + +PRBTree::LMap * +PRBTree::rb_new_node (Key_t key, void *item) +{ + LMap *lm = new LMap (key, item); + lm->next = mlist; + mlist = lm; + return lm; +} + +PRBTree::LMap * +PRBTree::rb_new_node (LMap *lm) +{ + LMap *lmnew = new LMap (*lm); + lmnew->next = mlist; + mlist = lmnew; + return lmnew; +} + +PRBTree::LMap * +PRBTree::rb_child (LMap *lm, Direction d, Time_t ts) +{ + if (lm == NULL) + return NULL; + for (int i = 0; i < NPTRS; i++) + { + if (lm->time[i] > ts) + continue; + if (lm->dir[i] == d) + return lm->chld[i]; + else if (lm->dir[i] == None) + break; + } + return NULL; +} + +PRBTree::Direction +PRBTree::rb_which_chld (LMap *lm) +{ + LMap *prnt = lm->parent; + if (prnt == NULL) + return None; + for (int i = 0; i < NPTRS; i++) + { + if (prnt->dir[i] == None) + break; + if (prnt->chld[i] == lm) + return (Direction) prnt->dir[i]; + } + return None; +} + +PRBTree::LMap * +PRBTree::rb_neighbor (LMap *lm, Time_t ts) +{ + ASSERT (lm->dir[0] != None); + Direction d = D_OPPOSITE (lm->dir[0]); + LMap *y = NULL; + LMap *next = lm->chld[0]; + while (next) + { + y = next; + next = rb_child (y, d, ts); + } + return y; +} + +PRBTree::LMap * +PRBTree::rb_copy_node (LMap *lm, Direction d) +{ + LMap *nlm = rb_new_node (lm); + rb_fix_chld (lm->parent, nlm, rb_which_chld (lm)); + if (d == None) + { + d = Left; + rb_fix_chld (nlm, rb_child (lm, d, curts), d); + } + + // copy the other child + Direction dd = D_OPPOSITE (d); + rb_fix_chld (nlm, rb_child (lm, dd, curts), dd); + return nlm; +} + +PRBTree::LMap * +PRBTree::rb_fix_chld (LMap *prnt, LMap *lm, Direction d) +{ + + if (prnt == NULL) + { + // fixing root + ASSERT (d == None); + if (rtts == curts) + root = lm; + else + { + roots->append (root); + times->append (rtts); + root = lm; + rtts = curts; + } + if (lm != NULL) + lm->parent = prnt; + return prnt; + } + + // If we already have a d-pointer at time curts, reuse it + for (int i = 0; prnt->time[i] == curts; i++) + { + if (prnt->dir[i] == d) + { + prnt->chld[i] = lm; + if (lm != NULL) + lm->parent = prnt; + return prnt; + } + } + + if (prnt->dir[NPTRS - 1] != None) + prnt = rb_copy_node (prnt, d); + ASSERT (prnt->dir[NPTRS - 1] == None); + + for (int i = NPTRS - 1; i > 0; i--) + { + prnt->dir[i] = prnt->dir[i - 1]; + prnt->chld[i] = prnt->chld[i - 1]; + prnt->time[i] = prnt->time[i - 1]; + } + prnt->dir[0] = d; + prnt->chld[0] = lm; + prnt->time[0] = curts; + if (lm != NULL) + lm->parent = prnt; + return prnt; +} + +PRBTree::LMap * +PRBTree::rb_rotate (LMap *x, Direction d) +{ + Direction dd = D_OPPOSITE (d); + LMap *y = rb_child (x, dd, curts); + x = rb_fix_chld (x, rb_child (y, d, curts), dd); + rb_fix_chld (x->parent, y, rb_which_chld (x)); + rb_fix_chld (y, x, d); + return x; +} + +void +PRBTree::rb_remove_fixup (LMap *x, LMap *prnt, Direction d0) +{ + + while (IS_BLACK (x) && (x != root)) + { + Direction d = (x == NULL) ? d0 : rb_which_chld (x); + Direction dd = D_OPPOSITE (d); + LMap *y = rb_child (prnt, dd, curts); + if (IS_RED (y)) + { + SET_BLACK (y); + SET_RED (prnt); + prnt = rb_rotate (prnt, d); + y = rb_child (prnt, dd, curts); + } + LMap *y_d = rb_child (y, d, curts); + LMap *y_dd = rb_child (y, dd, curts); + if (IS_BLACK (y_d) && IS_BLACK (y_dd)) + { + SET_RED (y); + x = prnt; + prnt = x->parent; + } + else + { + if (IS_BLACK (y_dd)) + { + SET_BLACK (y_d); + SET_RED (y); + y = rb_rotate (y, dd); + prnt = y->parent->parent; + y = rb_child (prnt, dd, curts); + y_dd = rb_child (y, dd, curts); + } + y->color = prnt->color; + SET_BLACK (prnt); + SET_BLACK (y_dd); + prnt = rb_rotate (prnt, d); + break; + } + } + SET_BLACK (x); +} + +PRBTree::LMap * +PRBTree::rb_locate (Key_t key, Time_t ts, bool low) +{ + LMap *lm; + Direction d; + int i, lt, rt; + int tsz = times->size (); + + if (ts >= rtts) + lm = root; + else + { + // exponential search + for (i = 1; i <= tsz; i = i * 2) + if (times->fetch (tsz - i) <= ts) + break; + + if (i <= tsz) + { + lt = tsz - i; + rt = tsz - i / 2 - 1; + } + else + { + lt = 0; + rt = tsz - 1; + } + while (lt <= rt) + { + int md = (lt + rt) / 2; + if (times->fetch (md) <= ts) + lt = md + 1; + else + rt = md - 1; + } + if (rt < 0) + return NULL; + lm = roots->fetch (rt); + } + + LMap *last_lo = NULL; + LMap *last_hi = NULL; + while (lm != NULL) + { + if (key >= lm->key) + { + last_lo = lm; + d = Right; + } + else + { + last_hi = lm; + d = Left; + } + lm = rb_child (lm, d, ts); + } + return low ? last_lo : last_hi; +} + +//==================================================== Public interface + +bool +PRBTree::insert (Key_t key, Time_t ts, void *item) +{ + LMap *lm, *y; + Direction d, dd; + if (ts > curts) + curts = ts; + else if (ts < curts) + return false; // can only update the current tree + + // Insert in the tree in the usual way + y = NULL; + d = None; + for (LMap *next = root; next;) + { + y = next; + if (key == y->key) + { + // copy the node with both children + lm = rb_copy_node (y, None); + // but use the new item + lm->item = item; + return true; + } + d = (key < y->key) ? Left : Right; + next = rb_child (y, d, curts); + } + lm = rb_new_node (key, item); + rb_fix_chld (y, lm, d); + + // Rebalance the tree + while (IS_RED (lm->parent)) + { + d = rb_which_chld (lm->parent); + dd = D_OPPOSITE (d); + + y = rb_child (lm->parent->parent, dd, curts); + if (IS_RED (y)) + { + SET_BLACK (lm->parent); + SET_BLACK (y); + SET_RED (lm->parent->parent); + lm = lm->parent->parent; + } + else + { + if (rb_which_chld (lm) == dd) + { + lm = lm->parent; + lm = rb_rotate (lm, d); + } + SET_BLACK (lm->parent); + SET_RED (lm->parent->parent); + rb_rotate (lm->parent->parent, dd); + } + } + + // Color the root Black + SET_BLACK (root); + return true; +} + +bool +PRBTree::remove (Key_t key, Time_t ts) +{ + LMap *lm, *x, *y, *prnt; + if (ts > curts) + curts = ts; + else if (ts < curts) + return false; // can only update the current tree + + lm = rb_locate (key, curts, true); + if (lm == NULL || lm->key != key) + return false; + + if (rb_child (lm, Left, curts) && rb_child (lm, Right, curts)) + y = rb_neighbor (lm, curts); + else + y = lm; + + x = rb_child (y, Left, curts); + if (x == NULL) + x = rb_child (y, Right, curts); + + if (y != lm) + { + lm = rb_copy_node (lm, None); // copied with children + lm->key = y->key; + lm->item = y->item; + } + + Direction d = rb_which_chld (y); + prnt = rb_fix_chld (y->parent, x, d); + if (IS_BLACK (y)) + rb_remove_fixup (x, prnt, d); + return true; +} + +void * +PRBTree::locate (Key_t key, Time_t ts) +{ + LMap *lm = rb_locate (key, ts, true); + return lm ? lm->item : NULL; +} + +void * +PRBTree::locate_up (Key_t key, Time_t ts) +{ + LMap *lm = rb_locate (key, ts, false); + return lm ? lm->item : NULL; +} + +void * +PRBTree::locate_exact_match (Key_t key, Time_t ts) +{ + LMap *lm = rb_locate (key, ts, true); + if (lm && key == lm->key) + return lm->item; + return NULL; +} diff --git a/gprofng/src/PRBTree.h b/gprofng/src/PRBTree.h new file mode 100644 index 0000000..1a6f07f --- /dev/null +++ b/gprofng/src/PRBTree.h @@ -0,0 +1,106 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +// +// The Persistent Red-Black Tree +// + +#ifndef _PRBTREE_H +#define _PRBTREE_H + +#include "dbe_types.h" +template <class ITEM> class Vector; + +// The number of pointers in a node must be set greater than 2. +// The higher the number the faster the search seems to be and +// the more memory the tree takes. +#define NPTRS 5 + +class PRBTree +{ +public: + + typedef Vaddr Key_t; + typedef hrtime_t Time_t; + + PRBTree (); + ~PRBTree (); + + bool insert (Key_t key, Time_t ts, void *item); + bool remove (Key_t key, Time_t ts); + void *locate (Key_t key, Time_t ts); + void *locate_exact_match (Key_t key, Time_t ts); + void *locate_up (Key_t key, Time_t ts); + Vector<void *> *values (); + +private: + + enum Color + { + Red, + Black + }; + + enum Direction + { + None, + Left, + Right + }; + + struct LMap + { + Key_t key; + void *item; + LMap *parent; + LMap *chld[NPTRS]; + Time_t time[NPTRS]; + char dir[NPTRS]; + char color; + LMap *next; + + LMap (Key_t _key, void *_item); + LMap (const LMap& lm); + }; + friend struct LMap; + + LMap *mlist; // The master list of all nodes + Vector<LMap*> *roots; + Vector<Time_t> *times; + Vector<void *> *vals; + LMap *root; + Time_t rtts; // root timestamp + Time_t curts; // last update timestamp + + LMap *rb_locate (Key_t key, Time_t ts, bool low); + LMap *rb_new_node (Key_t key, void *item); + LMap *rb_new_node (LMap *lm); + LMap *rb_copy_node (LMap *lm, Direction d); + LMap *rb_fix_chld (LMap *prnt, LMap *lm, Direction d); + LMap *rb_rotate (LMap *x, Direction d); + void rb_remove_fixup (LMap *x, LMap *prnt, Direction d0); + + static LMap *rb_child (LMap *lm, Direction d, Time_t ts); + static Direction rb_which_chld (LMap *lm); + static LMap *rb_neighbor (LMap *lm, Time_t ts); + +}; + +#endif /* _PRBTREE_H */ diff --git a/gprofng/src/PathTree.cc b/gprofng/src/PathTree.cc new file mode 100644 index 0000000..798e55c --- /dev/null +++ b/gprofng/src/PathTree.cc @@ -0,0 +1,2637 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> + +#include "util.h" +#include "DefaultMap.h" +#include "CacheMap.h" + +#include "DbeSession.h" +#include "Application.h" +#include "CallStack.h" +#include "Emsg.h" +#include "Experiment.h" +#include "Expression.h" +#include "Function.h" +#include "Histable.h" +#include "IndexObject.h" +#include "MetricList.h" +#include "Module.h" +#include "DbeView.h" +#include "Metric.h" +#include "PathTree.h" +#include "LoadObject.h" +#include "Sample.h" +#include "StringBuilder.h" +#include "Table.h" + +// Define counts, rate for error warnings for statistical profiles +#define MIN_PROF_CNT 100 +#define MAX_PROF_RATE 1000. + +#define NUM_DESCENDANTS(nd) ((nd)->descendants ? (nd)->descendants->size() : 0) +#define IS_LEAF(nd) ((nd)->descendants == NULL) + +#ifdef DEBUG +#define DBG(__func) __func +#else +#define DBG(__func) +#endif + +void +PathTree::construct (DbeView *_dbev, int _indxtype, PathTreeType _pathTreeType) +{ + dbev = _dbev; + indxtype = _indxtype; + pathTreeType = _pathTreeType; + status = 0; + nchunks = 0; + chunks = NULL; + nodes = 1; // don't use node 0 + nslots = 0; + slots = NULL; + root_idx = 0; + root = NULL; + depth = 1; + dnodes = 0; + phaseIdx = -1; + nexps = 0; + total_obj = NULL; + indx_expr = NULL; + statsq = NULL; + warningq = NULL; + cancel_ok = 1; + ptree_internal = NULL; + ftree_internal = NULL; + ftree_needs_update = false; + depth_map = NULL; + init (); +} + +PathTree::~PathTree () +{ + fini (); + for (long i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; +} + +void +PathTree::init () +{ + fn_map = new DefaultMap<Function*, NodeIdx>; + stack_prop = PROP_NONE; + desc_htable_size = 511; + desc_htable_nelem = 0; + descHT = new hash_node_t*[desc_htable_size]; + for (int i = 0; i < desc_htable_size; i++) + descHT[i] = NULL; + pathMap = new CacheMap<uint64_t, NodeIdx>; + statsq = new Emsgqueue (NTXT ("statsq")); + warningq = new Emsgqueue (NTXT ("warningq")); + if (indxtype < 0) + { + Function *ftotal = dbeSession->get_Total_Function (); + if (pathTreeType == PATHTREE_INTERNAL_FUNCTREE) + total_obj = ftotal; + else + total_obj = ftotal->find_dbeinstr (0, 0); + VMode view_mode = dbev->get_view_mode (); + if (view_mode == VMODE_MACHINE) + stack_prop = PROP_MSTACK; + else if (view_mode == VMODE_EXPERT) + stack_prop = PROP_XSTACK; + else if (view_mode == VMODE_USER) + { + stack_prop = PROP_USTACK; + if (dbeSession->is_omp_available () + && pathTreeType == PATHTREE_INTERNAL_OMP) + stack_prop = PROP_XSTACK; + } + } + else + { + total_obj = new IndexObject (indxtype, (uint64_t) - 2); + total_obj->set_name (dbe_strdup (NTXT ("<Total>"))); + char *idxname = dbeSession->getIndexSpaceName (indxtype); + if (streq (idxname, NTXT ("OMP_preg"))) + stack_prop = PROP_CPRID; + else if (streq (idxname, NTXT ("OMP_task"))) + stack_prop = PROP_TSKID; + else + indx_expr = dbeSession->getIndexSpaceExpr (indxtype); + } + root_idx = new_Node (0, total_obj, false); + root = NODE_IDX (root_idx); +} + +void +PathTree::fini () +{ + // For each node free its descendants vector + // and reset the node list of its function + for (long i = 1; i < nodes; i++) + { + Node *node = NODE_IDX (i); + if (node->descendants) + delete node->descendants; + } + nodes = 1; // don't use node 0 + + for (int i = 0; i < nslots; i++) + { + int **tmp = slots[i].mvals; + for (long j = 0; j < nchunks; j++) + delete[] tmp[j]; + delete[] tmp; + } + delete[] slots; + slots = NULL; + nslots = 0; + delete fn_map; + fn_map = NULL; + delete pathMap; + pathMap = NULL; + destroy (depth_map); + depth_map = NULL; + if (indxtype >= 0) + delete total_obj; + + for (int i = 0; i < desc_htable_size; i++) + { + hash_node_t *p = descHT[i]; + while (p) + { + hash_node_t *p1 = p; + p = p->next; + delete p1; + } + } + delete[] descHT; + delete statsq; + delete warningq; + depth = 1; + dnodes = 0; + phaseIdx = -1; + nexps = 0; + status = 0; +} + +PtreePhaseStatus +PathTree::reset () +{ + if (pathTreeType == PATHTREE_INTERNAL_FUNCTREE) + return NORMAL; // never process reset for ftree_internal. + + if (dbeSession->is_omp_available () && dbev->get_view_mode () == VMODE_USER + && pathTreeType == PATHTREE_MAIN && ptree_internal == NULL) + ptree_internal = new PathTree (dbev, indxtype, PATHTREE_INTERNAL_OMP); + + if (phaseIdx != dbev->getPhaseIdx ()) + { + fini (); + init (); + phaseIdx = dbev->getPhaseIdx (); + ftree_needs_update = true; + } + for (; nexps < dbeSession->nexps (); nexps++) + { + ftree_needs_update = true; + if (add_experiment (nexps) == CANCELED) + return CANCELED; + } + + // LIBRARY_VISIBILITY + if (dbev->isNewViewMode ()) + dbev->resetNewViewMode (); + if (dbev->isShowHideChanged ()) + dbev->resetShowHideChanged (); + return NORMAL; +} + +int +PathTree::allocate_slot (int id, ValueTag vtype) +{ + + int i; + int slot_idx = find_slot (id); + if (slot_idx >= 0) + { + DBG (assert (slots[slot_idx].vtype == vtype)); + return slot_idx; + } + slot_idx = nslots++; + + Slot *old_slots = slots; + slots = new Slot[nslots]; + for (i = 0; i < slot_idx; i++) + slots[i] = old_slots[i]; + delete[] old_slots; + + slots[slot_idx].id = id; + slots[slot_idx].vtype = vtype; + int **ip = new int*[nchunks]; + for (i = 0; i < nchunks; i++) + ip[i] = NULL; + slots[slot_idx].mvals = ip; + + return slot_idx; +} + +void +PathTree::allocate_slots (Slot *new_slots, int new_nslots) +{ + // duplicates new_slots + + // if previously had more slots than currently requested, delete the data from those slots. + for (int i = new_nslots; i < nslots; i++) + { + int **tmp = slots[i].mvals; + for (long j = 0; j < nchunks; j++) + delete tmp[j]; + delete tmp; + } + if (new_nslots == 0) + { + nslots = new_nslots; + delete[] slots; + slots = NULL; + return; + } + + Slot *old_slots = slots; + slots = new Slot[new_nslots]; + for (int i = 0; i < new_nslots; i++) + { + slots[i] = new_slots[i]; // pick up id and vtype + if (i < nslots) + slots[i].mvals = old_slots[i].mvals; + else + { + if (nchunks == 0) + slots[i].mvals = NULL; + else + { + int **ip = new int*[nchunks]; + for (long j = 0; j < nchunks; j++) + ip[j] = NULL; + slots[i].mvals = ip; + } + } + } + nslots = new_nslots; + delete old_slots; +} + +int +PathTree::find_slot (int id) +{ + for (int i = 0; i < nslots; i++) + if (slots[i].id == id) + return i; + return -1; +} + +PathTree::NodeIdx +PathTree::new_Node (NodeIdx anc, Histable *instr, bool leaf) +{ + if (nodes >= nchunks * CHUNKSZ) + { + long idx = nchunks++; + + // Reallocate Node chunk array + Node **old_chunks = chunks; + chunks = new Node*[nchunks]; + for (long k = 0; k < idx; k++) + chunks[k] = old_chunks[k]; + delete[] old_chunks; + + // Reallocate metric value chunk arrays. + for (int i = 0; i < nslots; i++) + { + int **mvals = new int*[nchunks]; + for (long k = 0; k < idx; k++) + { + mvals[k] = slots[i].mvals[k]; + } + delete[] slots[i].mvals; + slots[i].mvals = mvals; + slots[i].mvals[idx] = NULL; + } + + // Allocate new chunk for nodes. + // Note that we don't need to allocate new chunks + // for metric values at this point as we rely on + // lazy allocation. + // + allocate_chunk (chunks, idx); + } + NodeIdx node_idx = nodes++; + Node *node = NODE_IDX (node_idx); + node->ancestor = anc; + node->descendants = leaf ? (Vector<NodeIdx>*)NULL : new Vector<NodeIdx>(2); + node->instr = instr; + Function *func = (Function*) (instr->convertto (Histable::FUNCTION)); + node->funclist = fn_map->get (func); + fn_map->put (func, node_idx); + return node_idx; +} + +PathTree::NodeIdx +PathTree::find_path (Experiment *exp, DataView *dview, long recIdx) +{ + if (indx_expr != NULL) + { + Expression::Context ctx (dbev, exp, dview, recIdx); + uint64_t idx = indx_expr->eval (&ctx); + Histable *cur_obj = dbeSession->createIndexObject (indxtype, idx); + cur_obj->set_name_from_context (&ctx); + NodeIdx dsc_idx = find_in_desc_htable (root_idx, cur_obj, true); + depth = 2; + return dsc_idx; + } + + bool showAll = dbev->isShowAll (); + int t_stack_prop = stack_prop; + void *stackId = dview->getObjValue (t_stack_prop, recIdx); + NodeIdx node_idx; + if (stackId != NULL) + { + // pathMap does not work with NULL key + node_idx = pathMap->get ((uint64_t) stackId); + if (node_idx != 0) + return node_idx; + } + Vector<Histable*> *stack = (Vector<Histable*>*)CallStack::getStackPCs (stackId, !showAll); + int stack_size = stack->size (); + if (stack_size == 0) + return root_idx; + + node_idx = root_idx; + int thisdepth = 1; + + for (int i = stack_size - 1; i >= 0; i--) + { + bool leaf = (i == 0); + Histable *cur_addr = stack->fetch (i); + + // bail out of loop if load object API-only is set + // and this is not the top frame + // This is now done in HSTACK if hide is set + + Function *func = (Function*) cur_addr->convertto (Histable::FUNCTION); + if (func != NULL) + { + Module *mod = func->module; + LoadObject *lo = mod->loadobject; + int segx = lo->seg_idx; + if (showAll && dbev->get_lo_expand (segx) == LIBEX_API + && i != stack_size - 1) + leaf = true; + } + + NodeIdx dsc_idx = find_desc_node (node_idx, cur_addr, leaf); + thisdepth++; + node_idx = dsc_idx; + + // LIBEX_API processing might have set leaf to true + if (leaf) + break; + } + if (thisdepth > depth) + depth = thisdepth; + delete stack; + pathMap->put ((uint64_t) stackId, node_idx); + return node_idx; +} + +static int +desc_node_comp (const void *s1, const void *s2, const void *ptree) +{ + PathTree::NodeIdx t1, t2; + t1 = *(PathTree::NodeIdx *)s1; + t2 = *(PathTree::NodeIdx *)s2; + PathTree* Ptree = (PathTree *) ptree; + PathTree::Node *n1 = Ptree->NODE_IDX (t1); + PathTree::Node *n2 = Ptree->NODE_IDX (t2); + Histable *d1 = n1->instr; + Histable *d2 = n2->instr; + if (d1->id < d2->id) + return -1; + else if (d1->id > d2->id) + return +1; + else + return 0; +} + +PathTree::NodeIdx +PathTree::find_in_desc_htable (NodeIdx node_idx, Histable *instr, bool leaf) +{ + unsigned int hash_code = (unsigned int) instr->id % desc_htable_size; + Node *node = NODE_IDX (node_idx); + hash_node_t *p = NULL; + for (p = descHT[hash_code]; p; p = p->next) + { + Node *dsc = NODE_IDX (p->nd); + Histable *dinstr = dsc->instr; + if (dinstr->id == instr->id && leaf == IS_LEAF (dsc)) + return p->nd; + } + // Not found + NodeIdx dsc_idx = new_Node (node_idx, instr, leaf); + node->descendants->append (dsc_idx); + p = new hash_node_t (); + p->nd = dsc_idx; + p->next = descHT[hash_code]; + descHT[hash_code] = p; + desc_htable_nelem++; + + // time to resize + if (desc_htable_nelem == desc_htable_size) + { + int old_htable_size = desc_htable_size; + desc_htable_size = old_htable_size * 2 + 1; + hash_node_t **old_htable = descHT; + descHT = new hash_node_t*[desc_htable_size]; + for (int i = 0; i < desc_htable_size; i++) + descHT[i] = NULL; + + for (int i = 0; i < old_htable_size; i++) + if (old_htable[i] != NULL) + { + hash_node *old_p; + hash_node_t *hash_p = old_htable[i]; + while (hash_p != NULL) + { + hash_node_t *new_p = new hash_node_t (); + new_p->nd = hash_p->nd; + Node *dnode = NODE_IDX (hash_p->nd); + Histable *dnode_instr = dnode->instr; + hash_code = (unsigned int) dnode_instr->id % desc_htable_size; + new_p->next = descHT[hash_code]; + descHT[hash_code] = new_p; + old_p = hash_p; + hash_p = hash_p->next; + delete old_p; + } + } + delete[] old_htable; + } + return dsc_idx; +} + +PathTree::NodeIdx +PathTree::find_desc_node (NodeIdx node_idx, Histable *instr, bool leaf) +{ + // Binary search. All nodes are ordered by Histable::id. + + // We have a special case when two nodes with the same + // id value may co-exist: one representing a leaf node and + // another one representing a call site. + Node *node = NODE_IDX (node_idx); + int left = 0; + int right = NUM_DESCENDANTS (node) - 1; + while (left <= right) + { + int index = (left + right) / 2; + NodeIdx dsc_idx = node->descendants->fetch (index); + Node *dsc = NODE_IDX (dsc_idx); + Histable *dinstr = dsc->instr; + if (instr->id < dinstr->id) + right = index - 1; + else if (instr->id > dinstr->id) + left = index + 1; + else if (leaf == IS_LEAF (dsc)) + return dsc_idx; + else if (leaf) + right = index - 1; + else + left = index + 1; + } + + // None was found. Create one. + NodeIdx dsc_idx = new_Node (node_idx, instr, leaf); + node->descendants->insert (left, dsc_idx); + return dsc_idx; +} + +PtreePhaseStatus +PathTree::process_packets (Experiment *exp, DataView *packets, int data_type) +{ + Expression::Context ctx (dbev, exp); + char *progress_bar_msg = NULL; + int progress_bar_percent = -1; + + Vector<BaseMetric*> *mlist = dbev->get_all_reg_metrics (); + Vector<BaseMetric*> mlist2; + StringBuilder stb; + for (int midx = 0, mlist_sz = mlist->size (); midx < mlist_sz; ++midx) + { + BaseMetric *mtr = mlist->fetch (midx); + if (mtr->get_packet_type () == data_type && + (mtr->get_expr () == NULL || mtr->get_expr ()->passes (&ctx))) + { + Hwcentry *hwc = mtr->get_hw_ctr (); + if (hwc) + { + stb.setLength (0); + // XXX this should be done at metric registration + Collection_params *col_params = exp->get_params (); + for (int i = 0; i < MAX_HWCOUNT; i++) + { + // We may have duplicate counters in col_params, + // check for all (see 5081284). + if (dbe_strcmp (hwc->name, col_params->hw_aux_name[i]) == 0) + { + if (stb.length () != 0) + stb.append (NTXT ("||")); + stb.append (NTXT ("HWCTAG==")); + stb.append (i); + } + } + if (stb.length () == 0) + continue; + stb.append (NTXT ("&& ((HWCINT & ")); + stb.append ((long long) HWCVAL_ERR_FLAG); + stb.append (NTXT (")==0)")); + char *s = stb.toString (); + mtr->set_cond_spec (s); + free (s); + } + ValueTag vtype = mtr->get_vtype (); + switch (vtype) + { + case VT_INT: + case VT_ULLONG: + case VT_LLONG: + break; // nothing to do + default: + vtype = VT_ULLONG; // ym: not sure when this would happen + break; + } + allocate_slot (mtr->get_id (), vtype); + mlist2.append (mtr); + } + } + + Slot **mslots = new Slot*[mlist2.size ()]; + for (int midx = 0, mlist_sz = mlist2.size (); midx < mlist_sz; ++midx) + { + BaseMetric *mtr = mlist2.fetch (midx); + int id = mtr->get_id (); + int slot_ind = find_slot (id); + mslots[midx] = SLOT_IDX (slot_ind); + } + + for (long i = 0, packets_sz = packets->getSize (); i < packets_sz; ++i) + { + if (dbeSession->is_interactive ()) + { + if (NULL == progress_bar_msg) + progress_bar_msg = dbe_sprintf (GTXT ("Processing Experiment: %s"), + get_basename (exp->get_expt_name ())); + int val = (int) (100 * i / packets_sz); + if (val > progress_bar_percent) + { + progress_bar_percent += 10; + if (theApplication->set_progress (val, progress_bar_msg) + && cancel_ok) + { + delete[] mslots; + return CANCELED; + } + } + } + + NodeIdx path_idx = 0; + ctx.put (packets, i); + + for (int midx = 0, mlist_sz = mlist2.size (); midx < mlist_sz; ++midx) + { + BaseMetric *mtr = mlist2.fetch (midx); + if (mtr->get_cond () != NULL && !mtr->get_cond ()->passes (&ctx)) + continue; + + int64_t mval = mtr->get_val ()->eval (&ctx); + if (mval == 0) + continue; + if (path_idx == 0) + path_idx = find_path (exp, packets, i); + NodeIdx node_idx = path_idx; + Slot *mslot = mslots[midx]; + while (node_idx) + { + INCREMENT_METRIC (mslot, node_idx, mval); + node_idx = NODE_IDX (node_idx)->ancestor; + } + } + } + if (dbeSession->is_interactive ()) + free (progress_bar_msg); + delete[] mslots; + if (indx_expr != NULL) + root->descendants->sort ((CompareFunc) desc_node_comp, this); + return NORMAL; +} + +DataView * +PathTree::get_filtered_events (int exp_index, int data_type) +{ + if (indx_expr != NULL) + { + IndexObjType_t *indexObj = dbeSession->getIndexSpace (indxtype); + if (indexObj->memObj && data_type != DATA_HWC) + return NULL; + } + return dbev->get_filtered_events (exp_index, data_type); +} + +PtreePhaseStatus +PathTree::add_experiment (int exp_index) +{ + StringBuilder sb; + char *expt_name; + char *base_name; + Emsg *m; + Experiment *experiment = dbeSession->get_exp (exp_index); + if (experiment->broken != 0) + return NORMAL; + status = 0; + expt_name = experiment->get_expt_name (); + base_name = get_basename (expt_name); + + hrtime_t starttime = gethrtime (); + hrtime_t startvtime = gethrvtime (); + + // Experiment::getEndTime was initially implemented as + // returning exp->last_event. To preserve the semantics + // new Experiment::getLastEvent() is used here. + hrtime_t tot_time = experiment->getLastEvent () - experiment->getStartTime (); + + if (!dbev->isShowAll () && (dbev->isShowHideChanged () + || dbev->isNewViewMode ())) + experiment->resetShowHideStack (); + + // To report experiment index to the user, + // start numeration from 1, not 0 + sb.sprintf (GTXT ("PathTree processing experiment %d (`%s'); duration %lld.%06lld"), + exp_index + 1, base_name, + tot_time / NANOSEC, (tot_time % NANOSEC / 1000)); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + + DataView *prof_packet = get_filtered_events (exp_index, DATA_CLOCK); + if (prof_packet && prof_packet->getSize () > 0) + { + if (process_packets (experiment, prof_packet, DATA_CLOCK) == CANCELED) + return CANCELED; + long clock_cnt = prof_packet->getSize (); + double clock_rate; + if (tot_time != 0) + clock_rate = (double) clock_cnt / (double) tot_time * (double) NANOSEC; + else + clock_rate = (double) 0.; + if (experiment->timelineavail) + sb.sprintf (GTXT (" Processed %ld clock-profile events (%3.2f/sec.)"), + clock_cnt, clock_rate); + else + sb.sprintf (GTXT (" Processed %ld clock-profile events"), clock_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + + // check for statistical validity + if ((experiment->timelineavail == true) + && !dbev->get_filter_active () && (clock_cnt < MIN_PROF_CNT)) + { + sb.sprintf (GTXT ("WARNING: too few clock-profile events (%ld) in experiment %d (`%s') for statistical validity"), + clock_cnt, exp_index + 1, base_name); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + } + + DataView *sync_packet = get_filtered_events (exp_index, DATA_SYNCH); + if (sync_packet && sync_packet->getSize () > 0) + { + if (process_packets (experiment, sync_packet, DATA_SYNCH) == CANCELED) + return CANCELED; + long sync_cnt = sync_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld synctrace events"), sync_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + DataView *iotrace_packet = get_filtered_events (exp_index, DATA_IOTRACE); + if (iotrace_packet && iotrace_packet->getSize () > 0) + { + if (process_packets (experiment, iotrace_packet, DATA_IOTRACE) == CANCELED) + return CANCELED; + long iotrace_cnt = iotrace_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld IO trace events"), iotrace_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + DataView *hwc_packet = get_filtered_events (exp_index, DATA_HWC); + if (hwc_packet && hwc_packet->getSize () > 0) + { + if (process_packets (experiment, hwc_packet, DATA_HWC) == CANCELED) + return CANCELED; + long hwc_cnt = hwc_packet->getSize (); + double hwc_rate = (double) hwc_cnt / (double) tot_time * (double) NANOSEC; + if (experiment->timelineavail) + sb.sprintf (GTXT (" Processed %ld hwc-profile events (%3.2f/sec.)"), + hwc_cnt, hwc_rate); + else + sb.sprintf (GTXT (" Processed %ld hwc-profile events"), hwc_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + + // check for statistical validity + if (experiment->timelineavail && !dbev->get_filter_active () && (hwc_cnt < MIN_PROF_CNT)) + { + sb.sprintf (GTXT ("WARNING: too few HW counter profile events (%ld) in experiment %d (`%s') for statistical validity"), + hwc_cnt, exp_index + 1, base_name); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + } + + DataView *heap_packet = get_filtered_events (exp_index, DATA_HEAP); + if (heap_packet && heap_packet->getSize () > 0) + { + if (process_packets (experiment, heap_packet, DATA_HEAP) == CANCELED) + return CANCELED; + long heap_cnt = heap_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld heaptrace events"), heap_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + DataView *race_packet = get_filtered_events (exp_index, DATA_RACE); + if (race_packet && race_packet->getSize () > 0) + { + if (process_packets (experiment, race_packet, DATA_RACE) == CANCELED) + return CANCELED; + long race_cnt = race_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld race access events"), race_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + DataView *deadlock_packet = get_filtered_events (exp_index, DATA_DLCK); + if (deadlock_packet && deadlock_packet->getSize () > 0) + { + if (process_packets (experiment, deadlock_packet, DATA_DLCK) == CANCELED) + return CANCELED; + long race_cnt = deadlock_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld race access events"), race_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + hrtime_t pathtime = gethrtime () - starttime; + hrtime_t pathvtime = gethrvtime () - startvtime; + sb.sprintf (GTXT ("PathTree time = %lld.%06lld CPU-time %lld.%06lld\n"), + pathtime / NANOSEC, (pathtime % NANOSEC) / 1000, + pathvtime / NANOSEC, (pathvtime % NANOSEC) / 1000); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + return NORMAL; +} + +Hist_data * +PathTree::compute_metrics (MetricList *mlist, Histable::Type type, + Hist_data::Mode mode, Vector<Histable*> *objs, + Histable *context, Vector<Histable*> *sel_objs, + PtreeComputeOption computeOpt) +{ + VMode view_mode = dbev->get_view_mode (); + + // For displaying disassembly correctly in user mode with openmp + if (ptree_internal != NULL && + (view_mode == VMODE_EXPERT || + (view_mode == VMODE_USER && (type == Histable::INSTR + || (dbev->isOmpDisMode () + && type == Histable::FUNCTION + && mode == Hist_data::CALLEES + && computeOpt == COMPUTEOPT_OMP_CALLEE)) + ))) + return ptree_internal->compute_metrics (mlist, type, mode, objs, context, + sel_objs); + + PtreePhaseStatus resetStatus = reset (); + + hist_data = new Hist_data (mlist, type, mode); + int nmetrics = mlist->get_items ()->size (); + int sort_ind = -1; + Hist_data::HistItem *hi; + int index; + + if (status != 0 || resetStatus == CANCELED) + return hist_data; + + hist_data->set_status (Hist_data::SUCCESS); + if (dbeSession->is_interactive () && mode != Hist_data::CALLEES) + theApplication->set_progress (0, GTXT ("Constructing Metrics")); + + xlate = new int[nmetrics]; + for (int mind = 0; mind < nmetrics; mind++) + { + Metric *mtr = mlist->get (mind); + xlate[mind] = find_slot (mtr->get_id ()); + } + + // Compute dynamic metrics + obj_list = new Histable*[depth]; + if ((type == Histable::LINE || type == Histable::INSTR) + && mode == Hist_data::CALLERS) + node_list = new Node*[depth]; + percent = 0; + ndone = 0; + if (mode == Hist_data::MODL) + { + Histable *obj = objs && objs->size () > 0 ? objs->fetch (0) : NULL; + if (obj != NULL) + { + switch (obj->get_type ()) + { + case Histable::FUNCTION: + { + Vector<Function*> *funclist = new Vector<Function*>; + funclist->append ((Function*) obj); + get_metrics (funclist, context); + delete funclist; + break; + } + case Histable::MODULE: + { + Vector<Histable*> *comparableModules = obj->get_comparable_objs (); + if (comparableModules != NULL) + { + Vector<Function*> *functions = new Vector<Function*>; + for (int i = 0; i < comparableModules->size (); i++) + { + Module *mod = (Module*) comparableModules->fetch (i); + if (mod) + { + bool found = false; + for (int i1 = 0; i1 < i; i1++) + { + if (mod == comparableModules->fetch (i1)) + { + found = true; + break; + } + } + if (!found) + functions->addAll (mod->functions); + } + } + get_metrics (functions, context); + delete functions; + } + else + get_metrics (((Module*) obj)->functions, context); + break; + } + case Histable::SOURCEFILE: + get_metrics (((SourceFile *) obj)->get_functions (), context); + break; + default: + DBG (assert (0)); + } + } + } + else if (mode == Hist_data::CALLERS) + { + if (objs && objs->size () > 0) + get_clr_metrics (objs); + } + else if (mode == Hist_data::CALLEES) + { + if (objs && objs->size () > 0) + get_cle_metrics (objs); + else // Special case: get root + get_cle_metrics (NULL); + } + else if (mode == Hist_data::SELF) + { + if (objs->size () == 1) + { + Histable *obj = objs->fetch (0); + if (obj != NULL) + { + if (obj->get_type () == Histable::LINE) + { + Vector<Function*> *funclist = new Vector<Function*>; + for (DbeLine *dl = (DbeLine*) obj->convertto (Histable::LINE); + dl; dl = dl->dbeline_func_next) + if (dl->func) + funclist->append (dl->func); + + get_self_metrics (obj, funclist, sel_objs); + delete funclist; + } + else if (obj->get_type () == Histable::FUNCTION + || obj->get_type () == Histable::INSTR) + { + // Use shortcut for functions and oth. + if (context) + { + Vector<Function*> *funclist = NULL; + if (context->get_type () == Histable::MODULE) + funclist = ((Module*) context)->functions->copy (); + else + { + funclist = new Vector<Function*>; + funclist->append ((Function*) context); + } + get_self_metrics (obj, funclist, sel_objs); + delete funclist; + } + else + get_self_metrics (objs); + } + else + get_self_metrics (objs); + } + } + else + get_self_metrics (objs); + } + else // Hist_data::ALL + get_metrics (root_idx, 0); + + delete[] obj_list; + if ((type == Histable::LINE || type == Histable::INSTR) + && mode == Hist_data::CALLERS) + delete[] node_list; + + // Postprocess; find total + for (long mind = 0, sz = mlist->get_items ()->size (); mind < sz; mind++) + { + Metric *mtr = mlist->get_items ()->get (mind); + Metric::SubType subtype = mtr->get_subtype (); + ValueTag vtype = mtr->get_vtype (); + hist_data->total->value[mind].tag = vtype; + + switch (vtype) + { + // ignoring the following cases (why?) + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + + case VT_INT: + // Calculate total as the sum of all values in hist_data for + // ATTRIBUTED metrics only. For all others, use root node values. + // + if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES) + && subtype == Metric::ATTRIBUTED) + { + hist_data->total->value[mind].i = 0; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hist_data->total->value[mind].i += hi->value[mind].i; + } + if (mode == Hist_data::CALLEES) + hist_data->total->value[mind].i += hist_data->gprof_item->value[mind].i; + } + else if (xlate[mind] != -1) + ASN_METRIC_VAL (hist_data->total->value[mind], slots[xlate[mind]], + root_idx); + break; + + case VT_LLONG: + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + } + + if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES) + && subtype == Metric::ATTRIBUTED) + { + hist_data->total->value[mind].ll = 0; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hist_data->total->value[mind].ll += hi->value[mind].ll; + } + if (mode == Hist_data::CALLEES) + hist_data->total->value[mind].ll += hist_data->gprof_item->value[mind].ll; + } + else if (xlate[mind] != -1) + ASN_METRIC_VAL (hist_data->total->value[mind], slots[xlate[mind]], root_idx); + break; + + case VT_ULLONG: + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + } + if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES) + && subtype == Metric::ATTRIBUTED) + { + hist_data->total->value[mind].ull = 0; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hist_data->total->value[mind].ull += hi->value[mind].ull; + } + if (mode == Hist_data::CALLEES) + hist_data->total->value[mind].ull += hist_data->gprof_item->value[mind].ull; + } + else if (xlate[mind] != -1) + ASN_METRIC_VAL (hist_data->total->value[mind], slots[xlate[mind]], root_idx); + break; + + case VT_DOUBLE: + double prec = mtr->get_precision (); + ValueTag vt = (xlate[mind] != -1) ? slots[xlate[mind]].vtype : VT_INT; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + double val = (vt == VT_LLONG ? hi->value[mind].ll : + (vt == VT_ULLONG ? hi->value[mind].ull + : hi->value[mind].i)); + hi->value[mind].tag = vtype; + hi->value[mind].d = val / prec; + } + + if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES) + && subtype == Metric::ATTRIBUTED) + { + hist_data->total->value[mind].d = 0.0; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hist_data->total->value[mind].d += hi->value[mind].d; + } + if (mode == Hist_data::CALLEES) + hist_data->total->value[mind].d += + (double) (vt == VT_LLONG ? hist_data->gprof_item->value[mind].ll : + (vt == VT_ULLONG ? hist_data->gprof_item->value[mind].ull : + hist_data->gprof_item->value[mind].i)) / prec; + } + else if (xlate[mind] != -1) + { + TValue& total = hist_data->total->value[mind]; + ASN_METRIC_VAL (total, slots[xlate[mind]], root_idx); + double val = (vt == VT_LLONG ? total.ll : + (vt == VT_ULLONG ? total.ll : total.i)); + total.d = val / prec; + } + break; + } + } + delete[] xlate; + + // Determine by which metric to sort if any + bool rev_sort = mlist->get_sort_rev (); + for (long mind = 0, sz = mlist->get_items ()->size (); mind < sz; mind++) + { + Metric *mtr = mlist->get_items ()->get (mind); + if (mlist->get_sort_ref_index () == mind) + sort_ind = mind; + + switch (mtr->get_type ()) + { + case BaseMetric::SIZES: + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].tag = VT_LLONG; + hi->value[mind].ll = h ? h->get_size () : 0; + } + break; + case BaseMetric::ADDRESS: + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].tag = VT_ADDRESS; + hi->value[mind].ll = h ? h->get_addr () : 0; + } + break; + case BaseMetric::DERIVED: + { + Definition *def = mtr->get_definition (); + long *map = def->get_map (); + for (long i1 = 0, sz1 = hist_data->hist_items->size (); i1 < sz1; i1++) + { + /* Hist_data::HistItem * */hi = hist_data->hist_items->get (i1); + hi->value[mind].tag = VT_DOUBLE; + hi->value[mind].d = def->eval (map, hi->value); + } + hist_data->total->value[mind].tag = VT_DOUBLE; + hist_data->total->value[mind].d = def->eval (map, hist_data->total->value); + } + break; + default: + break; + } + } + + hist_data->sort (sort_ind, rev_sort); + hist_data->compute_minmax (); + if (dbeSession->is_interactive () && mode != Hist_data::CALLERS) + theApplication->set_progress (0, GTXT ("")); + +#if DEBUG_FTREE + if (ftree_hist_data) + { + bool matches = ftree_debug_match_hist_data (hist_data, ftree_hist_data); + if (!matches) + assert (false); + delete hist_data; + hist_data = ftree_hist_data; // return the debug version + } +#endif + return hist_data; +} + +#if DEBUG_FTREE +bool +PathTree::ftree_debug_match_hist_data (Hist_data *data /* ref */, + Hist_data *data_tmp) +{ + if (data->get_status () != Hist_data::SUCCESS) + { + DBG (assert (false)); + return false; + } + if (data == NULL && data != data_tmp) + { + DBG (assert (false)); + return false; + } + + MetricList *mlist; + mlist = data->get_metric_list (); + MetricList *mlist_tmp; + mlist_tmp = data_tmp->get_metric_list (); + if (mlist->size () != mlist_tmp->size ()) + { + DBG (assert (false)); + return false; + } + + // Get table size: count visible metrics + int nitems = data->size (); + if (data->size () != data_tmp->size ()) + { + DBG (assert (false)); + return false; + } + + for (int i = 0; i < nitems; ++i) + { + Hist_data::HistItem *item = data->fetch (i); + Hist_data::HistItem *item_tmp = data_tmp->fetch (i); + if (item->obj->id != item_tmp->obj->id) + { + DBG (assert (false)); + return false; + } + } + + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + long met_ind = i; + Metric *mitem = mlist->get (i); + Metric *mitem_tmp = mlist_tmp->get (i); + + if (mitem->get_id () != mitem_tmp->get_id ()) + { + DBG (assert (false)); + return false; + } + if (mitem->get_visbits () != mitem_tmp->get_visbits ()) + { + DBG (assert (false)); + return false; + } + if (mitem->get_vtype () != mitem_tmp->get_vtype ()) + { + DBG (assert (false)); + return false; + } + + if (!mitem->is_visible () && !mitem->is_tvisible () + && !mitem->is_pvisible ()) + continue; + // table->append(dbeGetTableDataOneColumn(data, i)); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + Metric *m = mitem; + TValue res; + TValue res_tmp; + TValue *v = data->get_value (&res, met_ind, row); + TValue *v_tmp = data_tmp->get_value (&res_tmp, met_ind, row); + if ((m->get_visbits () & VAL_RATIO) != 0) + { + if (v->tag != VT_LABEL) + { + if (v->to_double () != v_tmp->to_double ()) + { + DBG (assert (false)); + return false; + } + } + continue; + } + switch (m->get_vtype ()) + { + case VT_DOUBLE: + { + double diff = v->d - v_tmp->d; + if (diff < 0) diff = -diff; + if (diff > 0.0001) + { + DBG (assert (false)); + return false; + } + else + DBG (assert (true)); + break; + } + case VT_INT: + if (v->i != v_tmp->i) + { + DBG (assert (false)); + return false; + } + break; + case VT_ULLONG: + case VT_LLONG: + case VT_ADDRESS: + if (v->ll != v_tmp->ll) + { + DBG (assert (false)); + return false; + } + break; + + case VT_LABEL: + if (dbe_strcmp (v->l, v_tmp->l)) + { + DBG (assert (false)); + return false; + } + break; + default: + DBG (assert (false)); + return false; + } + } + } + return true; +} +#endif + +Histable * +PathTree::get_hist_func_obj (Node *node) +{ + LoadObject *lo; + Function *func; + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + // LIBRARY VISIBILITY + lo = func->module->loadobject; + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + return get_compare_obj (func); +} + +Histable * +PathTree::get_hist_obj (Node *node, Histable* context) +{ + LoadObject *lo; + Function *func; + switch (hist_data->type) + { + case Histable::INSTR: + if (hist_data->mode == Hist_data::MODL) + { + if (node->instr->get_type () != Histable::INSTR) + return NULL; + } + else + { + // LIBRARY VISIBILITY + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + lo = func->module->loadobject; + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + } + return node->instr; + + case Histable::LINE: + if (hist_data->mode != Hist_data::MODL) + { + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + lo = func->module->loadobject; + // LIBRARY VISIBILITY + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + } + // For openmp user mode - the stack is already made with dbelines, + // no need to convert it + if (node->instr->get_type () == Histable::LINE) + return node->instr; + return node->instr->convertto (Histable::LINE, context); + + case Histable::FUNCTION: + if (pathTreeType == PATHTREE_INTERNAL_FUNCTREE && node->ancestor != 0) + func = (Function*) node->instr; + else + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + lo = func->module->loadobject; + // LIBRARY VISIBILITY + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + return get_compare_obj (func); + case Histable::MODULE: + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + return func->module; + case Histable::LOADOBJECT: + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + return func->module->loadobject; + case Histable::INDEXOBJ: + case Histable::MEMOBJ: + return node->instr; + default: + DBG (assert (0)); + } + return NULL; +} + +Histable * +PathTree::get_compare_obj (Histable *obj) +{ + if (obj && dbev->comparingExperiments ()) + obj = dbev->get_compare_obj (obj); + return obj; +} + +void +PathTree::get_metrics (NodeIdx node_idx, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj = get_hist_obj (node); + obj_list[dpth] = cur_obj; + + // Check for recursion (inclusive metrics) + int incl_ok = 1; + for (int i = dpth - 1; i >= 0; i--) + if (cur_obj == obj_list[i]) + { + incl_ok = 0; + break; + } + + // Check for leaf nodes (exclusive metrics) + int excl_ok = 0; + if (IS_LEAF (node) || node == NODE_IDX (root_idx)) + excl_ok = 1; + + // We shouldn't eliminate empty subtrees here because + // we create the list of hist items dynamically and want + // one for each object in the tree. + cur_obj = get_compare_obj (cur_obj); + Hist_data::HistItem *hi = hist_data->append_hist_item (cur_obj); + DBG (assert (hi != NULL)); + + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + + switch (subtype) + { + case Metric::INCLUSIVE: + if (incl_ok && hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + case Metric::EXCLUSIVE: + if (excl_ok && hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + // ignoring the following cases (why?) + case Metric::STATIC: + case Metric::ATTRIBUTED: + break; + case Metric::DATASPACE: + if (hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + } + } + + if (dbeSession->is_interactive ()) + { + ndone++; + int new_percent = 95 * ndone / nodes; + if (new_percent > percent) + { + percent = new_percent; + theApplication->set_progress (percent, NULL); + } + } + + // Recursively process all descendants + int index; + int dsize = NUM_DESCENDANTS (node); + for (index = 0; index < dsize; index++) + get_metrics (node->descendants->fetch (index), dpth + 1); +} + +void +PathTree::get_clr_metrics (Vector<Histable*> *objs, NodeIdx node_idx, + int pmatch, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj; + if (hist_data->type == Histable::LINE || hist_data->type == Histable::INSTR) + { + cur_obj = get_hist_func_obj (node); + node_list[dpth] = node; + } + else + cur_obj = get_hist_obj (node); + obj_list[dpth] = cur_obj; + + bool match = false; + int nobj = objs->size (); + if (dpth + 1 >= nobj) + { + match = true; + for (int i = 0; i < nobj; ++i) + { + if (objs->fetch (i) != obj_list[dpth - nobj + 1 + i]) + { + match = false; + break; + } + } + } + + Hist_data::HistItem *hi = NULL; + Hist_data::HistItem *hi_adj = NULL; + if (match && dpth >= nobj) + { + if (hist_data->type == Histable::LINE + || hist_data->type == Histable::INSTR) + hi = hist_data->append_hist_item (get_hist_obj (node_list[dpth - nobj])); + else + hi = hist_data->append_hist_item (obj_list[dpth - nobj]); + + if (pmatch >= 0 && pmatch >= nobj) + { + if (hist_data->type == Histable::LINE + || hist_data->type == Histable::INSTR) + hi_adj = hist_data->append_hist_item (get_hist_obj ( + node_list[pmatch - nobj])); + else + hi_adj = hist_data->append_hist_item (obj_list[pmatch - nobj]); + } + } + + if (hi != NULL) + { + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + + switch (subtype) + { + case Metric::ATTRIBUTED: + if (hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + if (hi_adj) + SUB_METRIC_VAL (hi_adj->value[ind], slots[xlate[ind]], node_idx); + break; + case Metric::STATIC: + case Metric::EXCLUSIVE: + case Metric::INCLUSIVE: + case Metric::DATASPACE: + break; + } + } + } + + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + get_clr_metrics (objs, node->descendants->fetch (index), + match ? dpth : pmatch, dpth + 1); +} + +void +PathTree::get_clr_metrics (Vector<Histable*> *objs) +{ + get_clr_metrics (objs, root_idx, -1, 0); +} + +void +PathTree::get_cle_metrics (Vector<Histable*> *objs, NodeIdx node_idx, int pcle, + int pmatch, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj = get_hist_obj (node); + obj_list[dpth] = cur_obj; + + bool match = false; + int nobj = objs->size (); + if (dpth + 1 >= nobj) + { + match = true; + for (int i = 0; i < nobj; ++i) + if (objs->fetch (i) != obj_list[dpth - nobj + 1 + i]) + { + match = false; + break; + } + } + + Hist_data::HistItem *hi = NULL; + Hist_data::HistItem *hi_adj = NULL; + if (pmatch >= 0 && dpth == pmatch + 1) + hi = hist_data->append_hist_item (cur_obj); + if (match && IS_LEAF (node)) + hi = hist_data->gprof_item; + if (pcle >= 0) + hi_adj = hist_data->append_hist_item (obj_list[pcle]); + + if (hi != NULL) + { + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (subtype == Metric::ATTRIBUTED) + { + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + if (hi_adj) + SUB_METRIC_VAL (hi_adj->value[ind], slots[xlate[ind]], node_idx); + } + } + } + + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + get_cle_metrics (objs, node->descendants->fetch (index), + pmatch >= 0 && dpth == pmatch + 1 ? dpth : pcle, + match ? dpth : pmatch, dpth + 1); +} + +void +PathTree::get_cle_metrics (Vector<Histable*> *objs, NodeIdx node_idx, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj = get_hist_obj (node); + Hist_data::HistItem *hi = NULL; + if (NULL == objs) // Special case: get root + hi = hist_data->append_hist_item (cur_obj); + else + { + if (dpth == objs->size ()) + hi = hist_data->append_hist_item (cur_obj); + else if (cur_obj == objs->fetch (dpth)) + { + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + get_cle_metrics (objs, node->descendants->fetch (index), dpth + 1); + if (dpth == objs->size () - 1 && dsize == 0) + hi = hist_data->gprof_item; + } + } + + if (hi != NULL) + { + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (subtype == Metric::ATTRIBUTED) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + } + } +} + +void +PathTree::ftree_reset () +{ + if (pathTreeType == PATHTREE_MAIN && indxtype < 0) + { + reset (); + if (ftree_needs_update) + { + if (ftree_internal == NULL) + { + ftree_internal = new PathTree (dbev, indxtype, + PATHTREE_INTERNAL_FUNCTREE); + if (ftree_internal == NULL) + return; + } + ftree_internal->ftree_build (this); + ftree_needs_update = false; + } + } +} + +void +PathTree::ftree_build (PathTree * mstr) +{ + fini (); + init (); + allocate_slots (mstr->slots, mstr->nslots); + ftree_build (mstr, mstr->root_idx, root_idx); + depth = mstr->depth; + depth_map_build (); +} + +#if DEBUG_FTREE // possibly TBR +void +PathTree::ftree_dump () +{ + hrtime_t starttime, endtime; + int nmetrics = 1; + // int nmetrics = nslots; + for (int kk = 0; kk < nmetrics; kk++) + { + int id = slots[kk].id; + starttime = gethrtime (); + long nodecnt = 0; + for (int ii = 0; ii < depth; ii++) + { + Vector<Vector<void*>*> *tmp = (Vector<Vector<void*>*>*)get_ftree_level + (id, ii); + if (tmp == NULL) + continue; + long sz = tmp->get (0)->size (); + nodecnt += sz; +#if 1 + // fprintf(stderr, "... finished (%ld nodes)\n", sz); +#else + Vector<NodeIdx> *nodeIdxList = (Vector<NodeIdx> *)tmp->get (0); + Vector<NodeIdx> *ancestorNodeIdxList = (Vector<NodeIdx> *)tmp->get (1); + Vector<uint64_t> *idList = (Vector<uint64_t> *)tmp->get (2); + Vector<uint64_t> *vals = (Vector<uint64_t> *)tmp->get (3); + for (int jj = 0; jj < sz; jj++) + fprintf (stderr, " ...%d:%d node=%ld, anc=%ld, id=%llu, val=%llu\n", + sz, jj, nodeIdxList->get (jj), + ancestorNodeIdxList->get (jj), + idList->get (jj), vals->get (jj)); +#endif + destroy (tmp); + } + endtime = gethrtime (); + fprintf (stderr, "====================== %ld nodes time=%llu\n", + nodecnt, (endtime - starttime) / 1000 / 1000); + } +} +#endif + +// ftree: translate mstr Histable::INSTR to Histable::FUNCTION +void +PathTree::ftree_build (PathTree *mstr, NodeIdx mstr_node_idx, + NodeIdx local_node_idx) +{ + // requires: slots, nslots + Node *mstr_node = mstr->NODE_IDX (mstr_node_idx); + int dsize = NUM_DESCENDANTS (mstr_node); + + // Add metrics + for (int i = 0; i < nslots; i++) + { + if (i >= mstr->nslots) + continue; //weird + if (slots[i].vtype != mstr->slots[i].vtype) + continue; //weird + TValue val; + val.ll = 0; + mstr->ASN_METRIC_VAL (val, mstr->slots[i], mstr_node_idx); + int64_t mval; + switch (slots[i].vtype) + { + case VT_ULLONG: + case VT_LLONG: + mval = val.ll; + break; + case VT_INT: + mval = val.i; + break; + default: + mval = 0; + break; + } + if (mval) + { + Slot * mslot = SLOT_IDX (i); + if (mslot) + INCREMENT_METRIC (mslot, local_node_idx, mval); + } + } + + // Recursively process all descendants + for (int index = 0; index < dsize; index++) + { + NodeIdx mstr_desc_node_idx = mstr_node->descendants->fetch (index); + Node *mstr_desc_node = mstr->NODE_IDX (mstr_desc_node_idx); + Function *func = (Function*) mstr_desc_node->instr->convertto (Histable::FUNCTION); + int mstr_desc_dsize = NUM_DESCENDANTS (mstr_desc_node); + bool leaf = (mstr_desc_dsize == 0); + NodeIdx local_desc_node_idx = find_desc_node (local_node_idx, func, leaf); + ftree_build (mstr, mstr_desc_node_idx, local_desc_node_idx); + } +} + +void +PathTree::depth_map_build () +{ + destroy (depth_map); + depth_map = new Vector<Vector<NodeIdx>*>(depth); + if (depth) + { + depth_map->put (depth - 1, 0); // fill vector with nulls + depth_map_build (root_idx, 0); + } +} + +void +PathTree::depth_map_build (NodeIdx node_idx, int dpth) +{ + Node *node = NODE_IDX (node_idx); + + Vector<NodeIdx> *node_idxs = depth_map->get (dpth); + if (node_idxs == NULL) + { + node_idxs = new Vector<NodeIdx>(); + depth_map->store (dpth, node_idxs); + } + node_idxs->append (node_idx); + + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + { + NodeIdx desc_node_idx = node->descendants->fetch (index); + depth_map_build (desc_node_idx, dpth + 1); + } +} + +int +PathTree::get_ftree_depth () +{ // external use only + ftree_reset (); + if (!ftree_internal) + return 0; + return ftree_internal->get_depth (); +} + +Vector<Function*>* +PathTree::get_ftree_funcs () +{ // external use only + ftree_reset (); + if (!ftree_internal) + return NULL; + return ftree_internal->get_funcs (); +} + +Vector<Function*>* +PathTree::get_funcs () +{ + // get unique functions + if (fn_map == NULL) + return NULL; + return fn_map->keySet (); +} + +Vector<void*>* +PathTree::get_ftree_level (BaseMetric *bm, int dpth) +{ // external use only + ftree_reset (); + if (!ftree_internal) + return NULL; + return ftree_internal->get_level (bm, dpth); +} + +Vector<void*>* +PathTree::get_level (BaseMetric *bm, int dpth) +{ + // Nodes at tree depth dpth + if (dpth < 0 || dpth >= depth) + return NULL; + if (depth_map == NULL) + return NULL; + Vector<NodeIdx> *node_idxs = depth_map->get (dpth); + return get_nodes (bm, node_idxs); +} + +Vector<void*>* +PathTree::get_ftree_node_children (BaseMetric *bm, NodeIdx node_idx) +{ // external use only + ftree_reset (); + if (!ftree_internal) + return NULL; + return ftree_internal->get_node_children (bm, node_idx); +} + +Vector<void*>* +PathTree::get_node_children (BaseMetric *bm, NodeIdx node_idx) +{ + // Nodes that are children of node_idx + if (depth_map == NULL) + return NULL; + if (node_idx == 0) // special case for root + return get_nodes (bm, depth_map->get (0)); + if (node_idx < 0 || node_idx >= nodes) + return NULL; + Node *node = NODE_IDX (node_idx); + if (node == NULL) + return NULL; + Vector<NodeIdx> *node_idxs = node->descendants; + return get_nodes (bm, node_idxs); +} + +Vector<void*>* +PathTree::get_nodes (BaseMetric *bm, Vector<NodeIdx> *node_idxs) +{ // used for ftree + // capture info for node_idxs: + // node's idx + // node->ancestor idx + // node->instr->id + // mind metric value // in the future, could instead accept vector of mind + if (node_idxs == NULL) + return NULL; + long sz = node_idxs->size (); + if (sz <= 0) + return NULL; + + bool calculate_metric = false; + ValueTag vtype; + int slot_idx; + double prec; + if (bm != NULL) + { + int mind = bm->get_id (); + slot_idx = find_slot (mind); // may be -1 (CPI and IPC) + prec = bm->get_precision (); + vtype = bm->get_vtype (); + } + else + { + slot_idx = -1; + prec = 1.0; + vtype = VT_INT; + } + + if (slot_idx >= 0) + { + switch (vtype) + { + case VT_ULLONG: + case VT_LLONG: + case VT_INT: + if (slots[slot_idx].vtype == vtype) + calculate_metric = true; + else + DBG (assert (false)); + break; + case VT_DOUBLE: + calculate_metric = true; + break; + default: + break; + } + } + + Vector<void*> *results = new Vector<void*>(4); + if (!calculate_metric) + results->store (3, NULL); + else + { + // Code below cribbed from Dbe.cc:dbeGetTableDataV2Data. + // TBD: possibly create an intermediate HistData and instead call that routine + switch (vtype) + { + case VT_ULLONG: + case VT_LLONG: + { + Vector<long long> *vals = new Vector<long long>(sz); + for (long i = 0; i < sz; i++) + { + NodeIdx node_idx = node_idxs->get (i); + TValue val; + val.ll = 0; + ASN_METRIC_VAL (val, slots[slot_idx], node_idx); + vals->append (val.ll); + } + results->store (3, vals); + break; + } + case VT_DOUBLE: + { + Vector<double> *vals = new Vector<double>(sz); + TValue val; + val.tag = slots[slot_idx].vtype; // required for to_double(); + for (long i = 0; i < sz; i++) + { + NodeIdx node_idx = node_idxs->get (i); + val.ll = 0; + ASN_METRIC_VAL (val, slots[slot_idx], node_idx); + double dval = val.to_double (); + dval /= prec; + vals->append (dval); + } + results->store (3, vals); + break; + } + case VT_INT: + { + Vector<int> *vals = new Vector<int>(sz); + for (long i = 0; i < sz; i++) + { + NodeIdx node_idx = node_idxs->get (i); + TValue val; + val.i = 0; + ASN_METRIC_VAL (val, slots[slot_idx], node_idx); + vals->append (val.i); + } + results->store (3, vals); + break; + } + default: + results->store (3, NULL); + break; + } + } + + Vector<int> *nodeIdxList = new Vector<int>(sz); + Vector<int> *ancestorNodeIdxList = new Vector<int>(sz); + Vector<uint64_t> *idList = new Vector<uint64_t>(sz); + for (long i = 0; i < sz; i++) + { + NodeIdx node_idx = node_idxs->get (i); + Node *node = NODE_IDX (node_idx); + NodeIdx ancestor_idx = node->ancestor; + Histable *func = node->instr; + nodeIdxList->append (node_idx); + ancestorNodeIdxList->append (ancestor_idx); + idList->append (func->id); + } + + results->store (0, nodeIdxList); + results->store (1, ancestorNodeIdxList); + results->store (2, idList); + return results; +} + +void +PathTree::get_cle_metrics (Vector<Histable*> *objs) +{ + if (NULL == objs || objs->fetch (0) == get_hist_obj (NODE_IDX (root_idx))) + // Call Tree optimization + get_cle_metrics (objs, root_idx, 0); + else + // General case + get_cle_metrics (objs, root_idx, -1, -1, 0); +} + +void +PathTree::get_metrics (Vector<Function*> *functions, Histable *context) +{ + Function *fitem; + int excl_ok, incl_ok; + NodeIdx node_idx; + Node *node, *anc; + int index; + + Vec_loop (Function*, functions, index, fitem) + { + node_idx = fn_map->get (fitem); + for (; node_idx; node_idx = node->funclist) + { + node = NODE_IDX (node_idx); + Histable *h_obj = get_hist_obj (node, context); + if (h_obj == NULL) + continue; + + // Check for recursion (inclusive metrics) + incl_ok = 1; + for (anc = NODE_IDX (node->ancestor); anc; + anc = NODE_IDX (anc->ancestor)) + { + if (h_obj == get_hist_obj (anc, context)) + { + incl_ok = 0; + break; + } + } + + // Check for leaf nodes (exclusive metrics) + excl_ok = 0; + if (IS_LEAF (node)) + excl_ok = 1; + + h_obj = get_compare_obj (h_obj); + Hist_data::HistItem *hi = hist_data->append_hist_item (h_obj); + + if (!excl_ok) + hist_data->get_callsite_mark ()->put (h_obj, 1); + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (subtype == Metric::INCLUSIVE && !incl_ok) + continue; + if (subtype == Metric::EXCLUSIVE && !excl_ok) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + } + } + } +} + +void +PathTree::get_self_metrics (Vector<Histable*> *objs, NodeIdx node_idx, + bool seen, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj = get_hist_obj (node); + obj_list[dpth] = cur_obj; + + bool match = false; + int nobj = objs->size (); + if (dpth + 1 >= nobj) + { + match = true; + for (int i = 0; i < nobj; ++i) + { + if (objs->fetch (i) != obj_list[dpth - nobj + 1 + i]) + { + match = false; + break; + } + } + } + + if (match) + { + Hist_data::HistItem *hi = hist_data->append_hist_item (cur_obj); + int incl_ok = !seen; + int excl_ok = 0; + if (IS_LEAF (node) || node == NODE_IDX (root_idx)) + excl_ok = 1; + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + switch (subtype) + { + case Metric::INCLUSIVE: + if (incl_ok && hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + case Metric::EXCLUSIVE: + case Metric::ATTRIBUTED: + if (excl_ok && hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + case Metric::DATASPACE: + if (hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + // ignoring the following cases (why?) + case Metric::STATIC: + break; + } + } + } + + if (dbeSession->is_interactive ()) + { + ndone++; + int new_percent = 95 * ndone / nodes; + if (new_percent > percent) + { + percent = new_percent; + theApplication->set_progress (percent, NULL); + } + } + + // Recursively process all descendants + int index; + int dsize = NUM_DESCENDANTS (node); + for (index = 0; index < dsize; index++) + get_self_metrics (objs, node->descendants->fetch (index), + seen || match, dpth + 1); +} + +void +PathTree::get_self_metrics (Vector<Histable*> *objs) +{ + get_self_metrics (objs, root_idx, false, 0); +} + +void +PathTree::get_self_metrics (Histable *obj, Vector<Function*> *funclist, + Vector<Histable*>* sel_objs) +{ + int excl_ok, incl_ok; + NodeIdx node_idx; + Node *node, *anc; + + if (obj == NULL) + return; + + SourceFile *src = NULL; + if (obj && obj->get_type () == Histable::LINE) + { + DbeLine *dbeline = (DbeLine*) obj; + src = dbeline->sourceFile; + } + + Hist_data::HistItem *hi = hist_data->append_hist_item (obj); + for (int i = 0, sz = funclist ? funclist->size () : 0; i < sz; i++) + { + Function *fitem = (Function*) get_compare_obj (funclist->fetch (i)); + node_idx = fn_map->get (fitem); + for (; node_idx; node_idx = node->funclist) + { + node = NODE_IDX (node_idx); + if (obj && obj->get_type () == Histable::LINE) + { + Histable *h = get_hist_obj (node, src); + if (h == NULL) + continue; + if (h->convertto (Histable::LINE) != obj->convertto (Histable::LINE)) + continue; + } + else if (get_hist_obj (node, src) != obj) + continue; + + // Check for recursion (inclusive metrics) + incl_ok = 1; + for (anc = NODE_IDX (node->ancestor); anc; + anc = NODE_IDX (anc->ancestor)) + { + if (get_hist_obj (anc, src) == obj) + { + incl_ok = 0; + break; + } + if (sel_objs != NULL) + for (int k = 0; k < sel_objs->size (); k++) + if (sel_objs->fetch (k) == get_hist_obj (anc, src)) + { + incl_ok = 0; + break; + } + } + + // Check for leaf nodes (exclusive metrics) + excl_ok = 0; + if (IS_LEAF (node) || node == NODE_IDX (root_idx)) + excl_ok = 1; + + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, ind_sz = mlist->size (); ind < ind_sz; ind++) + { + if (xlate[ind] == -1) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (subtype == Metric::INCLUSIVE && !incl_ok) + continue; + if (subtype == Metric::EXCLUSIVE && !excl_ok) + continue; + if (subtype == Metric::ATTRIBUTED && !excl_ok) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + } + } + } +} + +Vector<Histable*> * +PathTree::get_clr_instr (Histable * func) +{ + Vector<Histable*> * instrs = NULL; + if (func->get_type () != Histable::FUNCTION) + return NULL; + NodeIdx node_idx = fn_map->get ((Function*) func); + Node *node = NODE_IDX (node_idx); + if (node == NULL) + return new Vector<Histable*>(); + int instr_num = 0; + for (; node; node = NODE_IDX (node->funclist)) + instr_num++; + instrs = new Vector<Histable*>(instr_num); + node = NODE_IDX (node_idx); + Histable *instr = NODE_IDX (node->ancestor)->instr; + instr_num = 0; + instrs->store (instr_num, instr); + node = NODE_IDX (node->funclist); + for (; node; node = NODE_IDX (node->funclist)) + { + instr = NODE_IDX (node->ancestor)->instr; + instr_num++; + instrs->store (instr_num, instr); + } + return instrs; +} + +Vector<void*> * +PathTree::get_cle_instr (Histable * func, Vector<Histable*>*&instrs) +{ + if (func->get_type () != Histable::FUNCTION) + return NULL; + NodeIdx node_idx = fn_map->get ((Function*) func); + Node *node = NODE_IDX (node_idx); + if (node == NULL) + { + instrs = new Vector<Histable*>(); + return new Vector<void*>(); + } + int instr_num = 0; + for (; node; node = NODE_IDX (node->funclist)) + instr_num++; + instrs = new Vector<Histable*>(instr_num); + Vector<void*> *callee_info = new Vector<void*>(instr_num); + node = NODE_IDX (node_idx); + Histable *instr = node->instr; + instr_num = 0; + instrs->store (instr_num, instr); + int dec_num = 0; + NodeIdx dec_idx = 0; + if (NUM_DESCENDANTS (node) > 0) + { + Vector<Histable*> * callee_instrs = new Vector<Histable*>(node->descendants->size ()); + Vec_loop (NodeIdx, node->descendants, dec_num, dec_idx) + { + Node * dec_node = NODE_IDX (dec_idx); + //XXXX Note: there can be more than one instrs in one leaf function + callee_instrs->store (dec_num, dec_node->instr); + } + callee_info->store (instr_num, callee_instrs); + } + else + callee_info->store (instr_num, NULL); + node = NODE_IDX (node->funclist); + for (; node; node = NODE_IDX (node->funclist)) + { + instr = node->instr; + instr_num++; + instrs->store (instr_num, instr); + if (NUM_DESCENDANTS (node) > 0) + { + Vector<Histable*> * callee_instrs = new Vector<Histable*>(node->descendants->size ()); + Vec_loop (NodeIdx, node->descendants, dec_num, dec_idx) + { + Node * dec_node = NODE_IDX (dec_idx); + //XXXX Note: there can be more than one instrs in one leaf function + callee_instrs->store (dec_num, dec_node->instr); + } + callee_info->store (instr_num, callee_instrs); + } + else + callee_info->store (instr_num, NULL); + } + return callee_info; +} + +// +// +// The following methods are used for debugging purpose only. +// +// +static int maxdepth; +static int maxwidth; + +void +PathTree::print (FILE *fd) +{ + (void) reset (); + fprintf (fd, NTXT ("n = %lld, dn = %lld, MD = %lld\n\n"), + (long long) nodes, (long long) dnodes, (long long) depth); + maxdepth = 0; + maxwidth = 0; + print (fd, root, 0); + fprintf (fd, NTXT ("md = %lld, mw = %lld\n"), + (long long) maxdepth, (long long) maxwidth); +} + +void +PathTree::print (FILE *fd, PathTree::Node *node, int lvl) +{ + const char *t; + char *n; + if (lvl + 1 > maxdepth) + maxdepth = lvl + 1; + for (int i = 0; i < lvl; i++) + fprintf (fd, NTXT ("-")); + Histable *instr = node->instr; + if (instr->get_type () == Histable::LINE) + { + t = "L"; + n = ((DbeLine *) instr)->func->get_name (); + } + else if (instr->get_type () == Histable::INSTR) + { + t = "I"; + n = ((DbeInstr *) instr)->func->get_name (); + } + else + { + t = "O"; + n = instr->get_name (); + } + long long addr = (long long) instr->get_addr (); + fprintf (fd, NTXT ("%s %s (0x%08llx) -- ndesc = %lld\n"), + t, n, addr, (long long) (NUM_DESCENDANTS (node))); + + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + if (dsize > maxwidth) + maxwidth = dsize; + for (int index = 0; index < dsize; index++) + print (fd, NODE_IDX (node->descendants->fetch (index)), lvl + 1); +} + +void +PathTree::printn (FILE *fd) +{ + int n = dbg_nodes (root); + fprintf (fd, GTXT ("Number of nodes: %d, total size: %d\n"), n, (int) (n * sizeof (Node))); +} + +void +PathTree::dumpNodes (FILE *fd, Histable *obj) +{ + const char *t; + char *n; + NodeIdx node_idx = fn_map->get ((Function*) obj); + Node *node = NODE_IDX (node_idx); + if (node == NULL) + { + fprintf (fd, GTXT ("No nodes associated with %s\n"), obj->get_name ()); + return; + } + Histable *instr = node->instr; + for (; node; node = NODE_IDX (node->funclist)) + { + instr = node->instr; + if (instr->get_type () == Histable::LINE) + { + t = "L"; + n = ((DbeLine *) instr)->func->get_name (); + } + else if (instr->get_type () == Histable::INSTR) + { + t = "I"; + n = ((DbeInstr *) instr)->func->get_name (); + } + else + { + t = "O"; + n = instr->get_name (); + } + long long addr = (long long) instr->get_addr (); + if (addr <= 0xFFFFFFFFU) + fprintf (fd, NTXT ("0x%08x -- %s %s\n"), (uint32_t) addr, t, n); + else + fprintf (fd, NTXT ("0x%016llX -- %s %s\n"), addr, t, n); + } +} + +int +PathTree::dbg_nodes (PathTree::Node *node) +{ + int res = 1; + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + res += dbg_nodes (NODE_IDX (node->descendants->fetch (index))); + return res; +} + +static int mind_g; + +int +leak_alloc_comp (const void *s1, const void *s2) +{ + // See Hist_data::sort_compare() for duplicate code + int result = 0; + CStack_data::CStack_item *t1, *t2; + t1 = *(CStack_data::CStack_item **)s1; + t2 = *(CStack_data::CStack_item **)s2; + + switch (t1->value[mind_g].tag) + { + case VT_INT: + if (t1->value[mind_g].i < t2->value[mind_g].i) + result = -1; + else if (t1->value[mind_g].i > t2->value[mind_g].i) + result = 1; + else + result = 0; + break; + case VT_LLONG: + if (t1->value[mind_g].ll < t2->value[mind_g].ll) + result = -1; + else if (t1->value[mind_g].ll > t2->value[mind_g].ll) + result = 1; + else + result = 0; + break; + case VT_ULLONG: + if (t1->value[mind_g].ull < t2->value[mind_g].ull) + result = -1; + else if (t1->value[mind_g].ull > t2->value[mind_g].ull) + result = 1; + else + result = 0; + break; + // ignoring the following cases (why?) + case VT_SHORT: + case VT_FLOAT: + case VT_DOUBLE: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + // Sort in descending order + return -result; +} + +CStack_data * +PathTree::get_cstack_data (MetricList *mlist) +{ + (void) reset (); + CStack_data *lam = new CStack_data (mlist); + int nmetrics = mlist->get_items ()->size (); + mind_g = -1; + xlate = new int[nmetrics]; + for (int mind = 0; mind < nmetrics; mind++) + { + xlate[mind] = -1; + Metric *mtr = mlist->get_items ()->fetch (mind); + if (mlist->get_sort_ref_index () == mind) + mind_g = mind; + xlate[mind] = find_slot (mtr->get_id ()); + } + + // now fill in the actual data + obj_list = new Histable*[depth]; + get_cstack_list (lam, root_idx, 0); + delete[] obj_list; + + if (mind_g >= 0) + lam->cstack_items->sort (leak_alloc_comp); + delete[] xlate; + return lam; +} + +void +PathTree::get_cstack_list (CStack_data *lam, NodeIdx node_idx, int dpth) +{ + + Node *node = NODE_IDX (node_idx); + obj_list[dpth] = node->instr; + + CStack_data::CStack_item *item = NULL; + if (IS_LEAF (node)) + item = lam->new_cstack_item (); + int nmetrics = lam->metrics->get_items ()->size (); + bool subtree_empty = true; + + for (int mind = 0; mind < nmetrics; mind++) + { + if (xlate[mind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[mind]], node_idx)) + continue; + else + subtree_empty = false; + if (item) + { + ADD_METRIC_VAL (item->value[mind], slots[xlate[mind]], node_idx); + ADD_METRIC_VAL (lam->total->value[mind], slots[xlate[mind]], node_idx); + } + } + + if (subtree_empty) + { + delete item; + return; + } + + if (item) + { + // Finish processing a leaf node + item->stack = new Vector<DbeInstr*>(dpth); + for (int i = 1; i <= dpth; i++) + item->stack->append ((DbeInstr*) obj_list[i]); + lam->cstack_items->append (item); + } + else + { + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + get_cstack_list (lam, node->descendants->fetch (index), dpth + 1); + } +} + +Emsg * +PathTree::fetch_stats () +{ + if (statsq == NULL) + return NULL; + return statsq->fetch (); +} + +void +PathTree::delete_stats () +{ + if (statsq != NULL) + { + delete statsq; + statsq = new Emsgqueue (NTXT ("statsq")); + } +} + +Emsg * +PathTree::fetch_warnings () +{ + if (warningq == NULL) + return NULL; + return warningq->fetch (); +} + +void +PathTree::delete_warnings () +{ + if (warningq != NULL) + { + delete warningq; + warningq = new Emsgqueue (NTXT ("warningq")); + } +} diff --git a/gprofng/src/PathTree.h b/gprofng/src/PathTree.h new file mode 100644 index 0000000..dc3ad1f --- /dev/null +++ b/gprofng/src/PathTree.h @@ -0,0 +1,405 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _PATH_TREE_H +#define _PATH_TREE_H + +#include <vec.h> +#include <Map.h> + +#include "dbe_structs.h" +#include "Hist_data.h" +#include "Histable.h" +#include "Metric.h" + +typedef enum +{ + NORMAL = 0, CANCELED +} PtreePhaseStatus; + +class PathTree +{ +public: + + PathTree (DbeView *_dbev, int _indxtype = -1) + { + construct (_dbev, _indxtype, PATHTREE_MAIN); + } + + ~PathTree (); + + static void make_deltas (int vtype, TValue *v1, TValue *v2); + static void make_ratios (int vtype, TValue *v1, TValue *v2); + + typedef enum + { + COMPUTEOPT_NONE = 0, + COMPUTEOPT_OMP_CALLEE + } PtreeComputeOption; + + Hist_data *compute_metrics (MetricList *, Histable::Type, + Hist_data::Mode, Vector<Histable*>*, + Histable*, Vector<Histable*>* sel_objs = NULL, + PtreeComputeOption flag = COMPUTEOPT_NONE); + // Get aggregated callstack data + CStack_data *get_cstack_data (MetricList *); + + Vector<Histable*> *get_clr_instr (Histable *); + Vector<void*> *get_cle_instr (Histable *, Vector<Histable*>*&); + + int + get_status () + { + return status; + } + + int + get_depth () + { + return depth; + } + + int + getStackProp () + { + return stack_prop; + } + + typedef long NodeIdx; + + struct Node + { + inline void + reset () + { + ancestor = 0; + descendants = NULL; + instr = NULL; + funclist = 0; + } + + NodeIdx ancestor; + Vector<NodeIdx> *descendants; + Histable *instr; + NodeIdx funclist; + }; + + static const int CHUNKSZ = 16384; + + inline Node * + NODE_IDX (NodeIdx idx) + { + return idx ? &chunks[idx / CHUNKSZ][idx % CHUNKSZ] : NULL; + } + + // queue for messages (statistics for pathtree processing) + Emsg *fetch_stats (void); // fetch the queue of comment messages + void delete_stats (void); // delete the queue of stats messages + Emsg *fetch_warnings (void); // fetch the queue of warnings messages + void delete_warnings (void); // delete the queue of warnings messages + + NodeIdx + get_func_nodeidx (Function * func) + { + return fn_map == NULL ? (NodeIdx) 0 : fn_map->get (func); + } + + void print (FILE *); + void dumpNodes (FILE *, Histable *); + + // flame charts functions - get values from ftree_internal + int get_ftree_depth (); // Depth of tree + Vector<void*>* get_ftree_level (BaseMetric *bm, int dpth); + Vector<void*>* get_ftree_node_children (BaseMetric *bm, NodeIdx node_idx); + Vector<Function*>* get_ftree_funcs (); + Vector<Function*>* get_funcs (); // Unique functions in tree + +private: + + enum + { + MAX_DESC_HTABLE_SZ = 65535 + }; + + typedef struct hash_node + { + NodeIdx nd; + struct hash_node *next; + } hash_node_t; + + int desc_htable_size; + int desc_htable_nelem; + hash_node_t **descHT; + + struct Slot + { + int id; + ValueTag vtype; + union + { + int **mvals; + int64_t **mvals64; + }; + }; + + typedef enum + { + PATHTREE_MAIN = 0, + PATHTREE_INTERNAL_OMP, + PATHTREE_INTERNAL_FUNCTREE + } PathTreeType; + + DbeView *dbev; + int indxtype; + int stack_prop; + Expression *indx_expr; + Histable *total_obj; + Map<Function*, NodeIdx> *fn_map; + Map<uint64_t, NodeIdx> *pathMap; + Map<uint64_t, uint64_t> *hideMap; + int status; + NodeIdx root_idx; + Node *root; + int depth; + long nodes; + long dnodes; + long nchunks; + Node **chunks; + int nslots; + Slot *slots; + int phaseIdx; + int nexps; + Emsgqueue *statsq; + Emsgqueue *warningq; + Hist_data *hist_data; + int percent; + int ndone; + Histable **obj_list; + Node **node_list; + int *xlate; + bool cancel_ok; + PathTreeType pathTreeType; + PathTree *ptree_internal; + PathTree *ftree_internal; // function-based pathtree + bool ftree_needs_update; + Vector<Vector<NodeIdx>*> *depth_map; // for each depth level, list of nodes + + void init (); + void fini (); + PtreePhaseStatus reset (); + PtreePhaseStatus add_experiment (int); + PtreePhaseStatus process_packets (Experiment*, DataView*, int); + DataView *get_filtered_events (int exp_index, int data_type); + void construct (DbeView *_dbev, int _indxtype, PathTreeType _pathTreeType); + + PathTree (DbeView *_dbev, int _indxtype, PathTreeType _pathTreeType) + { + construct (_dbev, _indxtype, _pathTreeType); + } + + inline int * + allocate_chunk (int **p, NodeIdx idx) + { + int *res = new int[CHUNKSZ]; + for (int i = 0; i < CHUNKSZ; i++) + res[i] = 0; + p[idx] = res; + return res; + }; + + inline int64_t * + allocate_chunk (int64_t **p, NodeIdx idx) + { + int64_t *res = new int64_t[CHUNKSZ]; + for (int i = 0; i < CHUNKSZ; i++) + res[i] = 0; + p[idx] = res; + return res; + }; + + inline Node * + allocate_chunk (Node **p, NodeIdx idx) + { + Node *res = new Node[CHUNKSZ]; + for (int i = 0; i < CHUNKSZ; i++) + res[i].reset (); + p[idx] = res; + return res; + }; + + inline bool + IS_MVAL_ZERO (Slot& slot, NodeIdx idx) + { + if (slot.vtype == VT_LLONG || slot.vtype == VT_ULLONG) + { + int64_t *tmp = slot.mvals64[idx / CHUNKSZ]; + return tmp ? tmp[idx % CHUNKSZ] == 0 : true; + } + else + { + int *tmp = slot.mvals[idx / CHUNKSZ]; + return tmp ? tmp[idx % CHUNKSZ] == 0 : true; + } + } + + inline void + ASN_METRIC_VAL (TValue& v, Slot& slot, NodeIdx idx) + { + if (slot.vtype == VT_LLONG) + { + int64_t *tmp = slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ll = tmp[idx % CHUNKSZ]; + } + else if (slot.vtype == VT_ULLONG) + { + uint64_t *tmp = (uint64_t *) slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ull = tmp[idx % CHUNKSZ]; + } + else + { + int *tmp = slot.mvals[idx / CHUNKSZ]; + if (tmp) + v.i = tmp[idx % CHUNKSZ]; + } + } + + inline void + ADD_METRIC_VAL (TValue& v, Slot& slot, NodeIdx idx) + { + if (slot.vtype == VT_LLONG) + { + int64_t *tmp = slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ll += tmp[idx % CHUNKSZ]; + } + else if (slot.vtype == VT_ULLONG) + { + uint64_t *tmp = (uint64_t *) slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ull += tmp[idx % CHUNKSZ]; + } + else + { + int *tmp = slot.mvals[idx / CHUNKSZ]; + if (tmp) v.i += tmp[idx % CHUNKSZ]; + } + } + + inline void + SUB_METRIC_VAL (TValue& v, Slot& slot, NodeIdx idx) + { + if (slot.vtype == VT_LLONG) + { + int64_t *tmp = slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ll -= tmp[idx % CHUNKSZ]; + } + else if (slot.vtype == VT_ULLONG) + { + uint64_t *tmp = (uint64_t *) slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ull -= tmp[idx % CHUNKSZ]; + } + else + { + int *tmp = slot.mvals[idx / CHUNKSZ]; + if (tmp) + v.i -= tmp[idx % CHUNKSZ]; + } + } + + inline void + INCREMENT_METRIC (Slot *slot, NodeIdx idx, int64_t val) + { + if (slot->vtype == VT_LLONG) + { + int64_t *tmp = slot->mvals64[idx / CHUNKSZ]; + if (tmp == NULL) + tmp = allocate_chunk (slot->mvals64, idx / CHUNKSZ); + tmp[idx % CHUNKSZ] += val; + } + else if (slot->vtype == VT_ULLONG) + { + uint64_t *tmp = (uint64_t *) slot->mvals64[idx / CHUNKSZ]; + if (tmp == NULL) + tmp = (uint64_t *) allocate_chunk (slot->mvals64, idx / CHUNKSZ); + tmp[idx % CHUNKSZ] += val; + } + else + { + int *tmp = slot->mvals[idx / CHUNKSZ]; + if (tmp == NULL) + tmp = allocate_chunk (slot->mvals, idx / CHUNKSZ); + tmp[idx % CHUNKSZ] += (int) val; + } + } + + inline Slot * + SLOT_IDX (int idx) + { + if (idx < 0 || idx >= nslots) + return NULL; + return &slots[idx]; + } + + int allocate_slot (int id, ValueTag vtype); + void allocate_slots (Slot *slots, int nslots); + int find_slot (int); + NodeIdx new_Node (NodeIdx, Histable*, bool); + NodeIdx find_path (Experiment*, DataView*, long); + NodeIdx find_desc_node (NodeIdx, Histable*, bool); + NodeIdx find_in_desc_htable (NodeIdx, Histable*, bool); + Histable *get_hist_obj (Node *, Histable* = NULL); + Histable *get_hist_func_obj (Node *); + Histable *get_compare_obj (Histable *obj); + void get_metrics (NodeIdx, int); + void get_metrics (Vector<Function*> *, Histable *); + void get_clr_metrics (Vector<Histable*>*, NodeIdx, int, int); + void get_clr_metrics (Vector<Histable*>*); + void get_cle_metrics (Vector<Histable*>*, NodeIdx, int, int, int); + void get_cle_metrics (Vector<Histable*>*, NodeIdx, int); + void get_cle_metrics (Vector<Histable*>*); + void get_self_metrics (Vector<Histable*>*, NodeIdx, bool, int); + void get_self_metrics (Vector<Histable*>*); + void get_self_metrics (Histable *, Vector<Function*> *funclist, + Vector<Histable*>* sel_objs = NULL); + void get_cstack_list (CStack_data *, NodeIdx, int); + + // Generate PathTree based on Functions instead of Instructions // Used for flame chart + void ftree_reset (); + void ftree_build (PathTree *mstr); + void ftree_build (PathTree *mstr, NodeIdx mstr_node_idx, NodeIdx local_node_idx); + void depth_map_build (); + void depth_map_build (NodeIdx node_idx, int depth); + Vector<void*>* get_level (BaseMetric *bm, int dpth); + Vector<void*>* get_nodes (BaseMetric *bm, Vector<NodeIdx> *node_idxs); + Vector<void*>* get_node_children (BaseMetric *bm, NodeIdx node_idx); + bool ftree_debug_match_hist_data (Hist_data *data, Hist_data *data_tmp); + void ftree_dump (); + + // Debugging functions + void print (FILE *, PathTree::Node*, int); + void printn (FILE *); + int dbg_nodes (PathTree::Node*); +}; + +#endif /* _PATH_TREE_H */ diff --git a/gprofng/src/PreviewExp.cc b/gprofng/src/PreviewExp.cc new file mode 100644 index 0000000..553afd3 --- /dev/null +++ b/gprofng/src/PreviewExp.cc @@ -0,0 +1,113 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> + +#include "PreviewExp.h" +#include "Data_window.h" +#include "DbeSession.h" +#include "Emsg.h" +#include "Print.h" +#include "i18n.h" + +PreviewExp::PreviewExp (): Experiment () { } + +PreviewExp::~PreviewExp () { }//~PreviewExp + +Experiment::Exp_status +PreviewExp::experiment_open (char *path) +{ + // Find experiment directory + if ((status = find_expdir (path)) != SUCCESS) + { + size_t len = strlen (path); + is_group = ((len > 4) && !strcmp (&path[len - 4], NTXT (".erg"))); + return status; + } + else + is_group = 0; + + read_log_file (); + if (status == FAILURE) + return status; + + if (status == INCOMPLETE && resume_ts != MAX_TIME) + // experiment is incomplete and "resumed" (non-paused) + // PreviewExp does not process all the packets, therefore... + // ... last_event does not reflect reality + // ... we don't know the duration or the end. + last_event = ZERO_TIME; // mark last_event as uninitialized + + read_notes_file (); + return status; +} + +Vector<char*> * +PreviewExp::preview_info () +{ + Vector<char*> *info = new Vector<char*>; + if (is_group) + info->append (GTXT ("Experiment Group")); + else + info->append (GTXT ("Experiment")); + info->append (expt_name); + + if (status == FAILURE /* != SUCCESS */) + { + if (is_group) + { + Vector<char*> *grp_list = dbeSession->get_group_or_expt (expt_name); + for (int i = 0, grp_sz = grp_list->size (); i < grp_sz; i++) + { + char *nm = grp_list->fetch (i); + char *str = dbe_sprintf (GTXT ("Exp.#%d"), i + 1); + info->append (str); + info->append (nm); + } + delete grp_list; + } + else + { + info->append (GTXT ("Error message")); + info->append (mqueue_str (errorq, GTXT ("No errors\n"))); + } + return info; + } + info->append (GTXT ("Experiment header")); + info->append (mqueue_str (commentq, GTXT ("Empty header\n"))); + info->append (GTXT ("Error message")); + info->append (mqueue_str (errorq, GTXT ("No errors\n"))); + info->append (GTXT ("Warning message")); + info->append (mqueue_str (warnq, GTXT ("No warnings\n"))); + info->append (GTXT ("Notes")); + info->append (mqueue_str (notesq, GTXT ("\n"))); + return info; +} + +char * +PreviewExp::mqueue_str (Emsgqueue *msgqueue, char *null_str) +{ + char *mesgs = pr_mesgs (msgqueue->fetch (), null_str, ""); + char *last = mesgs + strlen (mesgs) - 1; + if (*last == '\n') + *last = '\0'; + return mesgs; +} diff --git a/gprofng/src/PreviewExp.h b/gprofng/src/PreviewExp.h new file mode 100644 index 0000000..8b7834a --- /dev/null +++ b/gprofng/src/PreviewExp.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _PREVIEW_EXP_H +#define _PREVIEW_EXP_H + +#include "Experiment.h" +#include "vec.h" + +class PreviewExp : public Experiment +{ +public: + PreviewExp (); + ~PreviewExp (); + + virtual Exp_status experiment_open (char *path); + + Vector<char*> *preview_info (); + + char * + getArgList () + { + return uarglist; + } + +private: + char *mqueue_str (Emsgqueue *msgqueue, char *null_str); + + int is_group; +}; + +#endif /* ! _PREVIEW_EXP_H */ diff --git a/gprofng/src/Print.cc b/gprofng/src/Print.cc new file mode 100644 index 0000000..d6662df --- /dev/null +++ b/gprofng/src/Print.cc @@ -0,0 +1,3485 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <libintl.h> +//#include <libgen.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "util.h" +#include "Dbe.h" +#include "StringBuilder.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "Settings.h" +#include "Print.h" +#include "DbeView.h" +#include "Experiment.h" +#include "MetricList.h" +#include "Module.h" +#include "Function.h" +#include "DataSpace.h" +#include "DataObject.h" +#include "FilterExp.h" +#include "LoadObject.h" +#include "Emsg.h" +#include "Table.h" +#include "DbeFile.h" +#include "CallStack.h" + +int +er_print_common_display::open (Print_params *params) +{ + pr_params = *params; + pr_params.name = dbe_strdup (params->name); + if (params->dest == DEST_PRINTER) + { + tmp_file = dbeSession->get_tmp_file_name (NTXT ("print"), false); + dbeSession->tmp_files->append (strdup (tmp_file)); + out_file = fopen (tmp_file, NTXT ("w")); + } + else if (params->dest == DEST_OPEN_FILE) + out_file = pr_params.openfile; + else + out_file = fopen (pr_params.name, NTXT ("w")); + + if (out_file == NULL) + // Failure + return 1; + return 0; +} + +bool +er_print_common_display::print_output () +{ + char *sys_call; + bool ret = true; + if (pr_params.dest != DEST_OPEN_FILE) + fclose (out_file); + + if (pr_params.dest == DEST_PRINTER) + { + if (streq ((char *) pr_params.name, NTXT (""))) + sys_call = dbe_sprintf ("(/usr/bin/lp -c -n%d %s) 2>/dev/null 1>&2", + pr_params.ncopies, tmp_file); + else + sys_call = dbe_sprintf ("(/usr/bin/lp -c -d%s -n%d %s) 2>/dev/null 1>&2", + pr_params.name, pr_params.ncopies, tmp_file); + if (system (sys_call) != 0) + ret = false; + unlink (tmp_file); + free (sys_call); + } + + return ret; +} + +// Return the report. If the report size is greater than max, return truncated report +// Allocates memory, so the caller should free this memory. + +char * +er_print_common_display::get_output (int maxsize) +{ + off_t max = (off_t) maxsize; + if (out_file != (FILE *) NULL) + { + fclose (out_file); // close tmp_file + out_file = (FILE *) NULL; + } + struct stat sbuf; + int st = stat (tmp_file, &sbuf); + if (st == 0) + { + off_t sz = sbuf.st_size; + if (sz > max) + return dbe_sprintf (GTXT ("Error: report is too long.\n")); + if (sz <= 0) + return dbe_sprintf (GTXT ("Error: empty temporary file: %s\n"), + tmp_file); + max = sz; + } + + FILE *f = fopen (tmp_file, "r"); + if (f == NULL) + return dbe_sprintf (GTXT ("Error: cannot open temporary file: %s\n"), + tmp_file); + char *report = (char *) malloc (max); + if (report) + { + if (1 != fread (report, max - 1, 1, f)) + { + fclose (f); + free (report); + return dbe_sprintf (GTXT ("Error: cannot read temporary file: %s\n"), + tmp_file); + } + report[max - 1] = 0; + } + fclose (f); + return report; +} + +void +er_print_common_display::header_dump (int exp_idx) +{ + if (load && (exp_idx == exp_idx1)) + { + load = false; + print_load_object (out_file); + } + print_header (dbeSession->get_exp (exp_idx), out_file); +} + +char * +pr_load_objects (Vector<LoadObject*> *loadobjects, char *lead) +{ + int size, i; + LoadObject *lo; + Emsg *m; + char *msg; + StringBuilder sb; + char *lo_name; + size = loadobjects->size (); + for (i = 0; i < size; i++) + { + lo = loadobjects->fetch (i); + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + continue; + } + + // print the segment name + sb.append (lead); + sb.append (NTXT (" ")); + sb.append (lo->get_name ()); + sb.append (NTXT (" (")); + sb.append (lo->get_pathname ()); + sb.append (NTXT (")\n")); + + // and any warnings + m = lo->fetch_warnings (); + if (m != NULL) + { + msg = pr_mesgs (m, NULL, NTXT (" ")); + sb.append (msg); + free (msg); + } + } + return sb.toString (); +} + +char * +pr_mesgs (Emsg *msg, const char *null_str, const char *lead) +{ + Emsg *m; + StringBuilder sb; + if (msg == NULL) + return dbe_strdup (null_str); + for (m = msg; m; m = m->next) + { + sb.append (lead); + sb.append (m->get_msg ()); + sb.append (NTXT ("\n")); + } + return sb.toString (); +} + +void +print_load_object (FILE *out_file) +{ + Vector<LoadObject*> *loadobjects = dbeSession->get_text_segments (); + char *msg = pr_load_objects (loadobjects, NTXT ("\t")); + fprintf (out_file, GTXT ("Load Object Coverage:\n")); + fprintf (out_file, NTXT ("%s"), msg); + fprintf (out_file, + "----------------------------------------------------------------\n"); + free (msg); + delete loadobjects; +} + +void +print_header (Experiment *exp, FILE *out_file) +{ + fprintf (out_file, GTXT ("Experiment: %s\n"), exp->get_expt_name ()); + char *msg = pr_mesgs (exp->fetch_notes (), NTXT (""), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); + + msg = pr_mesgs (exp->fetch_errors (), GTXT ("No errors\n"), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); + + msg = pr_mesgs (exp->fetch_warnings (), GTXT ("No warnings\n"), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); + + msg = pr_mesgs (exp->fetch_comments (), NTXT (""), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); + + msg = pr_mesgs (exp->fetch_pprocq (), NTXT (""), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); +} + +void +get_width (Hist_data *data, + MetricList *metrics_list, Metric::HistMetric *hist_metric) +{ + Metric *mitem; + Metric::HistMetric *hitem; + int last_column; + int index; + + // find the last visible column. + last_column = 0; + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + if (mitem->is_visible () || mitem->is_tvisible () || mitem->is_pvisible ()) + last_column = index; + } + + // find the width for each column. + + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + hitem = &hist_metric[index]; + + if (mitem->is_visible ()) + { + if (mitem->get_vtype () == VT_LABEL) + { + if (index == last_column) + hitem->maxvalue_width = 0; + else + hitem->maxvalue_width = data->name_maxlen (); + // truncate names which will be too long + if (hitem->maxvalue_width > MAX_LEN - 3) + hitem->maxvalue_width = MAX_LEN - 3; + } + else if (mitem->get_vtype () == VT_ADDRESS) + { + hitem->maxvalue_width = data->value_maxlen (index); + if (hitem->maxvalue_width < 13) + hitem->maxvalue_width = 13; + } + else + hitem->maxvalue_width = data->value_maxlen (index); + } + else + hitem->maxvalue_width = 0; + + if (mitem->is_tvisible ()) + { + if (mitem->get_visbits () & VAL_RATIO) + hitem->maxtime_width = data->value_maxlen (index); + else + hitem->maxtime_width = data->time_maxlen (index, + dbeSession->get_clock (-1)); + } + else + { + hitem->maxtime_width = 0; + } + } +} + +void +get_format (char **fmt_int, char **fmt_real0, char **fmt_real1, + MetricList *metrics_list, Metric::HistMetric *hist_metric, + int nspace) +{ + Metric *mitem; + Metric::HistMetric *hitem; + int index; + int visible, tvisible, pvisible; + size_t maxlen; + bool prev; + char numstr[MAX_LEN], pstr_int[MAX_LEN], + pstr_real0[MAX_LEN], pstr_real1[MAX_LEN]; + + // find the width for each column. + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + hitem = &hist_metric[index]; + visible = mitem->is_visible (); + tvisible = mitem->is_tvisible (); + pvisible = mitem->is_pvisible (); + *pstr_int = *pstr_real0 = *pstr_real1 = '\0'; + + // Get 'Show Value' format + const char *sign = (mitem->get_visbits () & VAL_DELTA) ? "+" : ""; + if (visible) + { + maxlen = hitem->maxvalue_width; + switch (mitem->get_vtype2 ()) + { + case VT_DOUBLE: + if (mitem->get_visbits () & VAL_RATIO) + { + snprintf (numstr, sizeof (numstr), "x %%#%d.0lf ", + (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "x%%s%%%d.3lf ", + (int) maxlen); + } + else + { + snprintf (numstr, sizeof (numstr), "%%#%s%d.0lf ", sign, + (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "%%s%%%s%d.3lf ", + sign, (int) maxlen); + } + break; + case VT_INT: + snprintf (pstr_int, sizeof (pstr_int), "%%%s%dd ", sign, + (int) maxlen); + break; + case VT_LLONG: + snprintf (pstr_int, sizeof (pstr_int), "%%%s%dlld ", sign, + (int) maxlen); + break; + case VT_ULLONG: + snprintf (pstr_int, sizeof (pstr_int), "%%%s%dllu ", sign, + (int) maxlen); + break; + case VT_ADDRESS: + if (maxlen <= 13) + { + snprintf (pstr_int, sizeof (pstr_int), "%%%dd:0x%%08x", 2); + } + else + { + snprintf (pstr_int, sizeof (pstr_int), "%%%dd:0x%%08x", + (int) (maxlen - 13)); + } + break; + case VT_FLOAT: + snprintf (numstr, sizeof (numstr), "%%#%d.0f ", + (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "%%%d.3f ", + (int) maxlen); + break; + case VT_SHORT: + snprintf (pstr_int, sizeof (pstr_int), "%%%dhu ", (int) maxlen); + break; + case VT_LABEL: + if (maxlen == 0) // last column + snprintf (pstr_int, sizeof (pstr_int), NTXT ("%%s%%s")); + else if (maxlen + nspace >= MAX_LEN - 3) + snprintf (pstr_int, sizeof (pstr_int), NTXT ("%%s%%-%d.%ds "), + MAX_LEN - 7, MAX_LEN - 7); + else + snprintf (pstr_int, sizeof (pstr_int), NTXT ("%%s%%-%ds "), + (int) (maxlen + nspace)); + break; + default: + break; + } + } + + // Get 'Show Time' format + if (tvisible) + { + maxlen = hitem->maxtime_width; + if (mitem->get_visbits () & VAL_RATIO) + { + snprintf (numstr, sizeof (numstr), " %%%s#%d.0lf ", + sign, (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "%%s%%%s%d.3lf ", + sign, (int) maxlen); + } + else + { + snprintf (numstr, sizeof (numstr), "%%%s#%d.0lf ", + sign, (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "%%s%%%s%d.3lf ", + sign, (int) maxlen); + } + } + + // Copy format + if (*pstr_int) + fmt_int[index] = dbe_strdup (pstr_int); + else + fmt_int[index] = NULL; + + if (*pstr_real0) + fmt_real0[index] = dbe_strdup (pstr_real0); + else + fmt_real0[index] = NULL; + + if (*pstr_real1) + fmt_real1[index] = dbe_strdup (pstr_real1); + else + fmt_real1[index] = NULL; + + // Set total width + hitem->width = 0; + if (hitem->maxvalue_width > 0) + { + hitem->width += hitem->maxvalue_width; + prev = true; + } + else + prev = false; + + if (hitem->maxtime_width > 0) + { + if (prev) + hitem->width++; + hitem->width += hitem->maxtime_width; + prev = true; + } + + if (pvisible) + { + if (prev) + hitem->width++; + hitem->width += 6; // adjust to change format from xx.yy% + } + if (visible || tvisible || pvisible) + mitem->legend_width (hitem, 2); + } +} + +static char * +delTrailingBlanks (char *s) +{ + for (int i = (int) strlen (s) - 1; i >= 0 && s[i] == ' '; i--) + s[i] = 0; + return s; +} + +/** + * Print the 3-line header with column heads for the metrics + * Return offset of "Name" column (this is needed to print Callers-Callees) + */ +int +print_label (FILE *out_file, MetricList *metrics_list, + Metric::HistMetric *hist_metric, int space) +{ + char line0[2 * MAX_LEN], line1[2 * MAX_LEN]; + char line2[2 * MAX_LEN], line3[2 * MAX_LEN]; + int name_offset = 0; + *line0 = *line1 = *line2 = *line3 = '\0'; + Vector<Metric*> *mlist = metrics_list->get_items (); + for (int index = 0, mlist_sz = mlist->size (); index < mlist_sz; index++) + { + Metric *mitem = mlist->fetch (index); + if (mitem->is_visible () || mitem->is_tvisible () || mitem->is_pvisible ()) + { + Metric::HistMetric *hitem = hist_metric + index; + char *fmt; + if (index > 0 && mitem->get_type () == Metric::ONAME) + { + fmt = NTXT (" %-*s"); + name_offset = strlen (line1); + } + else + fmt = NTXT ("%-*s"); + int width = (int) hitem->width; + size_t len = strlen (line1); + snprintf (line1 + len, sizeof (line1) - len, fmt, width, + hitem->legend1); + len = strlen (line2); + snprintf (line2 + len, sizeof (line2) - len, fmt, width, + hitem->legend2); + len = strlen (line3); + snprintf (line3 + len, sizeof (line3) - len, fmt, width, + hitem->legend3); + len = strlen (line0); + snprintf (line0 + len, sizeof (line0) - len, fmt, width, + mitem->legend ? mitem->legend : NTXT ("")); + } + } + char *s = delTrailingBlanks (line0); + if (*s) + fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), s); + fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), delTrailingBlanks (line1)); + fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), delTrailingBlanks (line2)); + fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), delTrailingBlanks (line3)); + return name_offset; +} + +static int +print_one_visible (FILE *out_file, char *fmt_int, char *fmt_real0, char *fmt_real1, + TValue *value, int visbits) +{ + int nc = 0; + switch (value->tag) + { + case VT_DOUBLE: + if (value->d == 0.0) + nc = fprintf (out_file, fmt_real0); + else + { + if (visbits & VAL_RATIO) + { + if (value->d > 99.999) + nc = fprintf (out_file, fmt_real1, NTXT (">"), 99.999); + else + nc = fprintf (out_file, fmt_real1, NTXT (" "), value->d); + } + else + nc = fprintf (out_file, fmt_real1, NTXT (""), value->d); + } + break; + case VT_INT: + nc = fprintf (out_file, fmt_int, value->i); + break; + case VT_LLONG: + case VT_ULLONG: + nc = fprintf (out_file, fmt_int, value->ll); + break; + case VT_ADDRESS: + nc = fprintf (out_file, fmt_int, ADDRESS_SEG (value->ll), + ADDRESS_OFF (value->ll)); + break; + case VT_FLOAT: + if (value->f == 0.0) + nc = fprintf (out_file, fmt_real0); + else + nc = fprintf (out_file, fmt_real1, value->f); + break; + case VT_SHORT: + nc = fprintf (out_file, fmt_int, value->s); + break; + // ignoring the following cases (why?) + case VT_HRTIME: + case VT_LABEL: + case VT_OFFSET: + break; + } + return nc; +} + +static int +print_one_tvisible (FILE *out_file, char *fmt_real0, char *fmt_real1, + TValue *value, int visbits, int clock) +{ + int nc; + if (value->ll == 0LL) + nc = fprintf (out_file, fmt_real0); + else + { + if (visbits & VAL_RATIO) + { + if (value->d > 99.999) + nc = fprintf (out_file, fmt_real1, NTXT (">"), 99.999); + else + nc = fprintf (out_file, fmt_real1, NTXT (" "), value->d); + } + else + nc = fprintf (out_file, fmt_real1, "", 1.e-6 * value->ll / clock); + } + return nc; +} + +static void +print_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + char **fmt_int, char **fmt_real0, char **fmt_real1, + MetricList *metrics_list, Metric::HistMetric *hist_metric, + char *mark, Histable::NameFormat nfmt) +{ + Metric *mitem; + Metric::HistMetric *hitem; + int index, nc, np, i; + int visible, tvisible, pvisible; + TValue *value; + double percent; + + if (item->type == Module::AT_EMPTY) + { + fprintf (out_file, nl); + return; + } + + // set name_is_Total + int name_is_Total = 0; + + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + if (mitem->get_type () != Metric::ONAME) + continue; + name_is_Total = strcmp (item->obj->get_name (), GTXT ("<Total>")) == 0; + break; + } + + np = 0; + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + visible = mitem->is_visible (); + tvisible = mitem->is_tvisible (); + pvisible = mitem->is_pvisible (); + + // alignment + for (i = 0; i < np; i++) + fputc (' ', out_file); + + hitem = &hist_metric[index]; + nc = 0; + if (tvisible) + { + value = &(item->value[index]); + nc = print_one_tvisible (out_file, fmt_real0[index], fmt_real1[index], + value, mitem->get_visbits (), + dbeSession->get_clock (-1)); + } + else + nc = 0; + + if (visible) + { + if (mitem->get_vtype () == VT_LABEL) + { + value = &(item->value[index]); + if (value->tag == VT_OFFSET) + nc += fprintf (out_file, fmt_int[index], mark, + ((DataObject*) (item->obj))->get_offset_name ()); + else + nc += fprintf (out_file, fmt_int[index], mark, + item->obj->get_name (nfmt)); + } + else if (name_is_Total && + (strcmp (mitem->get_username (), "Block Covered %") == 0 + || strcmp (mitem->get_username (), "Instr Covered %") == 0)) + { + char stmp[128]; + snprintf (stmp, sizeof (stmp), fmt_int[index], 0); + + /* and now blank that '0' out */ + for (int ii = 0; ii < 128; ii++) + { + if (stmp[ii] != '0') + continue; + stmp[ii] = ' '; + break; + } + nc += fprintf (out_file, stmp); + } + else + nc += print_one_visible (out_file, fmt_int[index], fmt_real0[index], + fmt_real1[index], &(item->value[index]), + mitem->get_visbits ()); + } + + if (pvisible) + { + percent = data->get_percentage (item->value[index].to_double (), index); + if (percent == 0.0) + // adjust to change format from xx.yy% + nc += fprintf (out_file, NTXT ("%#4.0f "), 0.); + else + // adjust format below to change format from xx.yy% + nc += fprintf (out_file, NTXT ("%6.2f "), (100.0 * percent)); + } + np = (int) (hitem->width - nc); + } + fprintf (out_file, nl); +} + +void +print_content (FILE *out_file, Hist_data *data, + char **fmt_int, char **fmt_real0, char **fmt_real1, + MetricList *metrics_list, Metric::HistMetric *hist_metric, + int limit, Histable::NameFormat nfmt) +{ + // printing contents. + for (int i = 0; i < limit; i++) + { + Hist_data::HistItem *item = data->fetch (i); + print_one (out_file, data, item, fmt_int, fmt_real0, fmt_real1, + metrics_list, hist_metric, NTXT (" "), nfmt); + } +} + +er_print_histogram::er_print_histogram (DbeView *_dbev, Hist_data *data, + MetricList *metrics_list, + Print_mode disp_type, int limit, + char *sort_name, Histable *sobj, + bool show_load, bool show_header) +{ + hist_data = data; + mlist = metrics_list; + type = disp_type; + number_entries = limit; + sort_metric = sort_name; + sel_obj = sobj; + dbev = _dbev; + exp_idx1 = 0; + exp_idx2 = dbeSession->nexps () - 1; + load = show_load; + header = show_header; +} + +void +er_print_histogram::dump_list (int limit) +{ + Histable::NameFormat nfmt = dbev->get_name_format (); + StringBuilder sb; + char *title = NULL; // No title for some formats + enum PrintMode pm = dbev->get_printmode (); + + // create a header line, except for delimiter-separated list output + if (pm != PM_DELIM_SEP_LIST) + { + if (hist_data->type == Histable::FUNCTION) + sb.append (GTXT ("Functions sorted by metric: ")); + else if (hist_data->type == Histable::INSTR) + sb.append (GTXT ("PCs sorted by metric: ")); + else if (hist_data->type == Histable::LINE) + sb.append (GTXT ("Lines sorted by metric: ")); + else if (hist_data->type == Histable::DOBJECT) + sb.append (GTXT ("Dataobjects sorted by metric: ")); + else + sb.append (GTXT ("Objects sorted by metric: ")); + sb.append (sort_metric); + title = sb.toString (); + } + + switch (pm) + { + case PM_TEXT: + { + Metric::HistMetric *hist_metric = hist_data->get_histmetrics (); + fprintf (out_file, NTXT ("%s\n\n"), title); //print title + hist_data->print_label (out_file, hist_metric, 0); + hist_data->print_content (out_file, hist_metric, limit); + fprintf (out_file, nl); + break; + } + case PM_HTML: + { + print_html_title (out_file, title); + print_html_label (out_file, mlist); + print_html_content (out_file, hist_data, mlist, limit, nfmt); + print_html_trailer (out_file); + break; + } + case PM_DELIM_SEP_LIST: + { + char delim = dbev->get_printdelimiter (); + print_delim_label (out_file, mlist, delim); + print_delim_content (out_file, hist_data, mlist, limit, nfmt, delim); + print_delim_trailer (out_file, delim); + break; + } + } + free (title); +} + +void +er_print_histogram::dump_annotated_dataobjects (Vector<int> *marks, + int ithreshold) +{ + Metric::HistMetric *hist_metric; + char **fmt_int, **fmt_real0, **fmt_real1; + int no_metrics = mlist->get_items ()->size (); + int name_index = -1; + Histable::NameFormat nfmt = dbev->get_name_format (); + if (!dbeSession->is_datamode_available ()) + fprintf (out_file, + GTXT ("No dataspace information recorded in experiments\n\n")); + + Hist_data *layout_data = dbev->get_data_space ()->get_layout_data (hist_data, marks, ithreshold); + + for (int mind = 0; mind < no_metrics; mind++) + if (mlist->get_items ()->fetch (mind)->get_type () == Metric::ONAME) + name_index = mind; + + fmt_int = new char*[no_metrics]; + fmt_real0 = new char*[no_metrics]; + fmt_real1 = new char*[no_metrics]; + hist_metric = new Metric::HistMetric[no_metrics]; + + // use new layout_data to set metric format + get_width (hist_data, mlist, hist_metric); + get_format (fmt_int, fmt_real0, fmt_real1, mlist, hist_metric, 0); + snprintf (hist_metric[name_index].legend2, MAX_LEN, GTXT ("* +offset .element")); + print_label (out_file, mlist, hist_metric, 3); + fprintf (out_file, nl); + for (long i = 0; i < layout_data->size (); i++) + { + Hist_data::HistItem* item = layout_data->fetch (i); + if (marks->find (i) != -1) + fprintf (out_file, NTXT ("## ")); + else + fprintf (out_file, NTXT (" ")); + print_one (out_file, layout_data, item, fmt_int, fmt_real0, fmt_real1, + mlist, hist_metric, NTXT (" "), nfmt); + } + fprintf (out_file, nl); + + // free format strings. + for (int i = 0; i < no_metrics; i++) + { + free (fmt_int[i]); + free (fmt_real0[i]); + free (fmt_real1[i]); + } + delete[] fmt_int; + delete[] fmt_real0; + delete[] fmt_real1; + delete[] hist_metric; + delete layout_data; +} + +void +er_print_histogram::dump_detail (int limit) +{ + Histable *obj; + Hist_data *current_data; + Histable::Type htype; + TValue *values; + double dvalue, percent; + MetricList *prop_mlist = new MetricList (mlist); + Metric *mitem; + int index, i; + size_t max_len, len, smax_len, slen; + Vaddr pc; + Module *module; + LoadObject *loadobject; + char *sname, *oname, *lname, *alias, *mangle; + char fmt_name[MAX_LEN]; + char fmt_elem[MAX_LEN]; + char fmt_real1[MAX_LEN], fmt_real2[MAX_LEN]; + char fmt_int1[MAX_LEN], fmt_int2[MAX_LEN]; + char fmt_long1[MAX_LEN], fmt_long2[MAX_LEN], fmt_long3[MAX_LEN]; + char fmt_int0[MAX_LEN], fmt_long0[MAX_LEN]; + char numstr[MAX_LEN]; + + Histable::NameFormat nfmt = dbev->get_name_format (); + + // Check max. length of metrics names + max_len = smax_len = 0; + + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + mitem->set_vvisible (true); + if (mitem->get_vtype () == VT_LABEL) + continue; + + if (mitem->get_subtype () != Metric::STATIC) + { + mitem->set_pvisible (true); + len = hist_data->value_maxlen (index); + if (max_len < len) + max_len = len; + slen = strlen (mitem->get_name ()); + if (smax_len < slen) + smax_len = slen; + } + } + + // now get the length of the other (non-performance-data) messages + if (hist_data->type == Histable::FUNCTION) + { + slen = strlen (GTXT ("Source File")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Object File")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Load Object")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Mangled Name")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Aliases")); + if (smax_len < slen) + smax_len = slen; + } + else if (hist_data->type == Histable::DOBJECT) + { + slen = strlen (GTXT ("Scope")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Type")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Member of")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Offset (bytes)")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Size (bytes)")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Elements")); + if (smax_len < slen) + smax_len = slen; + } + snprintf (fmt_name, sizeof (fmt_name), NTXT ("\t%%%ds: "), (int) smax_len); + snprintf (fmt_elem, sizeof (fmt_elem), NTXT ("\t%%%ds "), (int) smax_len); + snprintf (numstr, sizeof (numstr), "%%#%d.0lf ( %#1.0f %%%%%%%%)\n", + (int) (max_len - 3), 0.); + snprintf (fmt_real1, sizeof (fmt_real1), numstr, 0.0); + snprintf (fmt_real2, sizeof (fmt_real2), NTXT ("%%%d.3lf (%%5.1f%%%%)\n"), + (int) max_len); + snprintf (fmt_int0, sizeof (fmt_int0), NTXT ("%%%dd\n"), (int) max_len); + snprintf (numstr, sizeof (numstr), NTXT ("%%%dd ( %#1.0f %%%%%%%%)\n"), + (int) max_len, 0.); + snprintf (fmt_int1, sizeof (fmt_int1), numstr, 0); + snprintf (fmt_int2, sizeof (fmt_int2), NTXT ("%%%dd (%%5.1f%%%%)\n"), + (int) max_len); + snprintf (fmt_long0, sizeof (fmt_long0), NTXT ("%%%dllu\n"), (int) max_len); + snprintf (numstr, sizeof (numstr), NTXT ("%%%dd ( %#1.0f %%%%%%%%)\n"), + (int) max_len, 0.); + snprintf (fmt_long1, sizeof (fmt_long1), numstr, 0); + snprintf (fmt_long2, sizeof (fmt_long2), "%%%dllu (%%5.1f%%%%)\n", + (int) max_len); + snprintf (numstr, sizeof (numstr), NTXT ("\t%%%ds %%%%%dllu\n"), + (int) (smax_len + 1), (int) max_len); + snprintf (fmt_long3, sizeof (fmt_long3), numstr, GTXT ("Count:")); + snprintf (numstr, sizeof (numstr), "%%%dd ( %#1.0f %%%%%%%%) %%#%d.0lf\n", + (int) max_len, 0., (int) (max_len - 6)); + + // now loop over the objects + int num_printed_items = 0; + for (i = 0; i < hist_data->size (); i++) + { + if (hist_data->type == Histable::FUNCTION) + { + if (num_printed_items >= limit) + break; + obj = sel_obj ? sel_obj : hist_data->fetch (i)->obj; + htype = obj->get_type (); + + // ask the view for all the data for the object + // xxxxx may be expensive to rescan all packets via get_hist_data() + current_data = dbev->get_hist_data (prop_mlist, + htype, 0, Hist_data::SELF, obj); + if (current_data->size () == 0) + continue; + values = current_data->fetch (0)->value; + } + else + { + obj = hist_data->fetch (i)->obj; + DataObject *dobj = (DataObject*) obj; + if (sel_obj) + { + // print selected item and its members + if (sel_obj != obj + && (DataObject*) sel_obj != dobj->get_parent ()) + // not a match, advance to next item + continue; + } + else if (num_printed_items >= limit) + break; + htype = obj->get_type (); + values = hist_data->fetch (i)->value; + current_data = hist_data; + } + + if (num_printed_items) + // if this isn't the first one, add a blank line + fprintf (out_file, NTXT ("\n")); + num_printed_items++; + + // Print full object name + if (htype != Histable::DOBJECT) + fprintf (out_file, NTXT ("%s\n"), obj->get_name (nfmt)); + else + { + DataObject *dobj = (DataObject*) obj; + if (!dobj->get_parent ()) + fprintf (out_file, NTXT ("%s\n"), obj->get_name (nfmt)); + else + fprintf (out_file, NTXT (" %s\n"), obj->get_name (nfmt)); + } + + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + if (mitem->get_vtype () == VT_LABEL) + continue; + if (mitem->get_subtype () == Metric::STATIC + && htype == Histable::DOBJECT) + continue; + fprintf (out_file, fmt_name, mitem->get_name ()); + + if (mitem->get_value_styles () & VAL_PERCENT) + { + dvalue = values[index].to_double (); + switch (mitem->get_vtype ()) + { + case VT_DOUBLE: + if (dvalue == 0.0) + fprintf (out_file, fmt_real1); + else + fprintf (out_file, fmt_real2, dvalue, 100.0 + * current_data->get_percentage (dvalue, index)); + break; + case VT_INT: + if (dvalue == 0.0) + fprintf (out_file, fmt_int1); + else + fprintf (out_file, fmt_int2, (int) dvalue, 100.0 + * current_data->get_percentage (dvalue, index)); + break; + case VT_LLONG: + case VT_ULLONG: + if (values[index].ll == 0LL) + { + if (mitem->is_time_val ()) + { + fprintf (out_file, fmt_real1); + fprintf (out_file, fmt_long3, 0LL); + } + else + fprintf (out_file, fmt_long1); + } + else + { + percent = 100.0 * + current_data->get_percentage (dvalue, index); + if (mitem->is_time_val ()) + { + dvalue /= 1.e+6 * dbeSession->get_clock (-1); + fprintf (out_file, fmt_real2, dvalue, percent); + fprintf (out_file, fmt_long3, values[index].ll); + } + else + fprintf (out_file, fmt_long2, values[index].ll, + percent); + } + break; + default: + break; + } + } + else + { + switch (mitem->get_vtype ()) + { + case VT_INT: + fprintf (out_file, fmt_int0, values[index].i); + break; + case VT_LLONG: + case VT_ULLONG: + fprintf (out_file, fmt_long0, values[index].ll); + break; + case VT_ADDRESS: + pc = values[index].ll; + fprintf (out_file, NTXT ("%u:0x%08x\n"), ADDRESS_SEG (pc), + ADDRESS_OFF (pc)); + break; + case VT_DOUBLE: + if (values[index].d == 0.0) + fprintf (out_file, fmt_real1); + else + fprintf (out_file, "\t%*.3lf\n", (int) (max_len - 5), values[index].d); + break; + default: + break; + } + } + } + + // now add the descriptive information about the object + if (htype != Histable::DOBJECT) + { + Function *func = (Function*) obj->convertto (Histable::FUNCTION); + if (func && func->get_type () == Histable::FUNCTION) + { + // Print the source/object/load-object files & aliases + oname = lname = alias = NULL; + sname = func->getDefSrcName (); + mangle = func->get_mangled_name (); + if (mangle && streq (func->get_name (), mangle)) + mangle = NULL; + module = func->module; + if (module) + { + oname = module->get_name (); + loadobject = module->loadobject; + if (loadobject) + { + lname = loadobject->get_pathname (); + alias = loadobject->get_alias (func); + } + } + + if (htype == Histable::INSTR && dbeSession->is_datamode_available ()) + alias = ((DbeInstr*) obj)->get_descriptor (); + + fprintf (out_file, fmt_name, GTXT ("Source File")); + if (sname) + fprintf (out_file, NTXT ("%s"), sname); + fprintf (out_file, NTXT ("\n")); + + fprintf (out_file, fmt_name, GTXT ("Object File")); + if (oname) + fprintf (out_file, NTXT ("%s"), oname); + fprintf (out_file, NTXT ("\n")); + + fprintf (out_file, fmt_name, GTXT ("Load Object")); + if (lname) + fprintf (out_file, NTXT ("%s"), lname); + fprintf (out_file, NTXT ("\n")); + + fprintf (out_file, fmt_name, GTXT ("Mangled Name")); + if (mangle) + fprintf (out_file, NTXT ("%s"), mangle); + fprintf (out_file, NTXT ("\n")); + fprintf (out_file, fmt_name, GTXT ("Aliases")); + if (alias) + fprintf (out_file, NTXT ("%s"), alias); + fprintf (out_file, NTXT ("\n")); + } + } + else + { + // Print the dataobject information + DataObject *dobj = (DataObject*) obj; + Histable *scope = dobj->get_scope (); + + // print the scope + fprintf (out_file, fmt_name, GTXT ("Scope")); + if (!scope) + fprintf (out_file, GTXT ("(Global)\n")); + else switch (scope->get_type ()) + { + case Histable::FUNCTION: + fprintf (out_file, NTXT ("%s(%s)\n"), + ((Function*) scope)->module->get_name (), + scope->get_name ()); + break; + case Histable::LOADOBJECT: + case Histable::MODULE: + default: + fprintf (out_file, NTXT ("%s\n"), scope->get_name ()); + } + + // print the type name + fprintf (out_file, fmt_name, GTXT ("Type")); + if (dobj->get_typename ()) + fprintf (out_file, NTXT ("%s\n"), dobj->get_typename ()); + else + fprintf (out_file, GTXT ("(Synthetic)\n")); + + // print the offset + if (dobj->get_offset () != -1) + { + if (dobj->get_parent ()) + { + fprintf (out_file, fmt_name, GTXT ("Member of")); + fprintf (out_file, NTXT ("%s\n"), dobj->get_parent ()->get_name ()); + } + fprintf (out_file, fmt_name, GTXT ("Offset (bytes)")); + fprintf (out_file, NTXT ("%lld\n"), (long long) dobj->get_offset ()); + } + // print the size + if (dobj->get_size ()) + { + fprintf (out_file, fmt_name, GTXT ("Size (bytes)")); + fprintf (out_file, NTXT ("%lld\n"), (long long) dobj->get_size ()); + } + } + if (hist_data->type == Histable::FUNCTION) + delete current_data; + } + if (num_printed_items == 0 && sel_obj) + fprintf (stderr, + GTXT ("Error: Specified item `%s' had no recorded metrics.\n"), + sel_obj->get_name ()); + delete prop_mlist; +} + +static Metric::HistMetric * +allocateHistMetric (int no_metrics) +{ + Metric::HistMetric *hist_metric = new Metric::HistMetric[no_metrics]; + for (int i = 0; i < no_metrics; i++) + { + Metric::HistMetric *hm = &hist_metric[i]; + hm->init (); + } + return hist_metric; +} + +void +er_print_histogram::dump_gprof (int limit) +{ + StringBuilder sb; + Histable *obj; + Hist_data *callers; + Hist_data *callees; + Hist_data *center; + + int no_metrics = mlist->get_items ()->size (); + Metric::HistMetric *hist_metric = allocateHistMetric (no_metrics); + for (int i = 0; i < limit; i++) + { + obj = sel_obj ? sel_obj : hist_data->fetch (i)->obj; + callers = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLERS, obj); + callees = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLEES, obj); + center = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::SELF, obj); + callers->update_max (hist_metric); + callees->update_max (hist_metric); + center->update_max (hist_metric); + callers->update_legend_width (hist_metric); + callers->print_label (out_file, hist_metric, 0); + callers->print_content (out_file, hist_metric, callers->size ()); + + if (center->size () > 0) + { + center->update_total (callers->get_totals ()); + sb.setLength (0); + center->print_row (&sb, 0, hist_metric, NTXT ("*")); + sb.toFileLn (out_file); + } + callees->print_content (out_file, hist_metric, callees->size ()); + fprintf (out_file, nl); + delete callers; + delete callees; + delete center; + } + delete[] hist_metric; +} + +// dump an annotated file +void +dump_anno_file (FILE *fp, Histable::Type type, Module *module, DbeView *dbev, + MetricList *mlist, TValue *ftotal, const char *srcFile, + Function *func, Vector<int> *marks, int threshold, int vis_bits, + int src_visible, bool hex_visible, bool src_only) +{ + int no_metrics, lspace, mspace, tspace, + remain, mindex, next_mark, hidx, + index; + Metric *mitem; + char **fmt_int, **fmt_real0, **fmt_real1, buf[MAX_LEN]; + Hist_data::HistItem *item; + + SourceFile *srcContext = NULL; + bool func_scope = dbev == NULL ? false : dbev->get_func_scope (); + if (srcFile) + { + srcContext = module->findSource (srcFile, false); + if (srcContext == NULL) + { + Vector<SourceFile*> *includes = module->includes; + char *bname = get_basename (srcFile); + for (int i = 0, sz = includes ? includes->size () : 0; i < sz; i++) + { + SourceFile *sf = includes->fetch (i); + if (streq (get_basename (sf->get_name ()), bname)) + { + srcContext = sf; + break; + } + } + } + if (func) + func_scope = true; + } + else if (func) + srcContext = func->getDefSrc (); + + Hist_data *hdata = module->get_data (dbev, mlist, type, ftotal, srcContext, + func, marks, threshold, vis_bits, + src_visible, hex_visible, + func_scope, src_only); + + if (hdata == NULL) + return; + + // force the name metric to be invisible + MetricList *nmlist = hdata->get_metric_list (); + nmlist->find_metric (GTXT ("name"), Metric::STATIC)->clear_all_visbits (); + no_metrics = nmlist->get_items ()->size (); + fmt_int = new char*[no_metrics]; + fmt_real0 = new char*[no_metrics]; + fmt_real1 = new char*[no_metrics]; + Metric::HistMetric *hist_metric = hdata->get_histmetrics (); + + // lspace is for max line number that's inserted; use to set width + int max_lineno = 0; + Vec_loop (Hist_data::HistItem*, hdata, hidx, item) + { + if (!item->obj) + continue; + if (item->obj->get_type () == Histable::LINE + && ((DbeLine*) item->obj)->lineno > max_lineno) + max_lineno = ((DbeLine*) item->obj)->lineno; + else if (item->obj->get_type () == Histable::INSTR + && ((DbeInstr*) item->obj)->lineno > max_lineno) + max_lineno = ((DbeInstr*) item->obj)->lineno; + } + + lspace = snprintf (buf, sizeof (buf), NTXT ("%d"), max_lineno); + + // mspace is the space needed for all metrics, and the mark, if any + mspace = 0; + if (nmlist->get_items ()->size () > 0) + { + mspace = 3; // mark "## " + Vec_loop (Metric*, nmlist->get_items (), index, mitem) + { + if (mitem->is_visible () || mitem->is_tvisible () + || mitem->is_pvisible ()) + mspace += (int) hist_metric[index].width; + } + } + tspace = 0; + remain = (mspace + lspace + 3) % 8; // " " before, ". " after line# + if (remain) + { // tab alignment + tspace = 8 - remain; + mspace += tspace; + } + mindex = 0; + next_mark = (mindex < marks->size ()) ? marks->fetch (mindex) : -1; + + // Print the header for this list + SourceFile *sf = srcContext ? srcContext : module->getMainSrc (); + char *src_name = sf->dbeFile->get_location_info (); + DbeFile *df = module->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_JAVACLASS) == 0) + df = module->loadobject->dbeFile; + char *lo_name = df->get_location_info (); + char *dot_o_name = lo_name; + if (module->dot_o_file) + dot_o_name = module->dot_o_file->dbeFile->get_location_info (); + fprintf (fp, GTXT ("Source file: %s\nObject file: %s\nLoad Object: %s\n\n"), + src_name, dot_o_name, lo_name); + + // Print metric labels + if (nmlist->get_items ()->size () != 0) + print_label (fp, nmlist, hist_metric, 3); + + // determine the name metric (not printed as a metric, though) + int lind = nmlist->get_listorder (GTXT ("name"), Metric::STATIC); + + // now loop over the data rows -- the lines in the annotated source/disasm, + // including index lines, compiler commentary, etc. + StringBuilder sb; + Vec_loop (Hist_data::HistItem*, hdata, hidx, item) + { + sb.setLength (0); + if (item->type == Module::AT_DIS || item->type == Module::AT_QUOTE + || item->type == Module::AT_SRC) + { + // does this line get a high-metric mark? + if (hidx == next_mark) + { + sb.append (NTXT ("## ")); + mindex++; + next_mark = (mindex < marks->size ()) ? marks->fetch (mindex) : -1; + } + else + sb.append (NTXT (" ")); + + hdata->print_row (&sb, hidx, hist_metric, NTXT (" ")); + sb.toFile (fp); + for (int i = sb.length (); i < mspace; i++) + { + fputc (' ', fp); + } + } + else + // this line does not get any metrics; insert blanks in lieu of them + for (int i = 0; i < mspace; i++) + fputc (' ', fp); + + switch (item->type) + { + case Module::AT_SRC_ONLY: + if (item->obj == NULL) + fprintf (fp, NTXT ("%*s. "), lspace + 1, "?"); + else + fprintf (fp, "%*d. ", lspace + 1, ((DbeLine*) item->obj)->lineno); + break; + + case Module::AT_SRC: + fprintf (fp, "%*d. ", lspace + 1, ((DbeLine*) item->obj)->lineno); + break; + case Module::AT_FUNC: + case Module::AT_QUOTE: + fprintf (fp, NTXT ("%*c"), lspace + 3, ' '); + break; + case Module::AT_DIS: + case Module::AT_DIS_ONLY: + if (item->obj == NULL || ((DbeInstr*) item->obj)->lineno == -1) + fprintf (fp, "%*c[%*s] ", lspace + 3, ' ', lspace, "?"); + else + fprintf (fp, "%*c[%*d] ", lspace + 3, ' ', lspace, + ((DbeInstr*) item->obj)->lineno); + break; + case Module::AT_COM: + case Module::AT_EMPTY: + break; + + } + if (item->value[lind].l == NULL) + item->value[lind].l = dbe_strdup (GTXT ("INTERNAL ERROR: missing line text")); + fprintf (fp, NTXT ("%s\n"), item->value[lind].l); + } + delete[] fmt_int; + delete[] fmt_real0; + delete[] fmt_real1; + delete hdata; +} + +void +er_print_histogram::dump_annotated () +{ + Vector<int> *marks = new Vector<int>; + Function *anno_func = (Function *) sel_obj; + Module *module = anno_func ? anno_func->module : NULL; + + if (hist_data->type == Histable::DOBJECT) + dump_annotated_dataobjects (marks, number_entries); // threshold + else if (number_entries == 0) + // Annotated source + dump_anno_file (out_file, Histable::LINE, module, dbev, mlist, + hist_data->get_totals ()->value, NULL, anno_func, marks, + dbev->get_thresh_src (), dbev->get_src_compcom (), + dbev->get_src_visible (), dbev->get_hex_visible (), true); + else + // Annotated disassembly + dump_anno_file (out_file, Histable::INSTR, module, dbev, mlist, + hist_data->get_totals ()->value, NULL, anno_func, marks, + dbev->get_thresh_dis (), dbev->get_dis_compcom (), + dbev->get_src_visible (), dbev->get_hex_visible (), true); +} + +void +er_print_histogram::data_dump () +{ + int limit; + if (hist_data->get_status () == Hist_data::SUCCESS) + { + if (sort_metric[0] == '\n') + { // csingle Callers-Callees entry + sort_metric++; + fprintf (out_file, NTXT ("%s\n\n"), sort_metric); + } + else if (!sel_obj && type != MODE_LIST) + { + if (hist_data->type == Histable::FUNCTION) + fprintf (out_file, + GTXT ("Functions sorted by metric: %s\n\n"), sort_metric); + else if (hist_data->type == Histable::DOBJECT) + fprintf (out_file, GTXT ("Dataobjects sorted by metric: %s\n\n"), + sort_metric); + else + fprintf (out_file, + GTXT ("Objects sorted by metric: %s\n\n"), sort_metric); + } + limit = hist_data->size (); + if ((number_entries > 0) && (number_entries < limit)) + limit = number_entries; + + switch (type) + { + case MODE_LIST: + dump_list (limit); + break; + case MODE_DETAIL: + dump_detail (limit); + break; + case MODE_GPROF: + dump_gprof (limit); + break; + case MODE_ANNOTATED: + dump_annotated (); + break; + } + } + else + fprintf (out_file, GTXT ("Get_Hist_data call failed %d\n"), + (int) hist_data->get_status ()); +} + +/* + * Class er_print_ctree to print functions call tree + */ +er_print_ctree::er_print_ctree (DbeView *_dbev, Vector<Histable*> *_cstack, + Histable *_sobj, int _limit) +{ + dbev = _dbev; + cstack = _cstack; + sobj = _sobj; + limit = _limit; + print_row = 0; + exp_idx1 = 0; + exp_idx2 = dbeSession->nexps () - 1; + load = false; + header = false; +} + +void +er_print_ctree::data_dump () +{ + StringBuilder sb; + Hist_data::HistItem *total; + sb.append (GTXT ("Functions Call Tree. Metric: ")); + char *s = dbev->getSort (MET_CALL_AGR); + sb.append (s); + free (s); + sb.toFileLn (out_file); + fprintf (out_file, NTXT ("\n")); + mlist = dbev->get_metric_list (MET_CALL_AGR); + + // Change cstack: add sobj to the end of cstack + cstack->append (sobj); + Hist_data *center = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::SELF, cstack); + Hist_data *callers = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLERS, cstack); + Hist_data *callees = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLEES, cstack); + + // Restore cstack + int last = cstack->size () - 1; + cstack->remove (last); + + // Prepare formats + int no_metrics = mlist->size (); + + // calculate max. width using data from callers, callees, center + hist_metric = allocateHistMetric (no_metrics); + callers->update_max (hist_metric); + callees->update_max (hist_metric); + center->update_max (hist_metric); + callers->update_legend_width (hist_metric); + callers->print_label (out_file, hist_metric, 0); // returns Name column offset + + print_row = 0; + // Pass real total to print_children() + total = center->get_totals (); + print_children (center, 0, sobj, NTXT (" "), total); + + // Free memory + cstack->reset (); + delete callers; + delete callees; + delete center; + delete[] hist_metric; +} + +/* + * Recursive method print_children prints Call Tree elements. + */ +void +er_print_ctree::print_children (Hist_data *data, int index, Histable *my_obj, + char * prefix, Hist_data::HistItem *total) +{ + StringBuilder buf; + const char *P0 = "+-"; + const char *P2 = " |"; + const char *P1 = " "; + + // If limit exceeded - return + ++print_row; + if (limit > 0 && print_row > limit) + return; + + if (my_obj == NULL) + return; // should never happen + + // Prepare prefix + buf.append (prefix); + if (buf.endsWith (P2)) + { + int len = buf.length () - 1; + buf.setLength (len); + } + buf.append (P0); + + // Change cstack: add my_obj to the end of cstack + cstack->append (my_obj); + + // Print current node info + char * my_prefix = buf.toString (); + + // Replace parent's total values with real total values + data->update_total (total); // Needed to to calculate percentage only + buf.setLength (0); + data->print_row (&buf, index, hist_metric, my_prefix); + buf.toFileLn (out_file); + free (my_prefix); + + // Get children + Hist_data *callees = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLEES, cstack); + int nc = callees->size (); + if (nc > 0) + { + // Print children + Hist_data::HistItem *item; + Histable *ch_obj; + char *ch_prefix; + buf.setLength (0); + buf.append (prefix); + buf.append (P2); + ch_prefix = buf.toString (); + for (int i = 0; i < nc - 1; i++) + { + item = callees->fetch (i); + ch_obj = item->obj; + print_children (callees, i, ch_obj, ch_prefix, total); + } + free (ch_prefix); + buf.setLength (0); + buf.append (prefix); + buf.append (P1); + ch_prefix = buf.toString (); + item = callees->fetch (nc - 1); + ch_obj = item->obj; + print_children (callees, nc - 1, ch_obj, ch_prefix, total); + free (ch_prefix); + } + + // Restore cstack + int last = cstack->size () - 1; + cstack->remove (last); + delete callees; + return; +} + +er_print_gprof::er_print_gprof (DbeView *_dbev, Vector<Histable*> *_cstack) +{ + dbev = _dbev; + cstack = _cstack; + exp_idx1 = 0; + exp_idx2 = dbeSession->nexps () - 1; + load = false; + header = false; +} + +void +er_print_gprof::data_dump () +{ + StringBuilder sb; + sb.append (GTXT ("Callers and callees sorted by metric: ")); + char *s = dbev->getSort (MET_CALL); + sb.append (s); + free (s); + sb.toFileLn (out_file); + fprintf (out_file, NTXT ("\n")); + + MetricList *mlist = dbev->get_metric_list (MET_CALL); + Hist_data *center = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::SELF, cstack); + Hist_data *callers = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLERS, cstack); + Hist_data *callees = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLEES, cstack); + + mlist = center->get_metric_list (); + int no_metrics = mlist->get_items ()->size (); + + // update max. width for callers/callees/center function item + Metric::HistMetric *hist_metric = allocateHistMetric (no_metrics); + callers->update_max (hist_metric); + callees->update_max (hist_metric); + center->update_max (hist_metric); + + callers->update_legend_width (hist_metric); + int name_offset = callers->print_label (out_file, hist_metric, 0); // returns Name column offset + // Print Callers + sb.setLength (0); + for (int i = 0; i < name_offset; i++) + sb.append (NTXT ("=")); + if (name_offset > 0) + sb.append (NTXT (" ")); + char *line1 = sb.toString (); + char *line2; + if (callers->size () > 0) + line2 = GTXT ("Callers"); + else + line2 = GTXT ("No Callers"); + fprintf (out_file, NTXT ("%s%s\n"), line1, line2); + callers->print_content (out_file, hist_metric, callers->size ()); + + // Print Stack Fragment + line2 = GTXT ("Stack Fragment"); + fprintf (out_file, NTXT ("\n%s%s\n"), line1, line2); + + for (long i = 0, last = cstack->size () - 1; i <= last; ++i) + { + sb.setLength (0); + if (i == last && center->size () > 0) + { + center->update_total (callers->get_totals ()); // Needed to to calculate percentage only + center->print_row (&sb, center->size () - 1, hist_metric, NTXT (" ")); + } + else + { + for (int n = name_offset; n > 0; n--) + sb.append (NTXT (" ")); + if (name_offset > 0) + sb.append (NTXT (" ")); + sb.append (cstack->get (i)->get_name ()); + } + sb.toFileLn (out_file); + } + + // Print Callees + if (callees->size () > 0) + line2 = GTXT ("Callees"); + else + line2 = GTXT ("No Callees"); + fprintf (out_file, NTXT ("\n%s%s\n"), line1, line2); + callees->print_content (out_file, hist_metric, callees->size ()); + fprintf (out_file, nl); + free (line1); + delete callers; + delete callees; + delete center; + delete[] hist_metric; +} + +er_print_leaklist::er_print_leaklist (DbeView *_dbev, bool show_leak, + bool show_alloca, int _limit) +{ + dbev = _dbev; + leak = show_leak; + alloca = show_alloca; + limit = _limit; +} + +// Output routine for leak list only +void +er_print_leaklist::data_dump () +{ + CStack_data *lam; + CStack_data::CStack_item *lae; + int index; + if (!dbeSession->is_leaklist_available ()) + fprintf (out_file, GTXT ("No leak or allocation information recorded in experiments\n\n")); + + MetricList *origmlist = dbev->get_metric_list (MET_NORMAL); + if (leak) + { + // make a copy of the metric list, and set metrics for leaks + MetricList *nmlist = new MetricList (origmlist); + nmlist->set_metrics ("e.heapleakbytes:e.heapleakcnt:name", true, + dbev->get_derived_metrics ()); + + // now make a compacted version of it to get the right indices + MetricList *mlist = new MetricList (nmlist); + delete nmlist; + + // fetch the callstack data + lam = dbev->get_cstack_data (mlist); + + // now print it + if (lam && lam->size () != 0) + { + fprintf (out_file, GTXT ("Summary Results: Distinct Leaks = %d, Total Instances = %lld, Total Bytes Leaked = %lld\n\n"), + (int) lam->size (), lam->total->value[1].ll, + lam->total->value[0].ll); + + Vec_loop (CStack_data::CStack_item*, lam->cstack_items, index, lae) + { + fprintf (out_file, + GTXT ("Leak #%d, Instances = %lld, Bytes Leaked = %lld\n"), + index + 1, lae->value[1].ll, lae->value[0].ll); + if (lae->stack != NULL) + for (int i = lae->stack->size () - 1; i >= 0; i--) + { + DbeInstr *instr = lae->stack->fetch (i); + fprintf (out_file, NTXT (" %s\n"), instr->get_name ()); + } + fprintf (out_file, NTXT ("\n")); + if (index + 1 == limit) break; + } + } + else + fprintf (out_file, GTXT ("No leak information\n\n")); + delete lam; + delete mlist; + } + + if (alloca) + { + // make a copy of the metric list, and set metrics for leaks + MetricList *nmlist = new MetricList (origmlist); + nmlist->set_metrics ("e.heapallocbytes:e.heapalloccnt:name", + true, dbev->get_derived_metrics ()); + + // now make a compacted version of it to get the right indices + MetricList *mlist = new MetricList (nmlist); + delete nmlist; + + // fetch the callstack data + lam = dbev->get_cstack_data (mlist); + + // now print it + if (lam && lam->size () != 0) + { + fprintf (out_file, GTXT ("Summary Results: Distinct Allocations = %d, Total Instances = %lld, Total Bytes Allocated = %lld\n\n"), + (int) lam->size (), lam->total->value[1].ll, + lam->total->value[0].ll); + Vec_loop (CStack_data::CStack_item*, lam->cstack_items, index, lae) + { + fprintf (out_file, GTXT ("Allocation #%d, Instances = %lld, Bytes Allocated = %lld\n"), + index + 1, lae->value[1].ll, lae->value[0].ll); + if (lae->stack != NULL) + for (int i = lae->stack->size () - 1; i >= 0; i--) + { + DbeInstr *instr = lae->stack->fetch (i); + fprintf (out_file, NTXT (" %s\n"), instr->get_name ()); + } + fprintf (out_file, NTXT ("\n")); + if (index + 1 == limit) break; + } + } + else + fprintf (out_file, GTXT ("No allocation information\n\n")); + delete lam; + delete mlist; + } +} + +er_print_heapactivity::er_print_heapactivity (DbeView *_dbev, + Histable::Type _type, + bool _printStat, int _limit) +{ + dbev = _dbev; + type = _type; + printStat = _printStat; + limit = _limit; +} + +void +er_print_heapactivity::printCallStacks (Hist_data *hist_data) +{ + Hist_data::HistItem *hi; + HeapData *hData; + long stackId; + int size = hist_data->size (); + if (limit > 0 && limit < size) + size = limit; + + Histable::NameFormat fmt = dbev->get_name_format (); + for (int i = 0; i < size; i++) + { + hi = hist_data->fetch (i); + hData = (HeapData*) hi->obj; + stackId = hData->id; + if (i != 0) + fprintf (out_file, NTXT ("\n")); + + fprintf (out_file, NTXT ("%s\n"), hData->get_name (fmt)); + if (hData->getAllocCnt () > 0) + { + fprintf (out_file, GTXT ("Instances = %d "), + (int) (hData->getAllocCnt ())); + fprintf (out_file, GTXT ("Bytes Allocated = %lld\n"), + (long long) hData->getAllocBytes ()); + } + + if (hData->getLeakCnt () > 0) + { + fprintf (out_file, GTXT ("Instances = %d "), + (int) (hData->getLeakCnt ())); + fprintf (out_file, GTXT ("Bytes Leaked = %lld\n"), + (long long) hData->getLeakBytes ()); + } + + // There is no stack trace for <Total> + if (i == 0) + continue; + + // LIBRARY VISIBILITY pass extra argument if necessary to get hide stack + Vector<Histable*> *instrs = CallStack::getStackPCs ((void *) stackId); + if (instrs != NULL) + { + int stSize = instrs->size (); + for (int j = 0; j < stSize; j++) + { + Histable *instr = instrs->fetch (j); + if (instr != NULL) + fprintf (out_file, NTXT (" %s\n"), instr->get_name ()); + } + delete instrs; + } + } +} + +void +er_print_heapactivity::printStatistics (Hist_data *hist_data) +{ + Hist_data::HistItem *hi; + HeapData *hDataTotal; + hi = hist_data->fetch (0); + hDataTotal = (HeapData*) hi->obj; + Vector<hrtime_t> *pTimestamps; + if (hDataTotal->getPeakMemUsage () > 0) + { + fprintf (out_file, GTXT ("\nProcess With Highest Peak Memory Usage\n")); + fprintf (out_file, + "-------------------------------------------------------\n"); + fprintf (out_file, GTXT ("Heap size bytes %lld\n"), + (long long) hDataTotal->getPeakMemUsage ()); + fprintf (out_file, GTXT ("Experiment Id %d\n"), + (int) (hDataTotal->getUserExpId ())); + fprintf (out_file, GTXT ("Process Id %d\n"), + (int) (hDataTotal->getPid ())); + pTimestamps = hDataTotal->getPeakTimestamps (); + if (pTimestamps != NULL) + for (int i = 0; i < pTimestamps->size (); i++) + fprintf (out_file, + GTXT ("Time of peak %.3f (secs.)\n"), + (double) (pTimestamps->fetch (i) / (double) NANOSEC)); + } + + if (hDataTotal->getAllocCnt () > 0) + { + fprintf (out_file, GTXT ("\nMemory Allocations Statistics\n")); + fprintf (out_file, + GTXT ("Allocation Size Range Allocations \n")); + fprintf (out_file, + "-------------------------------------------------------\n"); + if (hDataTotal->getA0KB1KBCnt () > 0) + fprintf (out_file, NTXT (" 0KB - 1KB %d\n"), + hDataTotal->getA0KB1KBCnt ()); + if (hDataTotal->getA1KB8KBCnt () > 0) + fprintf (out_file, NTXT (" 1KB - 8KB %d\n"), + hDataTotal->getA1KB8KBCnt ()); + if (hDataTotal->getA8KB32KBCnt () > 0) + fprintf (out_file, NTXT (" 8KB - 32KB %d\n"), + hDataTotal->getA8KB32KBCnt ()); + if (hDataTotal->getA32KB128KBCnt () > 0) + fprintf (out_file, NTXT (" 32KB - 128KB %d\n"), + hDataTotal->getA32KB128KBCnt ()); + if (hDataTotal->getA128KB256KBCnt () > 0) + fprintf (out_file, NTXT (" 128KB - 256KB %d\n"), + hDataTotal->getA128KB256KBCnt ()); + if (hDataTotal->getA256KB512KBCnt () > 0) + fprintf (out_file, NTXT (" 256KB - 512KB %d\n"), + hDataTotal->getA256KB512KBCnt ()); + if (hDataTotal->getA512KB1000KBCnt () > 0) + fprintf (out_file, NTXT (" 512KB - 1000KB %d\n"), + hDataTotal->getA512KB1000KBCnt ()); + if (hDataTotal->getA1000KB10MBCnt () > 0) + fprintf (out_file, NTXT (" 1000KB - 10MB %d\n"), + hDataTotal->getA1000KB10MBCnt ()); + if (hDataTotal->getA10MB100MBCnt () > 0) + fprintf (out_file, NTXT (" 10MB - 100MB %d\n"), + hDataTotal->getA10MB100MBCnt ()); + if (hDataTotal->getA100MB1GBCnt () > 0) + fprintf (out_file, NTXT (" 100MB - 1GB %d\n"), + hDataTotal->getA100MB1GBCnt ()); + if (hDataTotal->getA1GB10GBCnt () > 0) + fprintf (out_file, NTXT (" 1GB - 10GB %d\n"), + hDataTotal->getA1GB10GBCnt ()); + if (hDataTotal->getA10GB100GBCnt () > 0) + fprintf (out_file, NTXT (" 10GB - 100GB %d\n"), + hDataTotal->getA10GB100GBCnt ()); + if (hDataTotal->getA100GB1TBCnt () > 0) + fprintf (out_file, NTXT (" 100GB - 1TB %d\n"), + hDataTotal->getA100GB1TBCnt ()); + if (hDataTotal->getA1TB10TBCnt () > 0) + fprintf (out_file, NTXT (" 1TB - 10TB %d\n"), + hDataTotal->getA1TB10TBCnt ()); + fprintf (out_file, GTXT ("\nSmallest allocation bytes %lld\n"), + (long long) hDataTotal->getASmallestBytes ()); + fprintf (out_file, GTXT ("Largest allocation bytes %lld\n"), + (long long) hDataTotal->getALargestBytes ()); + fprintf (out_file, GTXT ("Total allocations %d\n"), + hDataTotal->getAllocCnt ()); + fprintf (out_file, GTXT ("Total bytes %lld\n"), + (long long) hDataTotal->getAllocBytes ()); + } + + if (hDataTotal->getLeakCnt () > 0) + { + fprintf (out_file, GTXT ("\nMemory Leaks Statistics\n")); + fprintf (out_file, + GTXT ("Leak Size Range Leaks \n")); + fprintf (out_file, + "-------------------------------------------------------\n"); + if (hDataTotal->getL0KB1KBCnt () > 0) + fprintf (out_file, NTXT (" 0KB - 1KB %d\n"), + hDataTotal->getL0KB1KBCnt ()); + if (hDataTotal->getL1KB8KBCnt () > 0) + fprintf (out_file, NTXT (" 1KB - 8KB %d\n"), + hDataTotal->getL1KB8KBCnt ()); + if (hDataTotal->getL8KB32KBCnt () > 0) + fprintf (out_file, NTXT (" 8KB - 32KB %d\n"), + hDataTotal->getL8KB32KBCnt ()); + if (hDataTotal->getL32KB128KBCnt () > 0) + fprintf (out_file, NTXT (" 32KB - 128KB %d\n"), + hDataTotal->getL32KB128KBCnt ()); + if (hDataTotal->getL128KB256KBCnt () > 0) + fprintf (out_file, NTXT (" 128KB - 256KB %d\n"), + hDataTotal->getL128KB256KBCnt ()); + if (hDataTotal->getL256KB512KBCnt () > 0) + fprintf (out_file, NTXT (" 256KB - 512KB %d\n"), + hDataTotal->getL256KB512KBCnt ()); + if (hDataTotal->getL512KB1000KBCnt () > 0) + fprintf (out_file, NTXT (" 512KB - 1000KB %d\n"), + hDataTotal->getL512KB1000KBCnt ()); + if (hDataTotal->getL1000KB10MBCnt () > 0) + fprintf (out_file, NTXT (" 1000KB - 10MB %d\n"), + hDataTotal->getL1000KB10MBCnt ()); + if (hDataTotal->getL10MB100MBCnt () > 0) + fprintf (out_file, NTXT (" 10MB - 100MB %d\n"), + hDataTotal->getL10MB100MBCnt ()); + if (hDataTotal->getL100MB1GBCnt () > 0) + fprintf (out_file, NTXT (" 100MB - 1GB %d\n"), + hDataTotal->getL100MB1GBCnt ()); + if (hDataTotal->getL1GB10GBCnt () > 0) + fprintf (out_file, NTXT (" 1GB - 10GB %d\n"), + hDataTotal->getL1GB10GBCnt ()); + if (hDataTotal->getL10GB100GBCnt () > 0) + fprintf (out_file, NTXT (" 10GB - 100GB %d\n"), + hDataTotal->getL10GB100GBCnt ()); + if (hDataTotal->getL100GB1TBCnt () > 0) + fprintf (out_file, NTXT (" 100GB - 1TB %d\n"), + hDataTotal->getL100GB1TBCnt ()); + if (hDataTotal->getL1TB10TBCnt () > 0) + fprintf (out_file, NTXT (" 1TB - 10TB %d\n"), + hDataTotal->getL1TB10TBCnt ()); + fprintf (out_file, GTXT ("\nSmallest leaked bytes %lld\n"), + (long long) hDataTotal->getLSmallestBytes ()); + fprintf (out_file, GTXT ("Largest leaked bytes %lld\n"), + (long long) hDataTotal->getLLargestBytes ()); + fprintf (out_file, GTXT ("Total leaked %d \n"), + hDataTotal->getLeakCnt ()); + fprintf (out_file, GTXT ("Total bytes %lld\n"), + (long long) hDataTotal->getLeakBytes ()); + } + fprintf (out_file, NTXT ("\n")); +} + +void +er_print_heapactivity::data_dump () +{ + // get the list of heap events from DbeView + int numExps = dbeSession->nexps (); + if (!numExps) + { + fprintf (out_file, + GTXT ("There is no heap event information in the experiments\n")); + return; + } + MetricList *mlist = dbev->get_metric_list (MET_HEAP); + Hist_data *hist_data; + hist_data = dbev->get_hist_data (mlist, type, 0, Hist_data::ALL); + if (printStat) + printStatistics (hist_data); + else + printCallStacks (hist_data); +} + +er_print_ioactivity::er_print_ioactivity (DbeView *_dbev, Histable::Type _type, + bool _printStat, int _limit) +{ + dbev = _dbev; + type = _type; + printStat = _printStat; + limit = _limit; +} + +void +er_print_ioactivity::printCallStacks (Hist_data *hist_data) +{ + Hist_data::HistItem *hi; + FileData *fData; + long stackId; + int size = hist_data->size (); + if (limit > 0 && limit < size) + size = limit; + + for (int i = 0; i < size; i++) + { + hi = hist_data->fetch (i); + fData = (FileData*) hi->obj; + stackId = fData->id; + if (i != 0) + fprintf (out_file, NTXT ("\n")); + fprintf (out_file, NTXT ("%s\n"), fData->getFileName ()); + if (fData->getWriteCnt () > 0) + { + fprintf (out_file, GTXT ("Write Time=%.6f (secs.) "), + (double) (fData->getWriteTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Write Bytes=%lld "), + (long long) fData->getWriteBytes ()); + fprintf (out_file, GTXT ("Write Count=%d\n"), + (int) (fData->getWriteCnt ())); + } + if (fData->getReadCnt () > 0) + { + fprintf (out_file, GTXT ("Read Time=%.6f (secs.) "), + (double) (fData->getReadTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Read Bytes=%lld "), + (long long) fData->getReadBytes ()); + fprintf (out_file, GTXT ("Read Count=%d\n"), + (int) fData->getReadCnt ()); + } + if (fData->getOtherCnt () > 0) + { + fprintf (out_file, GTXT ("Other I/O Time=%.6f (secs.) "), + (double) (fData->getOtherTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Other I/O Count=%d\n"), + (int) (fData->getOtherCnt ())); + } + if (fData->getErrorCnt () > 0) + { + fprintf (out_file, GTXT ("I/O Error Time=%.6f (secs.) "), + (double) (fData->getErrorTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("I/O Error Count=%d\n"), + (int) (fData->getErrorCnt ())); + } + + // There is no stack trace for <Total> + if (i == 0) + continue; + + // LIBRARY VISIBILITY pass extra argument if necessary to get hide stack + Vector<Histable*> *instrs = CallStack::getStackPCs ((void *) stackId); + if (instrs != NULL) + { + int stSize = instrs->size (); + for (int j = 0; j < stSize; j++) + { + Histable *instr = instrs->fetch (j); + if (instr != NULL) + fprintf (out_file, " %s\n", instr->get_name ()); + } + delete instrs; + } + } +} + +void +er_print_ioactivity::printStatistics (Hist_data *hist_data) +{ + Hist_data::HistItem *hi; + FileData *fDataTotal; + + hi = hist_data->fetch (0); + fDataTotal = (FileData*) hi->obj; + + if (fDataTotal->getWriteCnt () > 0) + { + fprintf (out_file, + GTXT ("\nWrite Statistics\n")); + fprintf (out_file, + GTXT ("I/O Size Range Write Calls \n")); + fprintf (out_file, + "-------------------------------------------------------\n"); + if (fDataTotal->getW0KB1KBCnt () > 0) + fprintf (out_file, NTXT (" 0KB - 1KB %d\n"), + fDataTotal->getW0KB1KBCnt ()); + if (fDataTotal->getW1KB8KBCnt () > 0) + fprintf (out_file, NTXT (" 1KB - 8KB %d\n"), + fDataTotal->getW1KB8KBCnt ()); + if (fDataTotal->getW8KB32KBCnt () > 0) + fprintf (out_file, NTXT (" 8KB - 32KB %d\n"), + fDataTotal->getW8KB32KBCnt ()); + if (fDataTotal->getW32KB128KBCnt () > 0) + fprintf (out_file, NTXT (" 32KB - 128KB %d\n"), + fDataTotal->getW32KB128KBCnt ()); + if (fDataTotal->getW128KB256KBCnt () > 0) + fprintf (out_file, NTXT (" 128KB - 256KB %d\n"), + fDataTotal->getW128KB256KBCnt ()); + if (fDataTotal->getW256KB512KBCnt () > 0) + fprintf (out_file, NTXT (" 256KB - 512KB %d\n"), + fDataTotal->getW256KB512KBCnt ()); + if (fDataTotal->getW512KB1000KBCnt () > 0) + fprintf (out_file, NTXT (" 512KB - 1000KB %d\n"), + fDataTotal->getW512KB1000KBCnt ()); + if (fDataTotal->getW1000KB10MBCnt () > 0) + fprintf (out_file, NTXT (" 1000KB - 10MB %d\n"), + fDataTotal->getW1000KB10MBCnt ()); + if (fDataTotal->getW10MB100MBCnt () > 0) + fprintf (out_file, NTXT (" 10MB - 100MB %d\n"), + fDataTotal->getW10MB100MBCnt ()); + if (fDataTotal->getW100MB1GBCnt () > 0) + fprintf (out_file, NTXT (" 100MB - 1GB %d\n"), + fDataTotal->getW100MB1GBCnt ()); + if (fDataTotal->getW1GB10GBCnt () > 0) + fprintf (out_file, NTXT (" 1GB - 10GB %d\n"), + fDataTotal->getW1GB10GBCnt ()); + if (fDataTotal->getW10GB100GBCnt () > 0) + fprintf (out_file, NTXT (" 10GB - 100GB %d\n"), + fDataTotal->getW10GB100GBCnt ()); + if (fDataTotal->getW100GB1TBCnt () > 0) + fprintf (out_file, NTXT (" 100GB - 1TB %d\n"), + fDataTotal->getW100GB1TBCnt ()); + if (fDataTotal->getW1TB10TBCnt () > 0) + fprintf (out_file, NTXT (" 1TB - 10TB %d\n"), + fDataTotal->getW1TB10TBCnt ()); + fprintf (out_file, + GTXT ("\nLongest write %.6f (secs.)\n"), + (double) (fDataTotal->getWSlowestBytes () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Smallest write bytes %lld\n"), + (long long) fDataTotal->getWSmallestBytes ()); + fprintf (out_file, GTXT ("Largest write bytes %lld\n"), + (long long) fDataTotal->getWLargestBytes ()); + fprintf (out_file, + GTXT ("Total time %.6f (secs.)\n"), + (double) (fDataTotal->getWriteTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Total calls %d\n"), + fDataTotal->getWriteCnt ()); + fprintf (out_file, GTXT ("Total bytes %lld\n"), + (long long) fDataTotal->getWriteBytes ()); + } + + if (fDataTotal->getReadCnt () > 0) + { + fprintf (out_file, + GTXT ("\nRead Statistics\n")); + fprintf (out_file, + GTXT ("I/O Size Range Read Calls \n")); + fprintf (out_file, + "------------------------------------------------------\n"); + if (fDataTotal->getR0KB1KBCnt () > 0) + fprintf (out_file, NTXT (" 0KB - 1KB %d\n"), + fDataTotal->getR0KB1KBCnt ()); + if (fDataTotal->getR1KB8KBCnt () > 0) + fprintf (out_file, NTXT (" 1KB - 8KB %d\n"), + fDataTotal->getR1KB8KBCnt ()); + if (fDataTotal->getR8KB32KBCnt () > 0) + fprintf (out_file, NTXT (" 8KB - 32KB %d\n"), + fDataTotal->getR8KB32KBCnt ()); + if (fDataTotal->getR32KB128KBCnt () > 0) + fprintf (out_file, NTXT (" 32KB - 128KB %d\n"), + fDataTotal->getR32KB128KBCnt ()); + if (fDataTotal->getR128KB256KBCnt () > 0) + fprintf (out_file, NTXT (" 128KB - 256KB %d\n"), + fDataTotal->getR128KB256KBCnt ()); + if (fDataTotal->getR256KB512KBCnt () > 0) + fprintf (out_file, NTXT (" 256KB - 512KB %d\n"), + fDataTotal->getR256KB512KBCnt ()); + if (fDataTotal->getR512KB1000KBCnt () > 0) + fprintf (out_file, NTXT (" 512KB - 1000KB %d\n"), + fDataTotal->getR512KB1000KBCnt ()); + if (fDataTotal->getR1000KB10MBCnt () > 0) + fprintf (out_file, NTXT (" 1000KB - 10MB %d\n"), + fDataTotal->getR1000KB10MBCnt ()); + if (fDataTotal->getR10MB100MBCnt () > 0) + fprintf (out_file, NTXT (" 10MB - 100MB %d\n"), + fDataTotal->getR10MB100MBCnt ()); + if (fDataTotal->getR100MB1GBCnt () > 0) + fprintf (out_file, NTXT (" 100MB - 1GB %d\n"), + fDataTotal->getR100MB1GBCnt ()); + if (fDataTotal->getR1GB10GBCnt () > 0) + fprintf (out_file, NTXT (" 1GB - 10GB %d\n"), + fDataTotal->getR1GB10GBCnt ()); + if (fDataTotal->getR10GB100GBCnt () > 0) + fprintf (out_file, NTXT (" 10GB - 100GB %d\n"), + fDataTotal->getR10GB100GBCnt ()); + if (fDataTotal->getR100GB1TBCnt () > 0) + fprintf (out_file, NTXT (" 100GB - 1TB %d\n"), + fDataTotal->getR100GB1TBCnt ()); + if (fDataTotal->getR1TB10TBCnt () > 0) + fprintf (out_file, NTXT (" 1TB - 10TB %d\n"), + fDataTotal->getR1TB10TBCnt ()); + fprintf (out_file, + GTXT ("\nLongest time %.6f (secs.)\n"), + (double) (fDataTotal->getRSlowestBytes () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Smallest read bytes %lld\n"), + (long long) fDataTotal->getRSmallestBytes ()); + fprintf (out_file, GTXT ("Largest read bytes %lld\n"), + (long long) fDataTotal->getRLargestBytes ()); + fprintf (out_file, + GTXT ("Total time %.6f (secs.)\n"), + (double) (fDataTotal->getReadTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Total calls %d\n"), + fDataTotal->getReadCnt ()); + fprintf (out_file, GTXT ("Total bytes %lld\n"), + (long long) fDataTotal->getReadBytes ()); + } + + if (fDataTotal->getOtherCnt () > 0) + { + fprintf (out_file, GTXT ("\nOther I/O Statistics\n")); + fprintf (out_file, + "-----------------------------------------------------\n"); + fprintf (out_file, + GTXT ("Total time %.6f (secs.)\n"), + (double) (fDataTotal->getOtherTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Total calls %d \n"), + fDataTotal->getOtherCnt ()); + } + if (fDataTotal->getErrorCnt () > 0) + { + fprintf (out_file, GTXT ("\nI/O Error Statistics\n")); + fprintf (out_file, + "-----------------------------------------------------\n"); + fprintf (out_file, + GTXT ("Total time %.6f (secs.)\n"), + (double) (fDataTotal->getErrorTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Total calls %d \n"), + fDataTotal->getErrorCnt ()); + } + fprintf (out_file, NTXT ("\n")); +} + +void +er_print_ioactivity::data_dump () +{ + // get the list of io events from DbeView + int numExps = dbeSession->nexps (); + if (!numExps) + { + fprintf (out_file, + GTXT ("There is no IO event information in the experiments\n")); + return; + } + + MetricList *mlist = dbev->get_metric_list (MET_IO); + Hist_data *hist_data = dbev->get_hist_data (mlist, type, 0, Hist_data::ALL); + if (type == Histable::IOCALLSTACK) + printCallStacks (hist_data); + else if (printStat) + printStatistics (hist_data); + else + { + Metric::HistMetric *hist_metric = hist_data->get_histmetrics (); + hist_data->print_label (out_file, hist_metric, 0); + hist_data->print_content (out_file, hist_metric, limit); + fprintf (out_file, nl); + } +} + +er_print_experiment::er_print_experiment (DbeView *_dbev, int bgn_idx, + int end_idx, bool show_load, + bool show_header, bool show_stat, + bool show_over, bool show_odetail) +{ + dbev = _dbev; + exp_idx1 = bgn_idx; + exp_idx2 = end_idx; + load = show_load; + header = show_header; + stat = show_stat; + over = show_over; + odetail = show_odetail; +} + +void +er_print_experiment::data_dump () +{ + int index, maxlen; + + maxlen = 0; + + if (stat) + { + snprintf (fmt1, sizeof (fmt1), NTXT ("%%50s")); + if (exp_idx2 > exp_idx1) + { + statistics_sum (maxlen); + fprintf (out_file, nl); + } + + for (index = exp_idx1; index <= exp_idx2; index++) + statistics_dump (index, maxlen); + } + else if (over) + { + snprintf (fmt1, sizeof (fmt1), NTXT ("%%30s")); + if (exp_idx2 > exp_idx1) + { + overview_sum (maxlen); + fprintf (out_file, nl); + } + + for (index = exp_idx1; index <= exp_idx2; index++) + overview_dump (index, maxlen); + } + else if (header) + for (index = exp_idx1; index <= exp_idx2; index++) + { + if (index != exp_idx1) + fprintf (out_file, + "----------------------------------------------------------------\n"); + header_dump (index); + } +} + +void +er_print_experiment::overview_sum (int &maxlen) +{ + int index; + Ovw_data *sum_data = new Ovw_data (); + for (index = exp_idx1; index <= exp_idx2; index++) + { + Ovw_data *ovw_data = dbev->get_ovw_data (index); + if (ovw_data == NULL) + continue; + sum_data->sum (ovw_data); + delete ovw_data; + } + + fprintf (out_file, GTXT ("<Sum across selected experiments>")); + fprintf (out_file, nl); + overview_summary (sum_data, maxlen); + fprintf (out_file, nl); + delete sum_data; +} + +void +er_print_experiment::overview_dump (int exp_idx, int &maxlen) +{ + Ovw_data *ovw_data; + Ovw_data::Ovw_item ovw_item_labels; + Ovw_data::Ovw_item ovw_item; + int index; + int size; + + ovw_data = dbev->get_ovw_data (exp_idx); + if (ovw_data == NULL) + return; + if (pr_params.header) + header_dump (exp_idx); + else if (odetail) + fprintf (out_file, GTXT ("Experiment: %s\n"), + dbeSession->get_exp (exp_idx)->get_expt_name ()); + + overview_summary (ovw_data, maxlen); + if (!odetail) + { + delete ovw_data; + return; + } + + //Get the collection params for the sample selection and display them. + fprintf (out_file, NTXT ("\n\n")); + fprintf (out_file, fmt1, GTXT ("Individual samples")); + fprintf (out_file, NTXT ("\n\n")); + + size = ovw_data->size (); + ovw_item_labels = ovw_data->get_labels (); + + for (index = 0; index < size; index++) + { + ovw_item = ovw_data->fetch (index); + fprintf (out_file, fmt1, GTXT ("Sample Number")); + fprintf (out_file, NTXT (": %d\n\n"), ovw_item.number); + overview_item (&ovw_item, &ovw_item_labels); + fprintf (out_file, nl); + } + + delete ovw_data; +} + +void +er_print_experiment::overview_summary (Ovw_data *ovw_data, int &maxlen) +{ + char buf[128]; + int len; + Ovw_data::Ovw_item totals; + Ovw_data::Ovw_item ovw_item_labels; + totals = ovw_data->get_totals (); + len = snprintf (buf, sizeof (buf), "%.3lf", tstodouble (totals.total.t)); + if (maxlen < len) + maxlen = len; + snprintf (buf, sizeof (buf), NTXT ("%%#%d.0lf ( %#1.0f %%%%%%%%)"), + maxlen - 3, 0.); + snprintf (fmt2, sizeof (fmt2), NTXT ("%%%d.3lf"), maxlen); + snprintf (fmt3, sizeof (fmt3), buf, 0.0); + snprintf (fmt4, sizeof (fmt4), NTXT ("%%%d.3lf (%%5.1f%%%%)"), maxlen); + fprintf (out_file, fmt1, GTXT ("Aggregated statistics for selected samples")); + fprintf (out_file, NTXT ("\n\n")); + + ovw_item_labels = ovw_data->get_labels (); + overview_item (&totals, &ovw_item_labels); +} + +void +er_print_experiment::overview_item (Ovw_data::Ovw_item *ovw_item, + Ovw_data::Ovw_item *ovw_item_labels) +{ + double start, end, total_value; + int index, size; + timestruc_t total_time = {0, 0}; + + start = tstodouble (ovw_item->start); + end = tstodouble (ovw_item->end); + + fprintf (out_file, fmt1, GTXT ("Start Label")); + fprintf (out_file, NTXT (": ")); + fprintf (out_file, NTXT ("%s"), ovw_item->start_label); + fprintf (out_file, nl); + fprintf (out_file, fmt1, GTXT ("End Label")); + fprintf (out_file, NTXT (": %s\n"), ovw_item->end_label); + + fprintf (out_file, fmt1, GTXT ("Start Time (sec.)")); + fprintf (out_file, NTXT (": ")); + if (start == -1.0) + fprintf (out_file, GTXT ("N/A")); + else + fprintf (out_file, fmt2, start); + fprintf (out_file, nl); + fprintf (out_file, fmt1, GTXT ("End Time (sec.)")); + fprintf (out_file, NTXT (": ")); + if (end == -1.0) + fprintf (out_file, GTXT ("N/A")); + else + fprintf (out_file, fmt2, end); + fprintf (out_file, nl); + fprintf (out_file, fmt1, GTXT ("Duration (sec.)")); + fprintf (out_file, NTXT (": ")); + fprintf (out_file, fmt2, tstodouble (ovw_item->duration)); + fprintf (out_file, NTXT ("\n")); + + size = ovw_item->size; + for (index = 0; index < size; index++) + tsadd (&total_time, &ovw_item->values[index].t); + + total_value = tstodouble (total_time); + fprintf (out_file, fmt1, GTXT ("Total Thread Time (sec.)")); + fprintf (out_file, NTXT (": ")); + fprintf (out_file, fmt2, tstodouble (ovw_item->tlwp)); + fprintf (out_file, NTXT ("\n")); + fprintf (out_file, fmt1, GTXT ("Average number of Threads")); + fprintf (out_file, NTXT (": ")); + if (tstodouble (ovw_item->duration) != 0) + fprintf (out_file, fmt2, ovw_item->nlwp); + else + fprintf (out_file, GTXT ("N/A")); + fprintf (out_file, NTXT ("\n\n")); + fprintf (out_file, fmt1, GTXT ("Process Times (sec.)")); + fprintf (out_file, NTXT (":\n")); + for (index = 1; index < size; index++) + { + overview_value (&ovw_item_labels->values[index], ovw_item_labels->type, + total_value); + overview_value (&ovw_item->values[index], ovw_item->type, + total_value); + fprintf (out_file, NTXT ("\n")); + } +} + +void +er_print_experiment::overview_value (Value *value, ValueTag value_tag, + double total_value) +{ + double dvalue; + switch (value_tag) + { + case VT_LABEL: + fprintf (out_file, fmt1, value->l); + fprintf (out_file, NTXT (": ")); + break; + case VT_HRTIME: + dvalue = tstodouble (value->t); + if (dvalue == 0.0) + fprintf (out_file, fmt3, 0., 0.); + else + fprintf (out_file, fmt4, dvalue, 100.0 * dvalue / total_value); + break; + case VT_INT: + fprintf (out_file, NTXT ("%d"), value->i); + break; + default: + fprintf (out_file, fmt3); + } +} + +void +er_print_experiment::statistics_sum (int &maxlen) +{ + int index; + int size, len; + Stats_data *sum_data = new Stats_data (); + for (index = exp_idx1; index <= exp_idx2; index++) + { + Stats_data *stats_data = dbev->get_stats_data (index); + if (stats_data == NULL) + continue; + sum_data->sum (stats_data); + delete stats_data; + } + + // get the maximum width of values + size = sum_data->size (); + for (index = 0; index < size; index++) + { + len = (int) sum_data->fetch (index).value.get_len (); + if (maxlen < len) + maxlen = len; + } + + // print overview average + overview_sum (maxlen); + + // print statistics data + snprintf (fmt2, sizeof (fmt2), NTXT (": %%%ds\n"), maxlen); + statistics_item (sum_data); + delete sum_data; +} + +void +er_print_experiment::statistics_dump (int exp_idx, int &maxlen) +{ + Stats_data *stats_data; + int index; + int size, len; + stats_data = dbev->get_stats_data (exp_idx); + if (stats_data == NULL) + return; + if (pr_params.header) + { + header_dump (exp_idx); + fprintf (out_file, nl); + } + else + fprintf (out_file, GTXT ("Experiment: %s\n"), + dbeSession->get_exp (exp_idx)->get_expt_name ()); + + // get the maximum width of values + size = stats_data->size (); + for (index = 0; index < size; index++) + { + len = (int) stats_data->fetch (index).value.get_len (); + if (maxlen < len) + maxlen = len; + } + + // print overview average + overview_dump (exp_idx, maxlen); + fprintf (out_file, nl); + + // print statistics data + snprintf (fmt2, sizeof (fmt2), NTXT (": %%%ds\n"), maxlen); + statistics_item (stats_data); + delete stats_data; +} + +void +er_print_experiment::statistics_item (Stats_data *stats_data) +{ + int size, index; + Stats_data::Stats_item stats_item; + char buf[256]; + size = stats_data->size (); + for (index = 0; index < size; index++) + { + stats_item = stats_data->fetch (index); + fprintf (out_file, fmt1, stats_item.label); + fprintf (out_file, fmt2, stats_item.value.to_str (buf, sizeof (buf))); + } + fprintf (out_file, nl); +} + +// Print annotated source or disassembly -- called by er_print only +void +print_anno_file (char *name, const char *sel, const char *srcFile, + bool isDisasm, FILE *dis_file, FILE *inp_file, FILE *out_file, + DbeView *dbev, bool xdefault) +{ + Histable *obj; + Function *func; + Module *module; + Vector<int> *marks; + Hist_data *hist_data; + char *errstr; + int index; + SourceFile *fitem; + int threshold; + int compcom_bits; + int src_visible; + bool hex_visible; + bool srcmetrics_visible; + + if ((name == NULL) || (strlen (name) == 0)) + { + fprintf (stderr, GTXT ("Error: No function or file has been specified.\n")); + return; + } + + // find the function from the name + if (!dbeSession->find_obj (dis_file, inp_file, obj, name, sel, + Histable::FUNCTION, xdefault)) + return; + + if (obj != NULL) + { + // source or disassembly for <Total>, <Unknown>, or @plt + if (obj->get_type () != Histable::FUNCTION) + { + fprintf (stderr, + GTXT ("Error: %s is not a real function; no source or disassembly available.\n"), + name); + return; + } + + func = (Function *) obj; + if (func->flags & FUNC_FLAG_SIMULATED) + { + fprintf (stderr, + GTXT ("Error: %s is not a real function; no source or disassembly available.\n"), + name); + return; + } + else if (dbev != NULL && isDisasm) + dbev->set_func_scope (true); + + // function found, set module + module = func->module; + int ix = module->loadobject->seg_idx; + if (dbev->get_lo_expand (ix) == LIBEX_HIDE) + { + char *lo_name = module->loadobject->get_name (); + fprintf (stderr, + GTXT ("Error: No source or disassembly available for hidden object %s.\n"), + lo_name); + return; + } + + if (srcFile) + { + Vector<SourceFile*> *sources = func->get_sources (); + bool found = false; + if (sources == NULL) + { + fitem = func->getDefSrc (); + found = (func->line_first > 0) + && strcmp (basename (srcFile), + basename (fitem->get_name ())) == 0; + } + else + { + Vec_loop (SourceFile*, sources, index, fitem) + { + if (strcmp (basename (srcFile), basename (fitem->get_name ())) == 0) + { + found = true; + break; + } + } + } + if (!found) + { + fprintf (stderr, GTXT ("Error: Source file context %s does not contribute to function `%s'.\n"), + srcFile, name); + return; + } + } + } + else + { + // function not found + if (sel && strrchr (sel, ':')) + { + // 'sel' was "@seg_num:address" or "file_name:address" + fprintf (stderr, + GTXT ("Error: No function with given name `%s %s' found.\n"), + name, sel); + return; + } + // search for a file of that name + if (!dbeSession->find_obj (dis_file, inp_file, obj, name, sel, + Histable::MODULE, xdefault)) + return; + + if (obj == NULL) + { // neither function nor file found + fprintf (stderr, GTXT ("Error: No function or file with given name `%s' found.\n"), + name); + return; + } + + func = NULL; + module = (Module *) obj; + int ix = module->loadobject->seg_idx; + if (dbev->get_lo_expand (ix) == LIBEX_HIDE) + { + char *lo_name = module->loadobject->get_name (); + fprintf (stderr, GTXT ("Error: No source or disassembly available for hidden object %s.\n"), + lo_name); + return; + } + if (name) + srcFile = name; + } + + if (module == NULL || module->get_name () == NULL) + { + fprintf (stderr, GTXT ("Error: Object name not recorded in experiment\n")); + return; + } + module->read_stabs (); + + if (!isDisasm && (module->file_name == NULL + || (module->flags & MOD_FLAG_UNKNOWN) != 0 + || *module->file_name == 0)) + { + fprintf (stderr, GTXT ("Error: Source location not recorded in experiment\n")); + return; + } + + MetricList *metric_list = dbev->get_metric_list (MET_NORMAL); + int sort_ref_index = metric_list->get_sort_ref_index (); + if (isDisasm) + metric_list->set_sort_ref_index (-1); + + // Ask DbeView to generate function-level data + // MSI: I think this is used only to get totals to compute percentages + hist_data = dbev->get_hist_data (metric_list, Histable::FUNCTION, 0, + Hist_data::ALL); + MetricList *nmlist = hist_data->get_metric_list (); + metric_list->set_sort_ref_index (sort_ref_index); + if (nmlist->get_items ()->size () != 0 + && hist_data->get_status () != Hist_data::SUCCESS) + { + errstr = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + if (errstr) + { + fprintf (stderr, GTXT ("Error: %s\n"), errstr); + free (errstr); + } + return; + } + + marks = new Vector<int>; + if (isDisasm) + { + threshold = dbev->get_thresh_dis (); + compcom_bits = dbev->get_dis_compcom (); + src_visible = dbev->get_src_visible (); + hex_visible = dbev->get_hex_visible (); + srcmetrics_visible = dbev->get_srcmetric_visible (); + } + else + { + threshold = dbev->get_thresh_src (); + compcom_bits = dbev->get_src_compcom (); + src_visible = SRC_NA; + hex_visible = false; + srcmetrics_visible = false; + } + + dump_anno_file (out_file, isDisasm ? Histable::INSTR : Histable::LINE, + module, dbev, nmlist, hist_data->get_totals ()->value, + srcFile, func, marks, threshold, compcom_bits, + src_visible, hex_visible, srcmetrics_visible); + + delete marks; + + errstr = module->anno_str (); + if (errstr) + { + fprintf (stderr, GTXT ("Error: %s\n"), errstr); + free (errstr); + } + delete hist_data; +} + +void +print_html_title (FILE *out_file, char *title) +{ + // This will print a header row for the report + fprintf (out_file, "<html><title>%s</title>\n", title); + fprintf (out_file, "<center><h3>%s</h3></center>\n", title); +} + +void +print_html_label (FILE *out_file, MetricList *metrics_list) +{ + int mlist_sz; + + // This will print a header row for the metrics + Vector<Metric*> *mlist = metrics_list->get_items (); + mlist_sz = mlist->size (); + + fprintf (out_file, "<style type=\"text/css\">\n"); + fprintf (out_file, "<!--\nBODY\n"); + fprintf (out_file, ".th_C { text-align:center; background-color:lightgoldenrodyellow; }\n"); + fprintf (out_file, ".th_CG { text-align:center; background-color:#ffff33; }\n"); + fprintf (out_file, ".th_L { text-align:left; background-color:lightgoldenrodyellow; }\n"); + fprintf (out_file, ".th_LG { text-align:left; background-color:#ffff33; }\n"); + fprintf (out_file, ".td_R { text-align:right; }\n"); + fprintf (out_file, ".td_RG { text-align:right; background-color:#ffff33; }\n"); + fprintf (out_file, ".td_L { text-align:left; }\n"); + fprintf (out_file, ".td_LG { text-align:left; background-color:#ffff33; }\n"); + fprintf (out_file, "-->\n</style>"); + fprintf (out_file, "<center><table border=1 cellspacing=2>\n<tr>"); + + for (int index = 0; index < mlist_sz; index++) + { + Metric *mitem = mlist->fetch (index); + int ncols = 0; + if (mitem->is_visible ()) + ncols++; + if (mitem->is_tvisible ()) + ncols++; + if (mitem->is_pvisible ()) + ncols++; + if (ncols == 0) + continue; + char *name = strdup (mitem->get_name ()); + char *name2 = split_metric_name (name); + const char *style = index == metrics_list->get_sort_ref_index () ? "G" : ""; + + // start the column, with colspan setting, legend, and sort metric indicator + if (ncols == 1) + { + if (mitem->get_vtype () == VT_LABEL) + // left-adjust the name metric + fprintf (out_file, + "<th class=\"th_L%s\">%s <br>%s %s <br>%s </th>", + style, mitem->legend == NULL ? " " : mitem->legend, + (index == metrics_list->get_sort_ref_index ()) ? "∇" : " ", + name, name2 == NULL ? " " : name2); + else + // but center the others + fprintf (out_file, + "<th class=\"th_C%s\">%s <br>%s %s <br>%s </th>", + style, mitem->legend == NULL ? " " : mitem->legend, + (index == metrics_list->get_sort_ref_index ()) ? + "∇" : " ", + name, name2 == NULL ? NTXT (" ") : name2); + } + else + // name metric can't span columns + fprintf (out_file, + "<th colspan=%d class=\"th_C%s\">%s <br>%s %s <br>%s </th>", + ncols, style, + mitem->legend == NULL ? " " : mitem->legend, + index == metrics_list->get_sort_ref_index () ? + "∇" : " ", + name, name2 == NULL ? " " : name2); + + free (name); + } + + // end this row, start the units row + fprintf (out_file, NTXT ("</tr>\n<tr>")); + + // now do the units row + for (int index = 0; index < mlist_sz; index++) + { + Metric *mitem = mlist->fetch (index); + const char *style = index == metrics_list->get_sort_ref_index () ? "G" : ""; + + if (mitem->is_tvisible ()) + fprintf (out_file, "<th class=\"th_C%s\"> (%s)</th>", style, + GTXT ("sec.")); + if (mitem->is_visible ()) + { + if (mitem->get_abbr_unit () == NULL) + fprintf (out_file, "<th class=\"th_C%s\"> </th>", style); + else + fprintf (out_file, "<th class=\"th_C%s\">(%s)</th>", style, + mitem->get_abbr_unit () == NULL ? " " + : mitem->get_abbr_unit ()); + } + if (mitem->is_pvisible ()) + fprintf (out_file, "<th class=\"th_C%s\"> (%%)</th>", style); + } + fprintf (out_file, NTXT ("</tr>\n")); +} + +void +print_html_content (FILE *out_file, Hist_data *data, MetricList *metrics_list, + int limit, Histable::NameFormat nfmt) +{ + Hist_data::HistItem *item; + + // printing contents. + for (int i = 0; i < limit; i++) + { + item = data->fetch (i); + print_html_one (out_file, data, item, metrics_list, nfmt); + } +} + +void +print_html_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + MetricList *metrics_list, Histable::NameFormat nfmt) +{ + Metric *mitem; + int index; + int visible, tvisible, pvisible; + TValue *value; + double percent; + + fprintf (out_file, NTXT ("<tr>")); + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + visible = mitem->is_visible (); + tvisible = mitem->is_tvisible (); + pvisible = mitem->is_pvisible (); + const char *style = index == metrics_list->get_sort_ref_index () ? "G" : ""; + + if (tvisible) + { + value = &(item->value[index]); + if (value->ll == 0LL) + fprintf (out_file, + "<td class=\"td_R%s\"><tt>0. </tt></td>", + style); + else + fprintf (out_file, "<td class=\"td_R%s\"><tt>%4.3lf</tt></td>", + style, 1.e-6 * value->ll / dbeSession->get_clock (-1)); + } + + if (visible) + { + if (mitem->get_vtype () == VT_LABEL) + { + value = &(item->value[index]); + char *r; + if (value->tag == VT_OFFSET) + r = ((DataObject*) (item->obj))->get_offset_name (); + else + r = item->obj->get_name (nfmt); + char *n = html_ize_name (r); + fprintf (out_file, NTXT ("<td class=\"td_L%s\">%s</td>"), style, n); + free (n); + } + else + { + value = &(item->value[index]); + switch (value->tag) + { + case VT_DOUBLE: + if (value->d == 0.0) + fprintf (out_file, + "<td class=\"td_R%s\"><tt>0. </tt></td>", + style); + else + fprintf (out_file, + "<td class=\"td_R%s\"><tt>%4.3lf</tt></td>", style, + value->d); + break; + case VT_INT: + fprintf (out_file, "<td class=\"td_R%s\"><tt>%d</tt></td>", + style, value->i); + break; + case VT_LLONG: + fprintf (out_file, "<td class=\"td_R%s\"><tt>%lld</td></tt>", + style, value->ll); + break; + case VT_ULLONG: + fprintf (out_file, "<td class=\"td_R%s\"><tt>%llu</td></tt>", + style, value->ull); + break; + case VT_ADDRESS: + fprintf (out_file, + "<td class=\"td_R%s\"><tt>%u:0x%08x</tt></td>", style, + ADDRESS_SEG (value->ll), ADDRESS_OFF (value->ll)); + break; + case VT_FLOAT: + if (value->f == 0.0) + fprintf (out_file, + "<td class=\"td_R%s\"><tt>0. </tt></td>", + style); + else + fprintf (out_file, + "<td class=\"td_R%s\"><tt>%4.3f</tt></td>", + style, value->f); + break; + case VT_SHORT: + fprintf (out_file, "<td class=\"td_R%s\"><tt>%d</tt></td>", + style, value->s); + break; + // ignoring the following cases (why?) + case VT_HRTIME: + case VT_LABEL: + case VT_OFFSET: + break; + } + } + } + + if (pvisible) + { + percent = data->get_percentage (item->value[index].to_double (), index); + if (percent == 0.0) + // adjust to change format from xx.yy% + fprintf (out_file, "<td class=\"td_R%s\">0. </td>", + style); + else + // adjust format below to change format from xx.yy% + fprintf (out_file, "<td class=\"td_R%s\">%3.2f</td>", style, + (100.0 * percent)); + } + } + fprintf (out_file, NTXT ("</tr>\n")); +} + +void +print_html_trailer (FILE *out_file) +{ + fprintf (out_file, NTXT ("</table></center></html>\n")); +} + +static char * +del_delim (char *s) +{ + size_t len = strlen (s); + if (len > 0) + s[len - 1] = 0; + return s; +} + +void +print_delim_label (FILE *out_file, MetricList *metrics_list, char delim) +{ + char line0[2 * MAX_LEN], line1[2 * MAX_LEN]; + char line2[2 * MAX_LEN], line3[2 * MAX_LEN]; + size_t len; + + // This will print four header rows for the metrics + line0[0] = 0; + line1[0] = 0; + line2[0] = 0; + line3[0] = 0; + Vector<Metric*> *mlist = metrics_list->get_items (); + for (int index = 0, mlist_sz = mlist->size (); index < mlist_sz; index++) + { + Metric *mitem = mlist->fetch (index); + if (!(mitem->is_visible () || mitem->is_tvisible () + || mitem->is_pvisible ())) + continue; + char *name = strdup (mitem->get_name ()); + char *name2 = split_metric_name (name); + + if (mitem->is_tvisible ()) + { + len = strlen (line0); + snprintf (line0 + len, sizeof (line0) - len, NTXT ("\"%s\"%c"), + mitem->legend == NULL ? NTXT ("") : mitem->legend, delim); + len = strlen (line1); + snprintf (line1 + len, sizeof (line1) - len, NTXT ("\"%s\"%c"), + name, delim); + len = strlen (line2); + snprintf (line2 + len, sizeof (line2) - len, NTXT ("\"%s\"%c"), + name2 == NULL ? NTXT ("") : name2, delim); + len = strlen (line3); + if (index == metrics_list->get_sort_ref_index ()) + snprintf (line3 + len, sizeof (line3) - len, NTXT ("\"V %s\"%c"), + GTXT ("(sec.)"), delim); + else + snprintf (line3 + len, sizeof (line3) - len, NTXT ("\" %s\"%c"), + GTXT ("(sec.)"), delim); + } + if (mitem->is_visible ()) + { + len = strlen (line0); + snprintf (line0 + len, sizeof (line0) - len, "\"%s\"%c", + mitem->legend == NULL ? "" : mitem->legend, delim); + + len = strlen (line1); + snprintf (line1 + len, sizeof (line1) - len, "\"%s\"%c", + name, delim); + + len = strlen (line2); + snprintf (line2 + len, sizeof (line2) - len, "\"%s\"%c", + name2 == NULL ? NTXT ("") : name2, delim); + + len = strlen (line3); + char *au = mitem->get_abbr_unit (); + + if (index == metrics_list->get_sort_ref_index ()) + { + if (au == NULL) + snprintf (line3 + len, sizeof (line3) - len, "\"V \"%c", delim); + else + snprintf (line3 + len, sizeof (line3) - len, "\"V (%s)\"%c", + au, delim); + } + else + { + if (au == NULL) + snprintf (line3 + len, sizeof (line3) - len, "\" \"%c", + delim); + else + snprintf (line3 + len, sizeof (line3) - len, "\" (%s)\"%c", + au, delim); + } + } + if (mitem->is_pvisible ()) + { + len = strlen (line0); + snprintf (line0 + len, sizeof (line0) - len, NTXT ("\"%s\"%c"), + mitem->legend == NULL ? NTXT ("") : mitem->legend, delim); + + len = strlen (line1); + snprintf (line1 + len, sizeof (line1) - len, NTXT ("\"%s\"%c"), + name, delim); + + len = strlen (line2); + snprintf (line2 + len, sizeof (line2) - len, NTXT ("\"%s\"%c"), + name2 == NULL ? NTXT ("") : name2, delim); + + len = strlen (line3); + if (index == metrics_list->get_sort_ref_index ()) + snprintf (line3 + len, sizeof (line3) - len, NTXT ("\"V %s\"%c"), + NTXT ("%%"), delim); + else + snprintf (line3 + len, sizeof (line3) - len, NTXT ("\" %s\"%c"), + NTXT ("%%"), delim); + } + free (name); + } + // now remove the trailing delimiter, and print the four lines + fprintf (out_file, NTXT ("%s\n"), del_delim (line0)); + fprintf (out_file, NTXT ("%s\n"), del_delim (line1)); + fprintf (out_file, NTXT ("%s\n"), del_delim (line2)); + fprintf (out_file, NTXT ("%s\n"), del_delim (line3)); +} + +void +print_delim_content (FILE *out_file, Hist_data *data, MetricList *metrics_list, + int limit, Histable::NameFormat nfmt, char delim) +{ + Hist_data::HistItem *item; + int i; + + // printing contents. + for (i = 0; i < limit; i++) + { + item = data->fetch (i); + print_delim_one (out_file, data, item, metrics_list, nfmt, delim); + } +} + +void +print_delim_trailer (FILE */*out_file*/, char /*delim*/) { } + +// EUGENE does this function work properly when "-compare ratio" is used? +// how about when the ratio is nonzero-divided-by-zero? +// EUGENE actually, review this entire file + +void +print_delim_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + MetricList *metrics_list, Histable::NameFormat nfmt, + char delim) +{ + Metric *mitem; + int index; + int visible, tvisible, pvisible; + TValue *value; + double percent; + size_t len; + + char line1[2 * MAX_LEN]; + *line1 = 0; + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + visible = mitem->is_visible (); + tvisible = mitem->is_tvisible (); + pvisible = mitem->is_pvisible (); + if (tvisible) + { + value = &(item->value[index]); + len = strlen (line1); + if (value->ll == 0LL) + snprintf (line1 + len, sizeof (line1) - len, "\"0.\"%c", delim); + else + snprintf (line1 + len, sizeof (line1) - len, "\"%4.3lf\"%c", + 1.e-6 * value->ll / dbeSession->get_clock (-1), + delim); + } + + if (visible) + { + len = strlen (line1); + if (mitem->get_vtype () == VT_LABEL) + { + value = &(item->value[index]); + char *r; + if (value->tag == VT_OFFSET) + r = ((DataObject*) (item->obj))->get_offset_name (); + else + r = item->obj->get_name (nfmt); + char *p = csv_ize_name (r, delim); + snprintf (line1 + len, sizeof (line1) - len, "\"%s\"%c", p, delim); + free (p); + } + else + { + value = &(item->value[index]); + switch (value->tag) + { + case VT_DOUBLE: + if (value->d == 0.0) + snprintf (line1 + len, sizeof (line1) - len, "\"0.\"%c", + delim); + else + snprintf (line1 + len, sizeof (line1) - len, "\"%4.3lf\"%c", + value->d, delim); + break; + case VT_INT: + snprintf (line1 + len, sizeof (line1) - len, "\"%d\"%c", + value->i, delim); + break; + case VT_LLONG: + snprintf (line1 + len, sizeof (line1) - len, "\"%lld\"%c", + value->ll, delim); + break; + case VT_ULLONG: + snprintf (line1 + len, sizeof (line1) - len, "\"%llu\"%c", + value->ull, delim); + break; + case VT_ADDRESS: + snprintf (line1 + len, sizeof (line1) - len, "\"%u:0x%08x\"%c", + ADDRESS_SEG (value->ll), + ADDRESS_OFF (value->ll), delim); + break; + case VT_FLOAT: + if (value->f == 0.0) + snprintf (line1 + len, sizeof (line1) - len, "\"0.\"%c", + delim); + else + snprintf (line1 + len, sizeof (line1) - len, "\"%4.3f\"%c", + value->f, delim); + break; + case VT_SHORT: + snprintf (line1 + len, sizeof (line1) - len, "\"%d\"%c", + value->s, delim); + break; + // ignoring the following cases (why?) + case VT_HRTIME: + case VT_LABEL: + case VT_OFFSET: + break; + } + } + } + + if (pvisible) + { + len = strlen (line1); + percent = data->get_percentage (item->value[index].to_double (), index); + if (percent == 0.0) + // adjust to change format from xx.yy% + snprintf (line1 + len, sizeof (line1) - len, "\"0.\"%c", delim); + else + // adjust format below to change format from xx.yy% + snprintf (line1 + len, sizeof (line1) - len, "\"%3.2f\"%c", + (100.0 * percent), delim); + } + } + fprintf (out_file, NTXT ("%s\n"), del_delim (line1)); +} + +char * +html_ize_name (char *name) +{ + StringBuilder sb; + for (size_t i = 0; i < strlen (name); i++) + { + switch (name[i]) + { + case ' ': sb.append (NTXT (" ")); + break; + case '"': sb.append (NTXT (""")); + break; + case '&': sb.append (NTXT ("&")); + break; + case '<': sb.append (NTXT ("<")); + break; + case '>': sb.append (NTXT (">")); + break; + default: sb.append (name[i]); + break; + } + } + char *ret = sb.toString (); + return ret; +} + +char * +csv_ize_name (char *name, char /*delim*/) +{ + StringBuilder sb; + for (size_t i = 0; i < strlen (name); i++) + sb.append (name[i]); + char *ret = sb.toString (); + return ret; +} + +// Split a metric name into two parts, replacing a blank with +// a zero and returning pointer to the rest of the string, or +// leaving the string unchanged, and returning NULL; + +char * +split_metric_name (char *name) +{ + // figure out the most even split of the name + size_t len = strlen (name); + char *middle = &name[len / 2]; + + // find the first blank + char *first = strchr (name, (int) ' '); + if (first == NULL) // no blanks + return NULL; + char *last = first; + char *p = first; + for (;;) + { + p = strchr (p + 1, (int) ' '); + if (p == NULL) + break; + if (p < middle) + { + first = p; + last = p; + } + else + { + last = p; + break; + } + } + // pick the better of the two + char *ret; + int f = (int) (middle - first); + int l = (int) (last - middle); + if ((first == last) || (f <= l)) + { + *first = '\0'; + ret = first + 1; + } + else + { + *last = '\0'; + ret = last + 1; + } + return ret; +} diff --git a/gprofng/src/Print.h b/gprofng/src/Print.h new file mode 100644 index 0000000..4bc6655 --- /dev/null +++ b/gprofng/src/Print.h @@ -0,0 +1,283 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _PRINT_H +#define _PRINT_H + + +// Include files +#include <stdio.h> +#include <stdlib.h> +#include "dbe_types.h" +#include "Metric.h" +#include "Hist_data.h" +#include "Ovw_data.h" +#include "Stats_data.h" +#include "Emsg.h" +#include "Exp_Layout.h" +#include "DefaultMap.h" +#include "FileData.h" +#include "HeapData.h" +#include "HashMap.h" + +const char nl[] = "\n"; +const char tab[] = "\t"; + +// Printing options. +enum Print_destination +{ + DEST_PRINTER = 0, + DEST_FILE = 1, + DEST_OPEN_FILE = 2 +}; + +enum Print_mode +{ + MODE_LIST, + MODE_DETAIL, + MODE_GPROF, + MODE_ANNOTATED +}; + +struct Print_params +{ + Print_destination dest; // printer or file + char *name; // of printer or file + int ncopies; // # of copies + bool header; // print header first + FILE *openfile; // if destination is DEST_OPEN_FILE +}; + +class Experiment; +class MetricList; +class DbeView; +class Stack_coverage; +class Function; +class LoadObject; + +// Class Definitions +class er_print_common_display +{ +public: + er_print_common_display () + { + out_file = NULL; + pr_params.header = false; + } + + virtual ~er_print_common_display () { } + + // Open the file/printer to write to + int open (Print_params *); + + void + set_out_file (FILE *o) + { + out_file = o; + pr_params.dest = DEST_FILE; + } + + // Print the final output data. This function calls + // data_dump() to actually do the dumping of data. + bool print_output (); + + // Print the output in the appropriate format. + virtual void data_dump () = 0; + + void header_dump (int exp_idx); + + // Return the report. If the report size is greater than max, return truncated report + // Allocates memory, so the caller should free this memory. + char *get_output (int max); + +protected: + DbeView *dbev; + FILE *out_file; + Print_params pr_params; + char *tmp_file; + int exp_idx1, exp_idx2; + bool load; + bool header; +}; + +class er_print_histogram : public er_print_common_display +{ +public: + er_print_histogram (DbeView *dbv, Hist_data *data, MetricList *metrics_list, + Print_mode disp_type, int limit, char *sort_name, + Histable *sobj, bool show_load, bool show_header); + void data_dump (); + +private: + void dump_list (int limit); + void dump_detail (int limit); + void get_gprof_width (Metric::HistMetric *hist_metric, int limit); + void dump_gprof (int limit); + void dump_annotated_dataobjects (Vector<int> *marks, int threshold); + void dump_annotated (); + + Stack_coverage *stack_cov; + Hist_data *hist_data; + MetricList *mlist; + Print_mode type; + int number_entries; + char *sort_metric; + Histable *sel_obj; +}; + +class er_print_ctree : public er_print_common_display +{ +public: + er_print_ctree (DbeView *dbv, Vector<Histable*> *cstack, Histable *sobj, + int limit); + void data_dump (); + void print_children (Hist_data *data, int index, Histable *obj, char *prefix, + Hist_data::HistItem *total); + +private: + Vector<Histable*> *cstack; + Histable *sobj; + MetricList *mlist; + Metric::HistMetric *hist_metric; + char **fmt_int; + char **fmt_real0; + char **fmt_real1; + int limit; + int print_row; +}; + +class er_print_gprof : public er_print_common_display +{ +public: + er_print_gprof (DbeView *dbv, Vector<Histable*> *cstack); + void data_dump (); +private: + Vector<Histable*> *cstack; +}; + +class er_print_leaklist : public er_print_common_display +{ +public: + er_print_leaklist (DbeView *dbv, bool show_leak, + bool show_alloca, int limit); + void data_dump (); + +private: + bool leak; + bool alloca; + int limit; +}; + +class er_print_heapactivity : public er_print_common_display +{ +public: + er_print_heapactivity (DbeView *_dbev, Histable::Type _type, + bool _printStat, int _limit); + void data_dump (); + +private: + void printStatistics (Hist_data *hist_data); + void printCallStacks (Hist_data *hist_data); + + Histable::Type type; + bool printStat; + int limit; +}; + +class er_print_ioactivity : public er_print_common_display +{ +public: + er_print_ioactivity (DbeView *_dbev, Histable::Type _type, + bool _printStat, int _limit); + void data_dump (); + +private: + void printStatistics (Hist_data *hist_data); + void printCallStacks (Hist_data *hist_data); + + Histable::Type type; + bool printStat; + int limit; +}; + +class er_print_experiment : public er_print_common_display +{ +public: + er_print_experiment (DbeView *me, int bgn_idx, int end_idx, bool show_load, + bool show_header, bool show_stat, bool show_over, bool show_odetail); + void data_dump (); + +private: + char fmt1[32], fmt2[32], fmt3[32], fmt4[32]; + // buffers shared by the following functions + void overview_sum (int &maxlen); + void overview_dump (int exp_idx, int &maxlen); + void overview_summary (Ovw_data *ovw_data, int &maxlen); + void overview_item (Ovw_data::Ovw_item *ovw_item, + Ovw_data::Ovw_item *ovw_item_labels); + void overview_value (Value *value, ValueTag value_tag, + double total_value); + void statistics_sum (int &maxlen); + void statistics_dump (int exp_idx, int &maxlen); + void statistics_item (Stats_data *stats_data); + + bool stat; + bool over; + bool odetail; +}; + +// Print the header. Experiment name and the sample +// selection, along with the percentage. +char *pr_load_objects (Vector<LoadObject*> *loadobjects, char *lead); +char *pr_samples (Experiment *exp); +char *pr_mesgs (Emsg *msg, const char *null_str, const char *lead); +void print_load_object (FILE *out_file); +void print_header (Experiment *exp, FILE *out_file); + +// Print Function metrics +void get_width (Hist_data *data, MetricList *metrics_list, + Metric::HistMetric *hist_metric); +void get_format (char **fmt_int, char **fmt_real0, char **fmt_real1, + MetricList *metrics_list, Metric::HistMetric *hist_metric, + int nspace); +int print_label (FILE *out_file, MetricList *metrics_list, + Metric::HistMetric *hist_metric, int space); +void print_anno_file (char *name, const char *sel, const char *srcFile, + bool isDisasm, FILE *dis_file, FILE *inp_file, + FILE *out_file, DbeView *dbev, bool xdefault); +void print_html_title (FILE *out_file, char *title); +void print_html_label (FILE *out_file, MetricList *metrics_list); +void print_html_content (FILE *out_file, Hist_data *d, MetricList *metrics_list, + int limit, Histable::NameFormat nfmt); +void print_html_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + MetricList *metrics_list, Histable::NameFormat nfmt); +void print_html_trailer (FILE* out_file); +char *html_ize_name (char *name); +void print_delim_label (FILE *out_file, MetricList *metrics_list, char delim); +void print_delim_content (FILE *out_file, Hist_data *data, + MetricList *metrics_list, int limit, + Histable::NameFormat nfmt, char delim); +void print_delim_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + MetricList *metrics_list, Histable::NameFormat nfmt, char delim); +void print_delim_trailer (FILE* out_file, char delim); +char *csv_ize_name (char *name, char delim); +char *split_metric_name (char *name); + +#endif diff --git a/gprofng/src/QLParser.h b/gprofng/src/QLParser.h new file mode 100644 index 0000000..c4665e8 --- /dev/null +++ b/gprofng/src/QLParser.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _QLPARSER_H +#define _QLPARSER_H + +#include <sstream> +#include <istream> +#include <iostream> +#include "Expression.h" + +/* This class contains parser inputs (a string, if non-NULL: if NULL, use cin), + and outputs (obtained via operator(), which resets the output + expression). The destructor deletes the returned expression to allow + exception throws on syntax error to clean up properly. */ + +namespace QL +{ + struct Result + { + std::stringstream streamify; + public: + std::istream in; + Expression *out; + + Result () : in (std::cin.rdbuf ()), out (NULL) { } + Result (const char *instr) : streamify (std::string (instr)), + in (streamify.rdbuf ()), out (NULL) { } + + Expression *operator() () + { + Expression *o = out; + out = NULL; + return o; + } + + ~Result () + { + delete out; + } + }; +}; + +#endif /* _QLPARSER_H */ diff --git a/gprofng/src/QLParser.tab.cc b/gprofng/src/QLParser.tab.cc new file mode 100644 index 0000000..4517b2e --- /dev/null +++ b/gprofng/src/QLParser.tab.cc @@ -0,0 +1,1453 @@ +// A Bison parser, made by GNU Bison 3.7.5. + +// Skeleton implementation for Bison LALR(1) parsers in C++ + +// Copyright (C) 2002-2015, 2018-2021 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 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 <http://www.gnu.org/licenses/>. + +// As a special exception, you may create a larger work that contains +// part or all of the Bison parser skeleton and distribute that work +// under terms of your choice, so long as that work isn't itself a +// parser generator using the skeleton or a modified version thereof +// as a parser skeleton. Alternatively, if you modify or redistribute +// the parser skeleton itself, you may (at your option) remove this +// special exception, which will cause the skeleton and the resulting +// Bison output files to be licensed under the GNU General Public +// License without this special exception. + +// This special exception was added by the Free Software Foundation in +// version 2.2 of Bison. + +// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, +// especially those whose name start with YY_ or yy_. They are +// private implementation details that can be changed or removed. + +// "%code top" blocks. +#line 28 "QLParser.yy" + +#include <stdio.h> +#include <string.h> +#include <string> + +#line 45 "QLParser.tab.cc" + + + + +#include "QLParser.tab.hh" + + +// Unqualified %code blocks. +#line 42 "QLParser.yy" + +namespace QL +{ + static QL::Parser::symbol_type yylex (QL::Result &result); +} + +#line 61 "QLParser.tab.cc" + + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> // FIXME: INFRINGES ON USER NAME SPACE. +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + + +// Whether we are compiled with exception support. +#ifndef YY_EXCEPTIONS +# if defined __GNUC__ && !defined __EXCEPTIONS +# define YY_EXCEPTIONS 0 +# else +# define YY_EXCEPTIONS 1 +# endif +#endif + + + +// Enable debugging if requested. +#if YYDEBUG + +// A pseudo ostream that takes yydebug_ into account. +# define YYCDEBUG if (yydebug_) (*yycdebug_) + +# define YY_SYMBOL_PRINT(Title, Symbol) \ + do { \ + if (yydebug_) \ + { \ + *yycdebug_ << Title << ' '; \ + yy_print_ (*yycdebug_, Symbol); \ + *yycdebug_ << '\n'; \ + } \ + } while (false) + +# define YY_REDUCE_PRINT(Rule) \ + do { \ + if (yydebug_) \ + yy_reduce_print_ (Rule); \ + } while (false) + +# define YY_STACK_PRINT() \ + do { \ + if (yydebug_) \ + yy_stack_print_ (); \ + } while (false) + +#else // !YYDEBUG + +# define YYCDEBUG if (false) std::cerr +# define YY_SYMBOL_PRINT(Title, Symbol) YY_USE (Symbol) +# define YY_REDUCE_PRINT(Rule) static_cast<void> (0) +# define YY_STACK_PRINT() static_cast<void> (0) + +#endif // !YYDEBUG + +#define yyerrok (yyerrstatus_ = 0) +#define yyclearin (yyla.clear ()) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYRECOVERING() (!!yyerrstatus_) + +#line 50 "QLParser.yy" +namespace QL { +#line 135 "QLParser.tab.cc" + + /// Build a parser object. + Parser::Parser (QL::Result &result_yyarg) +#if YYDEBUG + : yydebug_ (false), + yycdebug_ (&std::cerr), +#else + : +#endif + result (result_yyarg) + {} + + Parser::~Parser () + {} + + Parser::syntax_error::~syntax_error () YY_NOEXCEPT YY_NOTHROW + {} + + /*---------------. + | symbol kinds. | + `---------------*/ + + + + // by_state. + Parser::by_state::by_state () YY_NOEXCEPT + : state (empty_state) + {} + + Parser::by_state::by_state (const by_state& that) YY_NOEXCEPT + : state (that.state) + {} + + void + Parser::by_state::clear () YY_NOEXCEPT + { + state = empty_state; + } + + void + Parser::by_state::move (by_state& that) + { + state = that.state; + that.clear (); + } + + Parser::by_state::by_state (state_type s) YY_NOEXCEPT + : state (s) + {} + + Parser::symbol_kind_type + Parser::by_state::kind () const YY_NOEXCEPT + { + if (state == empty_state) + return symbol_kind::S_YYEMPTY; + else + return YY_CAST (symbol_kind_type, yystos_[+state]); + } + + Parser::stack_symbol_type::stack_symbol_type () + {} + + Parser::stack_symbol_type::stack_symbol_type (YY_RVREF (stack_symbol_type) that) + : super_type (YY_MOVE (that.state)) + { + switch (that.kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.YY_MOVE_OR_COPY< Expression * > (YY_MOVE (that.value)); + break; + + default: + break; + } + +#if 201103L <= YY_CPLUSPLUS + // that is emptied. + that.state = empty_state; +#endif + } + + Parser::stack_symbol_type::stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) that) + : super_type (s) + { + switch (that.kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.move< Expression * > (YY_MOVE (that.value)); + break; + + default: + break; + } + + // that is emptied. + that.kind_ = symbol_kind::S_YYEMPTY; + } + +#if YY_CPLUSPLUS < 201103L + Parser::stack_symbol_type& + Parser::stack_symbol_type::operator= (const stack_symbol_type& that) + { + state = that.state; + switch (that.kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.copy< Expression * > (that.value); + break; + + default: + break; + } + + return *this; + } + + Parser::stack_symbol_type& + Parser::stack_symbol_type::operator= (stack_symbol_type& that) + { + state = that.state; + switch (that.kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.move< Expression * > (that.value); + break; + + default: + break; + } + + // that is emptied. + that.state = empty_state; + return *this; + } +#endif + + template <typename Base> + void + Parser::yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const + { + if (yymsg) + YY_SYMBOL_PRINT (yymsg, yysym); + } + +#if YYDEBUG + template <typename Base> + void + Parser::yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const + { + std::ostream& yyoutput = yyo; + YY_USE (yyoutput); + if (yysym.empty ()) + yyo << "empty symbol"; + else + { + symbol_kind_type yykind = yysym.kind (); + yyo << (yykind < YYNTOKENS ? "token" : "nterm") + << ' ' << yysym.name () << " ("; + YY_USE (yykind); + yyo << ')'; + } + } +#endif + + void + Parser::yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym) + { + if (m) + YY_SYMBOL_PRINT (m, sym); + yystack_.push (YY_MOVE (sym)); + } + + void + Parser::yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym) + { +#if 201103L <= YY_CPLUSPLUS + yypush_ (m, stack_symbol_type (s, std::move (sym))); +#else + stack_symbol_type ss (s, sym); + yypush_ (m, ss); +#endif + } + + void + Parser::yypop_ (int n) + { + yystack_.pop (n); + } + +#if YYDEBUG + std::ostream& + Parser::debug_stream () const + { + return *yycdebug_; + } + + void + Parser::set_debug_stream (std::ostream& o) + { + yycdebug_ = &o; + } + + + Parser::debug_level_type + Parser::debug_level () const + { + return yydebug_; + } + + void + Parser::set_debug_level (debug_level_type l) + { + yydebug_ = l; + } +#endif // YYDEBUG + + Parser::state_type + Parser::yy_lr_goto_state_ (state_type yystate, int yysym) + { + int yyr = yypgoto_[yysym - YYNTOKENS] + yystate; + if (0 <= yyr && yyr <= yylast_ && yycheck_[yyr] == yystate) + return yytable_[yyr]; + else + return yydefgoto_[yysym - YYNTOKENS]; + } + + bool + Parser::yy_pact_value_is_default_ (int yyvalue) + { + return yyvalue == yypact_ninf_; + } + + bool + Parser::yy_table_value_is_error_ (int yyvalue) + { + return yyvalue == yytable_ninf_; + } + + int + Parser::operator() () + { + return parse (); + } + + int + Parser::parse () + { + int yyn; + /// Length of the RHS of the rule being reduced. + int yylen = 0; + + // Error handling. + int yynerrs_ = 0; + int yyerrstatus_ = 0; + + /// The lookahead symbol. + symbol_type yyla; + + /// The return value of parse (). + int yyresult; + +#if YY_EXCEPTIONS + try +#endif // YY_EXCEPTIONS + { + YYCDEBUG << "Starting parse\n"; + + + /* Initialize the stack. The initial state will be set in + yynewstate, since the latter expects the semantical and the + location values to have been already stored, initialize these + stacks with a primary value. */ + yystack_.clear (); + yypush_ (YY_NULLPTR, 0, YY_MOVE (yyla)); + + /*-----------------------------------------------. + | yynewstate -- push a new symbol on the stack. | + `-----------------------------------------------*/ + yynewstate: + YYCDEBUG << "Entering state " << int (yystack_[0].state) << '\n'; + YY_STACK_PRINT (); + + // Accept? + if (yystack_[0].state == yyfinal_) + YYACCEPT; + + goto yybackup; + + + /*-----------. + | yybackup. | + `-----------*/ + yybackup: + // Try to take a decision without lookahead. + yyn = yypact_[+yystack_[0].state]; + if (yy_pact_value_is_default_ (yyn)) + goto yydefault; + + // Read a lookahead token. + if (yyla.empty ()) + { + YYCDEBUG << "Reading a token\n"; +#if YY_EXCEPTIONS + try +#endif // YY_EXCEPTIONS + { + symbol_type yylookahead (yylex (result)); + yyla.move (yylookahead); + } +#if YY_EXCEPTIONS + catch (const syntax_error& yyexc) + { + YYCDEBUG << "Caught exception: " << yyexc.what() << '\n'; + error (yyexc); + goto yyerrlab1; + } +#endif // YY_EXCEPTIONS + } + YY_SYMBOL_PRINT ("Next token is", yyla); + + if (yyla.kind () == symbol_kind::S_YYerror) + { + // The scanner already issued an error message, process directly + // to error recovery. But do not keep the error token as + // lookahead, it is too special and may lead us to an endless + // loop in error recovery. */ + yyla.kind_ = symbol_kind::S_YYUNDEF; + goto yyerrlab1; + } + + /* If the proper action on seeing token YYLA.TYPE is to reduce or + to detect an error, take that action. */ + yyn += yyla.kind (); + if (yyn < 0 || yylast_ < yyn || yycheck_[yyn] != yyla.kind ()) + { + goto yydefault; + } + + // Reduce or error. + yyn = yytable_[yyn]; + if (yyn <= 0) + { + if (yy_table_value_is_error_ (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + // Count tokens shifted since error; after three, turn off error status. + if (yyerrstatus_) + --yyerrstatus_; + + // Shift the lookahead token. + yypush_ ("Shifting", state_type (yyn), YY_MOVE (yyla)); + goto yynewstate; + + + /*-----------------------------------------------------------. + | yydefault -- do the default action for the current state. | + `-----------------------------------------------------------*/ + yydefault: + yyn = yydefact_[+yystack_[0].state]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + + /*-----------------------------. + | yyreduce -- do a reduction. | + `-----------------------------*/ + yyreduce: + yylen = yyr2_[yyn]; + { + stack_symbol_type yylhs; + yylhs.state = yy_lr_goto_state_ (yystack_[yylen].state, yyr1_[yyn]); + /* Variants are always initialized to an empty instance of the + correct type. The default '$$ = $1' action is NOT applied + when using variants. */ + switch (yyr1_[yyn]) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + yylhs.value.emplace< Expression * > (); + break; + + default: + break; + } + + + + // Perform the reduction. + YY_REDUCE_PRINT (yyn); +#if YY_EXCEPTIONS + try +#endif // YY_EXCEPTIONS + { + switch (yyn) + { + case 2: // S: %empty +#line 104 "QLParser.yy" + { result.out = new Expression (Expression::OP_NUM, (uint64_t) 1); } +#line 577 "QLParser.tab.cc" + break; + + case 3: // S: exp +#line 105 "QLParser.yy" + { result.out = new Expression (yystack_[0].value.as < Expression * > ()); } +#line 583 "QLParser.tab.cc" + break; + + case 4: // exp: exp DEG exp +#line 107 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_DEG, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 589 "QLParser.tab.cc" + break; + + case 5: // exp: exp MUL exp +#line 108 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_MUL, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 595 "QLParser.tab.cc" + break; + + case 6: // exp: exp DIV exp +#line 109 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_DIV, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 601 "QLParser.tab.cc" + break; + + case 7: // exp: exp REM exp +#line 110 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_REM, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 607 "QLParser.tab.cc" + break; + + case 8: // exp: exp ADD exp +#line 111 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_ADD, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 613 "QLParser.tab.cc" + break; + + case 9: // exp: exp MINUS exp +#line 112 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_MINUS, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 619 "QLParser.tab.cc" + break; + + case 10: // exp: exp LS exp +#line 113 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_LS, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 625 "QLParser.tab.cc" + break; + + case 11: // exp: exp RS exp +#line 114 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_RS, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 631 "QLParser.tab.cc" + break; + + case 12: // exp: exp LT exp +#line 115 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_LT, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 637 "QLParser.tab.cc" + break; + + case 13: // exp: exp LE exp +#line 116 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_LE, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 643 "QLParser.tab.cc" + break; + + case 14: // exp: exp GT exp +#line 117 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_GT, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 649 "QLParser.tab.cc" + break; + + case 15: // exp: exp GE exp +#line 118 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_GE, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 655 "QLParser.tab.cc" + break; + + case 16: // exp: exp EQ exp +#line 119 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_EQ, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 661 "QLParser.tab.cc" + break; + + case 17: // exp: exp NE exp +#line 120 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_NE, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 667 "QLParser.tab.cc" + break; + + case 18: // exp: exp BITAND exp +#line 121 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_BITAND, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 673 "QLParser.tab.cc" + break; + + case 19: // exp: exp BITXOR exp +#line 122 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_BITXOR, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 679 "QLParser.tab.cc" + break; + + case 20: // exp: exp BITOR exp +#line 123 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_BITOR, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 685 "QLParser.tab.cc" + break; + + case 21: // exp: exp AND exp +#line 124 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_AND, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 691 "QLParser.tab.cc" + break; + + case 22: // exp: exp OR exp +#line 125 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_OR, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 697 "QLParser.tab.cc" + break; + + case 23: // exp: exp NEQV exp +#line 126 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_NEQV, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 703 "QLParser.tab.cc" + break; + + case 24: // exp: exp EQV exp +#line 127 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_EQV, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 709 "QLParser.tab.cc" + break; + + case 25: // exp: exp QWE exp COLON exp +#line 128 "QLParser.yy" + { Expression colon = Expression (Expression::OP_COLON, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); + yylhs.value.as < Expression * > () = new Expression (Expression::OP_QWE, yystack_[4].value.as < Expression * > (), &colon); } +#line 716 "QLParser.tab.cc" + break; + + case 26: // exp: exp COMMA exp +#line 130 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_COMMA, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 722 "QLParser.tab.cc" + break; + + case 27: // exp: exp IN exp +#line 131 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_IN, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 728 "QLParser.tab.cc" + break; + + case 28: // exp: exp SOME IN exp +#line 132 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_SOMEIN, yystack_[3].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 734 "QLParser.tab.cc" + break; + + case 29: // exp: exp ORDR IN exp +#line 133 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_ORDRIN, yystack_[3].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 740 "QLParser.tab.cc" + break; + + case 30: // exp: term +#line 134 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (yystack_[0].value.as < Expression * > ()); } +#line 746 "QLParser.tab.cc" + break; + + case 31: // term: MINUS term +#line 136 "QLParser.yy" + { Expression num = Expression (Expression::OP_NUM, (uint64_t) 0); + yylhs.value.as < Expression * > () = new Expression (Expression::OP_MINUS, &num, yystack_[0].value.as < Expression * > ()); } +#line 753 "QLParser.tab.cc" + break; + + case 32: // term: NOT term +#line 138 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_NOT, yystack_[0].value.as < Expression * > (), NULL); } +#line 759 "QLParser.tab.cc" + break; + + case 33: // term: BITNOT term +#line 139 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_BITNOT, yystack_[0].value.as < Expression * > (), NULL); } +#line 765 "QLParser.tab.cc" + break; + + case 34: // term: "(" exp ")" +#line 140 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (yystack_[1].value.as < Expression * > ()); } +#line 771 "QLParser.tab.cc" + break; + + case 35: // term: FNAME "(" QSTR ")" +#line 141 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_FUNC, yystack_[3].value.as < Expression * > (), yystack_[1].value.as < Expression * > ()); } +#line 777 "QLParser.tab.cc" + break; + + case 36: // term: HASPROP "(" "name" ")" +#line 142 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_HASPROP, yystack_[1].value.as < Expression * > (), NULL); } +#line 783 "QLParser.tab.cc" + break; + + case 37: // term: JGROUP "(" QSTR ")" +#line 143 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_JAVA, yystack_[3].value.as < Expression * > (), yystack_[1].value.as < Expression * > ()); } +#line 789 "QLParser.tab.cc" + break; + + case 38: // term: JPARENT "(" QSTR ")" +#line 144 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_JAVA, yystack_[3].value.as < Expression * > (), yystack_[1].value.as < Expression * > ()); } +#line 795 "QLParser.tab.cc" + break; + + case 39: // term: FILEIOVFD "(" QSTR ")" +#line 145 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_FILE, yystack_[3].value.as < Expression * > (), yystack_[1].value.as < Expression * > ()); } +#line 801 "QLParser.tab.cc" + break; + + case 40: // term: "number" +#line 146 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (yystack_[0].value.as < Expression * > ()); } +#line 807 "QLParser.tab.cc" + break; + + case 41: // term: "name" +#line 147 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (yystack_[0].value.as < Expression * > ()); } +#line 813 "QLParser.tab.cc" + break; + + +#line 817 "QLParser.tab.cc" + + default: + break; + } + } +#if YY_EXCEPTIONS + catch (const syntax_error& yyexc) + { + YYCDEBUG << "Caught exception: " << yyexc.what() << '\n'; + error (yyexc); + YYERROR; + } +#endif // YY_EXCEPTIONS + YY_SYMBOL_PRINT ("-> $$ =", yylhs); + yypop_ (yylen); + yylen = 0; + + // Shift the result of the reduction. + yypush_ (YY_NULLPTR, YY_MOVE (yylhs)); + } + goto yynewstate; + + + /*--------------------------------------. + | yyerrlab -- here on detecting error. | + `--------------------------------------*/ + yyerrlab: + // If not already recovering from an error, report this error. + if (!yyerrstatus_) + { + ++yynerrs_; + std::string msg = YY_("syntax error"); + error (YY_MOVE (msg)); + } + + + if (yyerrstatus_ == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + // Return failure if at end of input. + if (yyla.kind () == symbol_kind::S_YYEOF) + YYABORT; + else if (!yyla.empty ()) + { + yy_destroy_ ("Error: discarding", yyla); + yyla.clear (); + } + } + + // Else will try to reuse lookahead token after shifting the error token. + goto yyerrlab1; + + + /*---------------------------------------------------. + | yyerrorlab -- error raised explicitly by YYERROR. | + `---------------------------------------------------*/ + yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and + the label yyerrorlab therefore never appears in user code. */ + if (false) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + yypop_ (yylen); + yylen = 0; + YY_STACK_PRINT (); + goto yyerrlab1; + + + /*-------------------------------------------------------------. + | yyerrlab1 -- common code for both syntax error and YYERROR. | + `-------------------------------------------------------------*/ + yyerrlab1: + yyerrstatus_ = 3; // Each real token shifted decrements this. + // Pop stack until we find a state that shifts the error token. + for (;;) + { + yyn = yypact_[+yystack_[0].state]; + if (!yy_pact_value_is_default_ (yyn)) + { + yyn += symbol_kind::S_YYerror; + if (0 <= yyn && yyn <= yylast_ + && yycheck_[yyn] == symbol_kind::S_YYerror) + { + yyn = yytable_[yyn]; + if (0 < yyn) + break; + } + } + + // Pop the current state because it cannot handle the error token. + if (yystack_.size () == 1) + YYABORT; + + yy_destroy_ ("Error: popping", yystack_[0]); + yypop_ (); + YY_STACK_PRINT (); + } + { + stack_symbol_type error_token; + + + // Shift the error token. + error_token.state = state_type (yyn); + yypush_ ("Shifting", YY_MOVE (error_token)); + } + goto yynewstate; + + + /*-------------------------------------. + | yyacceptlab -- YYACCEPT comes here. | + `-------------------------------------*/ + yyacceptlab: + yyresult = 0; + goto yyreturn; + + + /*-----------------------------------. + | yyabortlab -- YYABORT comes here. | + `-----------------------------------*/ + yyabortlab: + yyresult = 1; + goto yyreturn; + + + /*-----------------------------------------------------. + | yyreturn -- parsing is finished, return the result. | + `-----------------------------------------------------*/ + yyreturn: + if (!yyla.empty ()) + yy_destroy_ ("Cleanup: discarding lookahead", yyla); + + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + yypop_ (yylen); + YY_STACK_PRINT (); + while (1 < yystack_.size ()) + { + yy_destroy_ ("Cleanup: popping", yystack_[0]); + yypop_ (); + } + + return yyresult; + } +#if YY_EXCEPTIONS + catch (...) + { + YYCDEBUG << "Exception caught: cleaning lookahead and stack\n"; + // Do not try to display the values of the reclaimed symbols, + // as their printers might throw an exception. + if (!yyla.empty ()) + yy_destroy_ (YY_NULLPTR, yyla); + + while (1 < yystack_.size ()) + { + yy_destroy_ (YY_NULLPTR, yystack_[0]); + yypop_ (); + } + throw; + } +#endif // YY_EXCEPTIONS + } + + void + Parser::error (const syntax_error& yyexc) + { + error (yyexc.what ()); + } + +#if YYDEBUG || 0 + const char * + Parser::symbol_name (symbol_kind_type yysymbol) + { + return yytname_[yysymbol]; + } +#endif // #if YYDEBUG || 0 + + + + + + const signed char Parser::yypact_ninf_ = -3; + + const signed char Parser::yytable_ninf_ = -1; + + const short + Parser::yypact_[] = + { + 0, 0, -3, -3, -2, 1, 8, 13, 14, 0, + 0, 0, 2, 142, -3, 50, 7, 15, 9, 11, + 12, -3, -3, -3, -3, 0, 6, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -3, 21, 22, 48, 49, 51, 188, 0, 0, + 221, 96, 95, 95, 95, 95, 95, 95, 95, 141, + 141, 141, 141, 141, 141, 17, 17, 17, 17, 17, + 17, 17, 17, -3, -3, -3, -3, -3, 188, 188, + 0, 221 + }; + + const signed char + Parser::yydefact_[] = + { + 2, 0, 40, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 30, 0, 0, 0, 0, 0, + 0, 31, 32, 33, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 34, 0, 0, 0, 0, 0, 27, 0, 0, + 26, 0, 21, 22, 24, 23, 18, 20, 19, 16, + 17, 12, 14, 13, 15, 10, 11, 8, 9, 5, + 6, 7, 4, 35, 36, 37, 38, 39, 28, 29, + 0, 25 + }; + + const signed char + Parser::yypgoto_[] = + { + -3, -3, -1, 4 + }; + + const signed char + Parser::yydefgoto_[] = + { + 0, 12, 13, 14 + }; + + const signed char + Parser::yytable_[] = + { + 15, 16, 24, 1, 17, 2, 3, 4, 5, 6, + 7, 18, 8, 21, 22, 23, 19, 20, 52, 58, + 54, 53, 55, 56, 57, 83, 84, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 9, 59, 85, 86, 51, 87, 0, 88, 89, 10, + 0, 11, 0, 25, 26, 27, 28, 0, 29, 0, + 0, 0, 30, 0, 31, 50, 32, 33, 34, 35, + 36, 0, 37, 0, 38, 0, 39, 0, 40, 91, + 41, 0, 42, 0, 43, 0, 44, 0, 45, 0, + 46, 0, 47, 0, 48, 0, 49, 0, 50, 25, + 26, 27, 28, 0, 29, 0, 90, 0, 30, 0, + 31, 0, 32, 33, 34, 35, 36, 37, 37, 38, + 38, 39, 39, 40, 40, 41, 41, 42, 42, 43, + 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, + 48, 49, 49, 50, 50, 25, 26, 27, 28, 0, + 29, 0, 0, 0, 30, 0, 31, 0, 32, 33, + 34, 35, 36, -1, 37, -1, 38, -1, 39, -1, + 40, -1, 41, -1, 42, 43, 43, 44, 44, 45, + 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, + 50, -1, -1, -1, 28, 0, 29, 0, 0, 0, + 30, 0, 31, 0, 32, 33, 34, 35, 36, 0, + 37, 0, 38, 0, 39, 0, 40, 0, 41, 0, + 42, 0, 43, 0, 44, 0, 45, 0, 46, 29, + 47, 0, 48, 30, 49, 31, 50, 32, 33, 34, + 35, 36, 0, 37, 0, 38, 0, 39, 0, 40, + 0, 41, 0, 42, 0, 43, 0, 44, 0, 45, + 0, 46, 0, 47, 0, 48, 0, 49, 0, 50 + }; + + const signed char + Parser::yycheck_[] = + { + 1, 3, 0, 3, 3, 5, 6, 7, 8, 9, + 10, 3, 12, 9, 10, 11, 3, 3, 11, 13, + 11, 6, 11, 11, 25, 4, 4, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 50, 13, 4, 4, 4, 4, -1, 58, 59, 59, + -1, 61, -1, 13, 14, 15, 16, -1, 18, -1, + -1, -1, 22, -1, 24, 58, 26, 27, 28, 29, + 30, -1, 32, -1, 34, -1, 36, -1, 38, 90, + 40, -1, 42, -1, 44, -1, 46, -1, 48, -1, + 50, -1, 52, -1, 54, -1, 56, -1, 58, 13, + 14, 15, 16, -1, 18, -1, 20, -1, 22, -1, + 24, -1, 26, 27, 28, 29, 30, 32, 32, 34, + 34, 36, 36, 38, 38, 40, 40, 42, 42, 44, + 44, 46, 46, 48, 48, 50, 50, 52, 52, 54, + 54, 56, 56, 58, 58, 13, 14, 15, 16, -1, + 18, -1, -1, -1, 22, -1, 24, -1, 26, 27, + 28, 29, 30, 32, 32, 34, 34, 36, 36, 38, + 38, 40, 40, 42, 42, 44, 44, 46, 46, 48, + 48, 50, 50, 52, 52, 54, 54, 56, 56, 58, + 58, 13, 14, 15, 16, -1, 18, -1, -1, -1, + 22, -1, 24, -1, 26, 27, 28, 29, 30, -1, + 32, -1, 34, -1, 36, -1, 38, -1, 40, -1, + 42, -1, 44, -1, 46, -1, 48, -1, 50, 18, + 52, -1, 54, 22, 56, 24, 58, 26, 27, 28, + 29, 30, -1, 32, -1, 34, -1, 36, -1, 38, + -1, 40, -1, 42, -1, 44, -1, 46, -1, 48, + -1, 50, -1, 52, -1, 54, -1, 56, -1, 58 + }; + + const signed char + Parser::yystos_[] = + { + 0, 3, 5, 6, 7, 8, 9, 10, 12, 50, + 59, 61, 64, 65, 66, 65, 3, 3, 3, 3, + 3, 66, 66, 66, 0, 13, 14, 15, 16, 18, + 22, 24, 26, 27, 28, 29, 30, 32, 34, 36, + 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, + 58, 4, 11, 6, 11, 11, 11, 65, 13, 13, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 4, 4, 4, 4, 4, 65, 65, + 20, 65 + }; + + const signed char + Parser::yyr1_[] = + { + 0, 63, 64, 64, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66 + }; + + const signed char + Parser::yyr2_[] = + { + 0, 2, 0, 1, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 5, 3, 3, 4, 4, + 1, 2, 2, 2, 3, 4, 4, 4, 4, 4, + 1, 1 + }; + + +#if YYDEBUG + // YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + // First, the terminals, then, starting at \a YYNTOKENS, nonterminals. + const char* + const Parser::yytname_[] = + { + "\"end of file\"", "error", "\"invalid token\"", "\"(\"", "\")\"", + "\"number\"", "\"name\"", "FNAME", "HASPROP", "JGROUP", "JPARENT", + "QSTR", "FILEIOVFD", "IN", "SOME", "ORDR", "COMMA", "\",\"", "QWE", + "\"?\"", "COLON", "\":\"", "AND", "\"&&\"", "OR", "\"|\"", "EQV", "NEQV", + "BITAND", "BITOR", "BITXOR", "\"^\"", "EQ", "\"=\"", "NE", "\"!=\"", + "LT", "\"<\"", "GT", "\">\"", "LE", "\"<=\"", "GE", "\">=\"", "LS", + "\"<<\"", "RS", "\">>\"", "ADD", "\"+\"", "MINUS", "\"-\"", "MUL", + "\"*\"", "DIV", "\"/\"", "REM", "\"%\"", "DEG", "NOT", "\"!\"", "BITNOT", + "\"~\"", "$accept", "S", "exp", "term", YY_NULLPTR + }; +#endif + + +#if YYDEBUG + const unsigned char + Parser::yyrline_[] = + { + 0, 104, 104, 105, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 130, 131, 132, 133, + 134, 136, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147 + }; + + void + Parser::yy_stack_print_ () const + { + *yycdebug_ << "Stack now"; + for (stack_type::const_iterator + i = yystack_.begin (), + i_end = yystack_.end (); + i != i_end; ++i) + *yycdebug_ << ' ' << int (i->state); + *yycdebug_ << '\n'; + } + + void + Parser::yy_reduce_print_ (int yyrule) const + { + int yylno = yyrline_[yyrule]; + int yynrhs = yyr2_[yyrule]; + // Print the symbols being reduced, and their result. + *yycdebug_ << "Reducing stack by rule " << yyrule - 1 + << " (line " << yylno << "):\n"; + // The symbols being reduced. + for (int yyi = 0; yyi < yynrhs; yyi++) + YY_SYMBOL_PRINT (" $" << yyi + 1 << " =", + yystack_[(yynrhs) - (yyi + 1)]); + } +#endif // YYDEBUG + + +#line 50 "QLParser.yy" +} // QL +#line 1210 "QLParser.tab.cc" + +#line 149 "QLParser.yy" + + +namespace QL +{ + static Parser::symbol_type + unget_ret (std::istream &in, char c, Parser::symbol_type tok) + { + in.putback (c); + return tok; + } + + static Expression * + processName (char *name) + { + int propID = dbeSession->getPropIdByName (name); + if (propID != PROP_NONE) + { + Expression *expr = new Expression (Expression::OP_NUM, (uint64_t) propID); + Expression *ret = new Expression (Expression::OP_NAME, expr); + delete expr; + return ret; + } + + // If a name is not statically known try user defined objects + Expression *expr = dbeSession->findObjDefByName (name); + if (expr != NULL) + return expr->copy(); + + throw Parser::syntax_error ("Name not found"); + } + + static Parser::symbol_type + yylex (QL::Result &result) + { + int base = 0; + int c; + + do + c = result.in.get (); + while (result.in && (c == ' ' || c == '\t')); + if (!result.in) + return Parser::make_YYEOF (); + + switch (c) + { + case '\n': return Parser::make_YYEOF (); + case '(': return Parser::make_LPAR () ; + case ')': return Parser::make_RPAR (); + case ',': return Parser::make_COMMA (); + case '%': return Parser::make_REM (); + case '/': return Parser::make_DIV (); + case '*': return Parser::make_MUL (); + case '-': return Parser::make_MINUS (); + case '+': return Parser::make_ADD (); + case '~': return Parser::make_BITNOT (); + case '^': return Parser::make_BITXOR (); + case '?': return Parser::make_QWE (); + case ':': return Parser::make_COLON (); + case '|': + c = result.in.get (); + if (c == '|') + return Parser::make_OR (); + else + return unget_ret (result.in, c, Parser::make_BITOR ()); + case '&': + c = result.in.get (); + if (c == '&') + return Parser::make_AND (); + else + return unget_ret (result.in, c, Parser::make_BITAND ()); + case '!': + c = result.in.get (); + if (c == '=') + return Parser::make_NE (); + else + return unget_ret (result.in, c, Parser::make_NOT ()); + case '=': + c = result.in.get (); + if (c == '=') + return Parser::make_EQ (); + else + throw Parser::syntax_error ("Syntax error after ="); + case '<': + c = result.in.get (); + if (c == '=') + return Parser::make_LE (); + else if (c == '<') + return Parser::make_LS (); + else + return unget_ret (result.in, c, Parser::make_LT ()); + case '>': + c = result.in.get (); + if (c == '=') + return Parser::make_GE (); + else if (c == '>') + return Parser::make_RS (); + else + return unget_ret (result.in, c, Parser::make_GT ()); + case '"': + { + int maxsz = 16; + char *str = (char *) malloc (maxsz); + char *ptr = str; + + for (;;) + { + c = result.in.get (); + if (!result.in) + { + free (str); + throw Parser::syntax_error ("Unclosed \""); + } + + switch (c) + { + case '"': + *ptr = (char)0; + // XXX omazur: need new string type + return Parser::make_QSTR (new Expression (Expression::OP_NUM, (uint64_t) str)); + case 0: + case '\n': + free (str); + throw Parser::syntax_error ("Multiline strings are not supported"); + default: + if (ptr - str >= maxsz) + { + size_t len = ptr - str; + maxsz = maxsz > 8192 ? maxsz + 8192 : maxsz * 2; + char *new_s = (char *) realloc (str, maxsz); + str = new_s; + ptr = str + len; + } + *ptr++ = c; + } + } + } + default: + if (c == '0') + { + base = 8; + c = result.in.get (); + if ( c == 'x' ) + { + base = 16; + c = result.in.get (); + } + } + else if (c >= '1' && c <='9') + base = 10; + + if (base) + { + uint64_t lval = 0; + for (;;) + { + int digit = -1; + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + digit = c - '0'; + break; + case '8': case '9': + if (base > 8) + digit = c - '0'; + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + if (base == 16) + digit = c - 'a' + 10; + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + if (base == 16) + digit = c - 'A' + 10; + break; + } + if (digit == -1) + { + result.in.putback (c); + break; + } + lval = lval * base + digit; + c = result.in.get (); + } + return Parser::make_NUM (new Expression (Expression::OP_NUM, lval)); + } + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + { + char name[32]; // omazur XXX: accept any length + name[0] = (char)c; + for (size_t i = 1; i < sizeof (name); i++) + { + c = result.in.get (); + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || (c == '_')) + name[i] = c; + else + { + name[i] = (char)0; + result.in.putback (c); + break; + } + } + + if (strcasecmp (name, NTXT ("IN")) == 0) + return Parser::make_IN (); + else if (strcasecmp (name, NTXT ("SOME")) == 0) + return Parser::make_SOME (); + else if (strcasecmp (name, NTXT ("ORDERED")) == 0) + return Parser::make_ORDR (); + else if (strcasecmp (name, NTXT ("TRUE")) == 0) + return Parser::make_NUM (new Expression (Expression::OP_NUM, (uint64_t) 1)); + else if (strcasecmp (name, NTXT ("FALSE")) == 0) + return Parser::make_NUM (new Expression (Expression::OP_NUM, (uint64_t) 0)); + else if (strcasecmp (name, NTXT ("FNAME")) == 0) + return Parser::make_FNAME (new Expression (Expression::OP_NUM, Expression::FUNC_FNAME)); + else if (strcasecmp (name, NTXT ("HAS_PROP")) == 0) + return Parser::make_HASPROP (); + else if (strcasecmp (name, NTXT ("JGROUP")) == 0) + return Parser::make_JGROUP (new Expression (Expression::OP_NUM, Expression::JAVA_JGROUP)); + else if (strcasecmp (name, NTXT ("JPARENT")) == 0 ) + return Parser::make_JPARENT (new Expression (Expression::OP_NUM, Expression::JAVA_JPARENT)); + else if (strcasecmp (name, NTXT ("DNAME")) == 0) + return Parser::make_FNAME (new Expression (Expression::OP_NUM, Expression::FUNC_DNAME)); + else if (strcasecmp (name, NTXT ("FILEIOVFD")) == 0 ) + return Parser::make_FILEIOVFD (new Expression (Expression::OP_NUM, (uint64_t) 0)); + + return Parser::make_NAME (processName (name)); + } + + throw Parser::syntax_error ("Syntax error"); + } + } + void + Parser::error (const std::string &) + { + // do nothing for now + } +} + diff --git a/gprofng/src/QLParser.tab.hh b/gprofng/src/QLParser.tab.hh new file mode 100644 index 0000000..eaf2cb5 --- /dev/null +++ b/gprofng/src/QLParser.tab.hh @@ -0,0 +1,2038 @@ +// A Bison parser, made by GNU Bison 3.7.5. + +// Skeleton interface for Bison LALR(1) parsers in C++ + +// Copyright (C) 2002-2015, 2018-2021 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 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 <http://www.gnu.org/licenses/>. + +// As a special exception, you may create a larger work that contains +// part or all of the Bison parser skeleton and distribute that work +// under terms of your choice, so long as that work isn't itself a +// parser generator using the skeleton or a modified version thereof +// as a parser skeleton. Alternatively, if you modify or redistribute +// the parser skeleton itself, you may (at your option) remove this +// special exception, which will cause the skeleton and the resulting +// Bison output files to be licensed under the GNU General Public +// License without this special exception. + +// This special exception was added by the Free Software Foundation in +// version 2.2 of Bison. + + +/** + ** \file QLParser.tab.hh + ** Define the QL::parser class. + */ + +// C++ LALR(1) parser skeleton written by Akim Demaille. + +// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, +// especially those whose name start with YY_ or yy_. They are +// private implementation details that can be changed or removed. + +#ifndef YY_YY_QLPARSER_TAB_HH_INCLUDED +# define YY_YY_QLPARSER_TAB_HH_INCLUDED +// "%code requires" blocks. +#line 33 "QLParser.yy" + +#include "QLParser.h" +#include "DbeSession.h" +#include "Expression.h" +#include "Table.h" +#include "i18n.h" + +#line 57 "QLParser.tab.hh" + +# include <cassert> +# include <cstdlib> // std::abort +# include <iostream> +# include <stdexcept> +# include <string> +# include <vector> + +#if defined __cplusplus +# define YY_CPLUSPLUS __cplusplus +#else +# define YY_CPLUSPLUS 199711L +#endif + +// Support move semantics when possible. +#if 201103L <= YY_CPLUSPLUS +# define YY_MOVE std::move +# define YY_MOVE_OR_COPY move +# define YY_MOVE_REF(Type) Type&& +# define YY_RVREF(Type) Type&& +# define YY_COPY(Type) Type +#else +# define YY_MOVE +# define YY_MOVE_OR_COPY copy +# define YY_MOVE_REF(Type) Type& +# define YY_RVREF(Type) const Type& +# define YY_COPY(Type) const Type& +#endif + +// Support noexcept when possible. +#if 201103L <= YY_CPLUSPLUS +# define YY_NOEXCEPT noexcept +# define YY_NOTHROW +#else +# define YY_NOEXCEPT +# define YY_NOTHROW throw () +#endif + +// Support constexpr when possible. +#if 201703 <= YY_CPLUSPLUS +# define YY_CONSTEXPR constexpr +#else +# define YY_CONSTEXPR +#endif + +#include <typeinfo> +#ifndef YY_ASSERT +# include <cassert> +# define YY_ASSERT assert +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +#line 50 "QLParser.yy" +namespace QL { +#line 192 "QLParser.tab.hh" + + + + + /// A Bison parser. + class Parser + { + public: +#ifndef YYSTYPE + /// A buffer to store and retrieve objects. + /// + /// Sort of a variant, but does not keep track of the nature + /// of the stored data, since that knowledge is available + /// via the current parser state. + class semantic_type + { + public: + /// Type of *this. + typedef semantic_type self_type; + + /// Empty construction. + semantic_type () YY_NOEXCEPT + : yybuffer_ () + , yytypeid_ (YY_NULLPTR) + {} + + /// Construct and fill. + template <typename T> + semantic_type (YY_RVREF (T) t) + : yytypeid_ (&typeid (T)) + { + YY_ASSERT (sizeof (T) <= size); + new (yyas_<T> ()) T (YY_MOVE (t)); + } + +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + semantic_type (const self_type&) = delete; + /// Non copyable. + self_type& operator= (const self_type&) = delete; +#endif + + /// Destruction, allowed only if empty. + ~semantic_type () YY_NOEXCEPT + { + YY_ASSERT (!yytypeid_); + } + +# if 201103L <= YY_CPLUSPLUS + /// Instantiate a \a T in here from \a t. + template <typename T, typename... U> + T& + emplace (U&&... u) + { + YY_ASSERT (!yytypeid_); + YY_ASSERT (sizeof (T) <= size); + yytypeid_ = & typeid (T); + return *new (yyas_<T> ()) T (std::forward <U>(u)...); + } +# else + /// Instantiate an empty \a T in here. + template <typename T> + T& + emplace () + { + YY_ASSERT (!yytypeid_); + YY_ASSERT (sizeof (T) <= size); + yytypeid_ = & typeid (T); + return *new (yyas_<T> ()) T (); + } + + /// Instantiate a \a T in here from \a t. + template <typename T> + T& + emplace (const T& t) + { + YY_ASSERT (!yytypeid_); + YY_ASSERT (sizeof (T) <= size); + yytypeid_ = & typeid (T); + return *new (yyas_<T> ()) T (t); + } +# endif + + /// Instantiate an empty \a T in here. + /// Obsolete, use emplace. + template <typename T> + T& + build () + { + return emplace<T> (); + } + + /// Instantiate a \a T in here from \a t. + /// Obsolete, use emplace. + template <typename T> + T& + build (const T& t) + { + return emplace<T> (t); + } + + /// Accessor to a built \a T. + template <typename T> + T& + as () YY_NOEXCEPT + { + YY_ASSERT (yytypeid_); + YY_ASSERT (*yytypeid_ == typeid (T)); + YY_ASSERT (sizeof (T) <= size); + return *yyas_<T> (); + } + + /// Const accessor to a built \a T (for %printer). + template <typename T> + const T& + as () const YY_NOEXCEPT + { + YY_ASSERT (yytypeid_); + YY_ASSERT (*yytypeid_ == typeid (T)); + YY_ASSERT (sizeof (T) <= size); + return *yyas_<T> (); + } + + /// Swap the content with \a that, of same type. + /// + /// Both variants must be built beforehand, because swapping the actual + /// data requires reading it (with as()), and this is not possible on + /// unconstructed variants: it would require some dynamic testing, which + /// should not be the variant's responsibility. + /// Swapping between built and (possibly) non-built is done with + /// self_type::move (). + template <typename T> + void + swap (self_type& that) YY_NOEXCEPT + { + YY_ASSERT (yytypeid_); + YY_ASSERT (*yytypeid_ == *that.yytypeid_); + std::swap (as<T> (), that.as<T> ()); + } + + /// Move the content of \a that to this. + /// + /// Destroys \a that. + template <typename T> + void + move (self_type& that) + { +# if 201103L <= YY_CPLUSPLUS + emplace<T> (std::move (that.as<T> ())); +# else + emplace<T> (); + swap<T> (that); +# endif + that.destroy<T> (); + } + +# if 201103L <= YY_CPLUSPLUS + /// Move the content of \a that to this. + template <typename T> + void + move (self_type&& that) + { + emplace<T> (std::move (that.as<T> ())); + that.destroy<T> (); + } +#endif + + /// Copy the content of \a that to this. + template <typename T> + void + copy (const self_type& that) + { + emplace<T> (that.as<T> ()); + } + + /// Destroy the stored \a T. + template <typename T> + void + destroy () + { + as<T> ().~T (); + yytypeid_ = YY_NULLPTR; + } + + private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. + semantic_type (const self_type&); + /// Non copyable. + self_type& operator= (const self_type&); +#endif + + /// Accessor to raw memory as \a T. + template <typename T> + T* + yyas_ () YY_NOEXCEPT + { + void *yyp = yybuffer_.yyraw; + return static_cast<T*> (yyp); + } + + /// Const accessor to raw memory as \a T. + template <typename T> + const T* + yyas_ () const YY_NOEXCEPT + { + const void *yyp = yybuffer_.yyraw; + return static_cast<const T*> (yyp); + } + + /// An auxiliary type to compute the largest semantic type. + union union_type + { + // "number" + // "name" + // FNAME + // JGROUP + // JPARENT + // QSTR + // FILEIOVFD + // exp + // term + char dummy1[sizeof (Expression *)]; + }; + + /// The size of the largest semantic type. + enum { size = sizeof (union_type) }; + + /// A buffer to store semantic values. + union + { + /// Strongest alignment constraints. + long double yyalign_me; + /// A buffer large enough to store any of the semantic values. + char yyraw[size]; + } yybuffer_; + + /// Whether the content is built: if defined, the name of the stored type. + const std::type_info *yytypeid_; + }; + +#else + typedef YYSTYPE semantic_type; +#endif + + /// Syntax errors thrown from user actions. + struct syntax_error : std::runtime_error + { + syntax_error (const std::string& m) + : std::runtime_error (m) + {} + + syntax_error (const syntax_error& s) + : std::runtime_error (s.what ()) + {} + + ~syntax_error () YY_NOEXCEPT YY_NOTHROW; + }; + + /// Token kinds. + struct token + { + enum token_kind_type + { + L_YYEMPTY = -2, + L_YYEOF = 0, // "end of file" + L_YYerror = 256, // error + L_YYUNDEF = 257, // "invalid token" + L_LPAR = 258, // "(" + L_RPAR = 259, // ")" + L_NUM = 260, // "number" + L_NAME = 261, // "name" + L_FNAME = 262, // FNAME + L_HASPROP = 263, // HASPROP + L_JGROUP = 264, // JGROUP + L_JPARENT = 265, // JPARENT + L_QSTR = 266, // QSTR + L_FILEIOVFD = 267, // FILEIOVFD + L_IN = 268, // IN + L_SOME = 269, // SOME + L_ORDR = 270, // ORDR + L_COMMA = 271, // COMMA + L_QWE = 273, // QWE + L_COLON = 275, // COLON + L_AND = 277, // AND + L_OR = 279, // OR + L_EQV = 281, // EQV + L_NEQV = 282, // NEQV + L_BITAND = 283, // BITAND + L_BITOR = 284, // BITOR + L_BITXOR = 285, // BITXOR + L_EQ = 287, // EQ + L_NE = 289, // NE + L_LT = 291, // LT + L_GT = 293, // GT + L_LE = 295, // LE + L_GE = 297, // GE + L_LS = 299, // LS + L_RS = 301, // RS + L_ADD = 303, // ADD + L_MINUS = 305, // MINUS + L_MUL = 307, // MUL + L_DIV = 309, // DIV + L_REM = 311, // REM + L_DEG = 313, // DEG + L_NOT = 314, // NOT + L_BITNOT = 316 // BITNOT + }; + /// Backward compatibility alias (Bison 3.6). + typedef token_kind_type yytokentype; + }; + + /// Token kind, as returned by yylex. + typedef token::yytokentype token_kind_type; + + /// Backward compatibility alias (Bison 3.6). + typedef token_kind_type token_type; + + /// Symbol kinds. + struct symbol_kind + { + enum symbol_kind_type + { + YYNTOKENS = 63, ///< Number of tokens. + S_YYEMPTY = -2, + S_YYEOF = 0, // "end of file" + S_YYerror = 1, // error + S_YYUNDEF = 2, // "invalid token" + S_LPAR = 3, // "(" + S_RPAR = 4, // ")" + S_NUM = 5, // "number" + S_NAME = 6, // "name" + S_FNAME = 7, // FNAME + S_HASPROP = 8, // HASPROP + S_JGROUP = 9, // JGROUP + S_JPARENT = 10, // JPARENT + S_QSTR = 11, // QSTR + S_FILEIOVFD = 12, // FILEIOVFD + S_IN = 13, // IN + S_SOME = 14, // SOME + S_ORDR = 15, // ORDR + S_COMMA = 16, // COMMA + S_17_ = 17, // "," + S_QWE = 18, // QWE + S_19_ = 19, // "?" + S_COLON = 20, // COLON + S_21_ = 21, // ":" + S_AND = 22, // AND + S_23_ = 23, // "&&" + S_OR = 24, // OR + S_25_ = 25, // "|" + S_EQV = 26, // EQV + S_NEQV = 27, // NEQV + S_BITAND = 28, // BITAND + S_BITOR = 29, // BITOR + S_BITXOR = 30, // BITXOR + S_31_ = 31, // "^" + S_EQ = 32, // EQ + S_33_ = 33, // "=" + S_NE = 34, // NE + S_35_ = 35, // "!=" + S_LT = 36, // LT + S_37_ = 37, // "<" + S_GT = 38, // GT + S_39_ = 39, // ">" + S_LE = 40, // LE + S_41_ = 41, // "<=" + S_GE = 42, // GE + S_43_ = 43, // ">=" + S_LS = 44, // LS + S_45_ = 45, // "<<" + S_RS = 46, // RS + S_47_ = 47, // ">>" + S_ADD = 48, // ADD + S_49_ = 49, // "+" + S_MINUS = 50, // MINUS + S_51_ = 51, // "-" + S_MUL = 52, // MUL + S_53_ = 53, // "*" + S_DIV = 54, // DIV + S_55_ = 55, // "/" + S_REM = 56, // REM + S_57_ = 57, // "%" + S_DEG = 58, // DEG + S_NOT = 59, // NOT + S_60_ = 60, // "!" + S_BITNOT = 61, // BITNOT + S_62_ = 62, // "~" + S_YYACCEPT = 63, // $accept + S_S = 64, // S + S_exp = 65, // exp + S_term = 66 // term + }; + }; + + /// (Internal) symbol kind. + typedef symbol_kind::symbol_kind_type symbol_kind_type; + + /// The number of tokens. + static const symbol_kind_type YYNTOKENS = symbol_kind::YYNTOKENS; + + /// A complete symbol. + /// + /// Expects its Base type to provide access to the symbol kind + /// via kind (). + /// + /// Provide access to semantic value. + template <typename Base> + struct basic_symbol : Base + { + /// Alias to Base. + typedef Base super_type; + + /// Default constructor. + basic_symbol () + : value () + {} + +#if 201103L <= YY_CPLUSPLUS + /// Move constructor. + basic_symbol (basic_symbol&& that) + : Base (std::move (that)) + , value () + { + switch (this->kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.move< Expression * > (std::move (that.value)); + break; + + default: + break; + } + + } +#endif + + /// Copy constructor. + basic_symbol (const basic_symbol& that); + + /// Constructors for typed symbols. +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t) + : Base (t) + {} +#else + basic_symbol (typename Base::kind_type t) + : Base (t) + {} +#endif + +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t, Expression *&& v) + : Base (t) + , value (std::move (v)) + {} +#else + basic_symbol (typename Base::kind_type t, const Expression *& v) + : Base (t) + , value (v) + {} +#endif + + /// Destroy the symbol. + ~basic_symbol () + { + clear (); + } + + /// Destroy contents, and record that is empty. + void clear () YY_NOEXCEPT + { + // User destructor. + symbol_kind_type yykind = this->kind (); + basic_symbol<Base>& yysym = *this; + (void) yysym; + switch (yykind) + { + case symbol_kind::S_NUM: // "number" +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 682 "QLParser.tab.hh" + break; + + case symbol_kind::S_NAME: // "name" +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 688 "QLParser.tab.hh" + break; + + case symbol_kind::S_FNAME: // FNAME +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 694 "QLParser.tab.hh" + break; + + case symbol_kind::S_JGROUP: // JGROUP +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 700 "QLParser.tab.hh" + break; + + case symbol_kind::S_JPARENT: // JPARENT +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 706 "QLParser.tab.hh" + break; + + case symbol_kind::S_QSTR: // QSTR +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 712 "QLParser.tab.hh" + break; + + case symbol_kind::S_FILEIOVFD: // FILEIOVFD +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 718 "QLParser.tab.hh" + break; + + case symbol_kind::S_exp: // exp +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 724 "QLParser.tab.hh" + break; + + case symbol_kind::S_term: // term +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 730 "QLParser.tab.hh" + break; + + default: + break; + } + + // Value type destructor. +switch (yykind) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.template destroy< Expression * > (); + break; + + default: + break; + } + + Base::clear (); + } + +#if YYDEBUG || 0 + /// The user-facing name of this symbol. + const char *name () const YY_NOEXCEPT + { + return Parser::symbol_name (this->kind ()); + } +#endif // #if YYDEBUG || 0 + + + /// Backward compatibility (Bison 3.6). + symbol_kind_type type_get () const YY_NOEXCEPT; + + /// Whether empty. + bool empty () const YY_NOEXCEPT; + + /// Destructive move, \a s is emptied into this. + void move (basic_symbol& s); + + /// The semantic value. + semantic_type value; + + private: +#if YY_CPLUSPLUS < 201103L + /// Assignment operator. + basic_symbol& operator= (const basic_symbol& that); +#endif + }; + + /// Type access provider for token (enum) based symbols. + struct by_kind + { + /// Default constructor. + by_kind (); + +#if 201103L <= YY_CPLUSPLUS + /// Move constructor. + by_kind (by_kind&& that); +#endif + + /// Copy constructor. + by_kind (const by_kind& that); + + /// The symbol kind as needed by the constructor. + typedef token_kind_type kind_type; + + /// Constructor from (external) token numbers. + by_kind (kind_type t); + + /// Record that this symbol is empty. + void clear () YY_NOEXCEPT; + + /// Steal the symbol kind from \a that. + void move (by_kind& that); + + /// The (internal) type number (corresponding to \a type). + /// \a empty when empty. + symbol_kind_type kind () const YY_NOEXCEPT; + + /// Backward compatibility (Bison 3.6). + symbol_kind_type type_get () const YY_NOEXCEPT; + + /// The symbol kind. + /// \a S_YYEMPTY when empty. + symbol_kind_type kind_; + }; + + /// Backward compatibility for a private implementation detail (Bison 3.6). + typedef by_kind by_type; + + /// "External" symbols: returned by the scanner. + struct symbol_type : basic_symbol<by_kind> + { + /// Superclass. + typedef basic_symbol<by_kind> super_type; + + /// Empty symbol. + symbol_type () {} + + /// Constructor for valueless symbols, and symbols from each type. +#if 201103L <= YY_CPLUSPLUS + symbol_type (int tok) + : super_type(token_type (tok)) +#else + symbol_type (int tok) + : super_type(token_type (tok)) +#endif + { + YY_ASSERT (tok == token::L_YYEOF + || (token::L_YYerror <= tok && tok <= token::L_RPAR) + || tok == token::L_HASPROP + || (token::L_IN <= tok && tok <= 317)); + } +#if 201103L <= YY_CPLUSPLUS + symbol_type (int tok, Expression * v) + : super_type(token_type (tok), std::move (v)) +#else + symbol_type (int tok, const Expression *& v) + : super_type(token_type (tok), v) +#endif + { + YY_ASSERT ((token::L_NUM <= tok && tok <= token::L_FNAME) + || (token::L_JGROUP <= tok && tok <= token::L_FILEIOVFD)); + } + }; + + /// Build a parser object. + Parser (QL::Result &result_yyarg); + virtual ~Parser (); + +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + Parser (const Parser&) = delete; + /// Non copyable. + Parser& operator= (const Parser&) = delete; +#endif + + /// Parse. An alias for parse (). + /// \returns 0 iff parsing succeeded. + int operator() (); + + /// Parse. + /// \returns 0 iff parsing succeeded. + virtual int parse (); + +#if YYDEBUG + /// The current debugging stream. + std::ostream& debug_stream () const YY_ATTRIBUTE_PURE; + /// Set the current debugging stream. + void set_debug_stream (std::ostream &); + + /// Type for debugging levels. + typedef int debug_level_type; + /// The current debugging level. + debug_level_type debug_level () const YY_ATTRIBUTE_PURE; + /// Set the current debugging level. + void set_debug_level (debug_level_type l); +#endif + + /// Report a syntax error. + /// \param msg a description of the syntax error. + virtual void error (const std::string& msg); + + /// Report a syntax error. + void error (const syntax_error& err); + +#if YYDEBUG || 0 + /// The user-facing name of the symbol whose (internal) number is + /// YYSYMBOL. No bounds checking. + static const char *symbol_name (symbol_kind_type yysymbol); +#endif // #if YYDEBUG || 0 + + + // Implementation of make_symbol for each symbol type. +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_YYEOF () + { + return symbol_type (token::L_YYEOF); + } +#else + static + symbol_type + make_YYEOF () + { + return symbol_type (token::L_YYEOF); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_YYerror () + { + return symbol_type (token::L_YYerror); + } +#else + static + symbol_type + make_YYerror () + { + return symbol_type (token::L_YYerror); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_YYUNDEF () + { + return symbol_type (token::L_YYUNDEF); + } +#else + static + symbol_type + make_YYUNDEF () + { + return symbol_type (token::L_YYUNDEF); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LPAR () + { + return symbol_type (token::L_LPAR); + } +#else + static + symbol_type + make_LPAR () + { + return symbol_type (token::L_LPAR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_RPAR () + { + return symbol_type (token::L_RPAR); + } +#else + static + symbol_type + make_RPAR () + { + return symbol_type (token::L_RPAR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NUM (Expression * v) + { + return symbol_type (token::L_NUM, std::move (v)); + } +#else + static + symbol_type + make_NUM (const Expression *& v) + { + return symbol_type (token::L_NUM, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NAME (Expression * v) + { + return symbol_type (token::L_NAME, std::move (v)); + } +#else + static + symbol_type + make_NAME (const Expression *& v) + { + return symbol_type (token::L_NAME, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_FNAME (Expression * v) + { + return symbol_type (token::L_FNAME, std::move (v)); + } +#else + static + symbol_type + make_FNAME (const Expression *& v) + { + return symbol_type (token::L_FNAME, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_HASPROP () + { + return symbol_type (token::L_HASPROP); + } +#else + static + symbol_type + make_HASPROP () + { + return symbol_type (token::L_HASPROP); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_JGROUP (Expression * v) + { + return symbol_type (token::L_JGROUP, std::move (v)); + } +#else + static + symbol_type + make_JGROUP (const Expression *& v) + { + return symbol_type (token::L_JGROUP, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_JPARENT (Expression * v) + { + return symbol_type (token::L_JPARENT, std::move (v)); + } +#else + static + symbol_type + make_JPARENT (const Expression *& v) + { + return symbol_type (token::L_JPARENT, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_QSTR (Expression * v) + { + return symbol_type (token::L_QSTR, std::move (v)); + } +#else + static + symbol_type + make_QSTR (const Expression *& v) + { + return symbol_type (token::L_QSTR, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_FILEIOVFD (Expression * v) + { + return symbol_type (token::L_FILEIOVFD, std::move (v)); + } +#else + static + symbol_type + make_FILEIOVFD (const Expression *& v) + { + return symbol_type (token::L_FILEIOVFD, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_IN () + { + return symbol_type (token::L_IN); + } +#else + static + symbol_type + make_IN () + { + return symbol_type (token::L_IN); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SOME () + { + return symbol_type (token::L_SOME); + } +#else + static + symbol_type + make_SOME () + { + return symbol_type (token::L_SOME); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_ORDR () + { + return symbol_type (token::L_ORDR); + } +#else + static + symbol_type + make_ORDR () + { + return symbol_type (token::L_ORDR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_COMMA () + { + return symbol_type (token::L_COMMA); + } +#else + static + symbol_type + make_COMMA () + { + return symbol_type (token::L_COMMA); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_QWE () + { + return symbol_type (token::L_QWE); + } +#else + static + symbol_type + make_QWE () + { + return symbol_type (token::L_QWE); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_COLON () + { + return symbol_type (token::L_COLON); + } +#else + static + symbol_type + make_COLON () + { + return symbol_type (token::L_COLON); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_AND () + { + return symbol_type (token::L_AND); + } +#else + static + symbol_type + make_AND () + { + return symbol_type (token::L_AND); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_OR () + { + return symbol_type (token::L_OR); + } +#else + static + symbol_type + make_OR () + { + return symbol_type (token::L_OR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_EQV () + { + return symbol_type (token::L_EQV); + } +#else + static + symbol_type + make_EQV () + { + return symbol_type (token::L_EQV); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NEQV () + { + return symbol_type (token::L_NEQV); + } +#else + static + symbol_type + make_NEQV () + { + return symbol_type (token::L_NEQV); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_BITAND () + { + return symbol_type (token::L_BITAND); + } +#else + static + symbol_type + make_BITAND () + { + return symbol_type (token::L_BITAND); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_BITOR () + { + return symbol_type (token::L_BITOR); + } +#else + static + symbol_type + make_BITOR () + { + return symbol_type (token::L_BITOR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_BITXOR () + { + return symbol_type (token::L_BITXOR); + } +#else + static + symbol_type + make_BITXOR () + { + return symbol_type (token::L_BITXOR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_EQ () + { + return symbol_type (token::L_EQ); + } +#else + static + symbol_type + make_EQ () + { + return symbol_type (token::L_EQ); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NE () + { + return symbol_type (token::L_NE); + } +#else + static + symbol_type + make_NE () + { + return symbol_type (token::L_NE); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LT () + { + return symbol_type (token::L_LT); + } +#else + static + symbol_type + make_LT () + { + return symbol_type (token::L_LT); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_GT () + { + return symbol_type (token::L_GT); + } +#else + static + symbol_type + make_GT () + { + return symbol_type (token::L_GT); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LE () + { + return symbol_type (token::L_LE); + } +#else + static + symbol_type + make_LE () + { + return symbol_type (token::L_LE); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_GE () + { + return symbol_type (token::L_GE); + } +#else + static + symbol_type + make_GE () + { + return symbol_type (token::L_GE); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LS () + { + return symbol_type (token::L_LS); + } +#else + static + symbol_type + make_LS () + { + return symbol_type (token::L_LS); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_RS () + { + return symbol_type (token::L_RS); + } +#else + static + symbol_type + make_RS () + { + return symbol_type (token::L_RS); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_ADD () + { + return symbol_type (token::L_ADD); + } +#else + static + symbol_type + make_ADD () + { + return symbol_type (token::L_ADD); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_MINUS () + { + return symbol_type (token::L_MINUS); + } +#else + static + symbol_type + make_MINUS () + { + return symbol_type (token::L_MINUS); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_MUL () + { + return symbol_type (token::L_MUL); + } +#else + static + symbol_type + make_MUL () + { + return symbol_type (token::L_MUL); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DIV () + { + return symbol_type (token::L_DIV); + } +#else + static + symbol_type + make_DIV () + { + return symbol_type (token::L_DIV); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_REM () + { + return symbol_type (token::L_REM); + } +#else + static + symbol_type + make_REM () + { + return symbol_type (token::L_REM); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DEG () + { + return symbol_type (token::L_DEG); + } +#else + static + symbol_type + make_DEG () + { + return symbol_type (token::L_DEG); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NOT () + { + return symbol_type (token::L_NOT); + } +#else + static + symbol_type + make_NOT () + { + return symbol_type (token::L_NOT); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_BITNOT () + { + return symbol_type (token::L_BITNOT); + } +#else + static + symbol_type + make_BITNOT () + { + return symbol_type (token::L_BITNOT); + } +#endif + + + private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. + Parser (const Parser&); + /// Non copyable. + Parser& operator= (const Parser&); +#endif + + + /// Stored state numbers (used for stacks). + typedef signed char state_type; + + /// Compute post-reduction state. + /// \param yystate the current state + /// \param yysym the nonterminal to push on the stack + static state_type yy_lr_goto_state_ (state_type yystate, int yysym); + + /// Whether the given \c yypact_ value indicates a defaulted state. + /// \param yyvalue the value to check + static bool yy_pact_value_is_default_ (int yyvalue); + + /// Whether the given \c yytable_ value indicates a syntax error. + /// \param yyvalue the value to check + static bool yy_table_value_is_error_ (int yyvalue); + + static const signed char yypact_ninf_; + static const signed char yytable_ninf_; + + /// Convert a scanner token kind \a t to a symbol kind. + /// In theory \a t should be a token_kind_type, but character literals + /// are valid, yet not members of the token_type enum. + static symbol_kind_type yytranslate_ (int t); + +#if YYDEBUG || 0 + /// For a symbol, its name in clear. + static const char* const yytname_[]; +#endif // #if YYDEBUG || 0 + + + // Tables. + // YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + // STATE-NUM. + static const short yypact_[]; + + // YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + // Performed when YYTABLE does not specify something else to do. Zero + // means the default is an error. + static const signed char yydefact_[]; + + // YYPGOTO[NTERM-NUM]. + static const signed char yypgoto_[]; + + // YYDEFGOTO[NTERM-NUM]. + static const signed char yydefgoto_[]; + + // YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + // positive, shift that token. If negative, reduce the rule whose + // number is the opposite. If YYTABLE_NINF, syntax error. + static const signed char yytable_[]; + + static const signed char yycheck_[]; + + // YYSTOS[STATE-NUM] -- The (internal number of the) accessing + // symbol of state STATE-NUM. + static const signed char yystos_[]; + + // YYR1[YYN] -- Symbol number of symbol that rule YYN derives. + static const signed char yyr1_[]; + + // YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. + static const signed char yyr2_[]; + + +#if YYDEBUG + // YYRLINE[YYN] -- Source line where rule number YYN was defined. + static const unsigned char yyrline_[]; + /// Report on the debug stream that the rule \a r is going to be reduced. + virtual void yy_reduce_print_ (int r) const; + /// Print the state stack on the debug stream. + virtual void yy_stack_print_ () const; + + /// Debugging level. + int yydebug_; + /// Debug stream. + std::ostream* yycdebug_; + + /// \brief Display a symbol kind, value and location. + /// \param yyo The output stream. + /// \param yysym The symbol. + template <typename Base> + void yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const; +#endif + + /// \brief Reclaim the memory associated to a symbol. + /// \param yymsg Why this token is reclaimed. + /// If null, print nothing. + /// \param yysym The symbol. + template <typename Base> + void yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const; + + private: + /// Type access provider for state based symbols. + struct by_state + { + /// Default constructor. + by_state () YY_NOEXCEPT; + + /// The symbol kind as needed by the constructor. + typedef state_type kind_type; + + /// Constructor. + by_state (kind_type s) YY_NOEXCEPT; + + /// Copy constructor. + by_state (const by_state& that) YY_NOEXCEPT; + + /// Record that this symbol is empty. + void clear () YY_NOEXCEPT; + + /// Steal the symbol kind from \a that. + void move (by_state& that); + + /// The symbol kind (corresponding to \a state). + /// \a symbol_kind::S_YYEMPTY when empty. + symbol_kind_type kind () const YY_NOEXCEPT; + + /// The state number used to denote an empty symbol. + /// We use the initial state, as it does not have a value. + enum { empty_state = 0 }; + + /// The state. + /// \a empty when empty. + state_type state; + }; + + /// "Internal" symbol: element of the stack. + struct stack_symbol_type : basic_symbol<by_state> + { + /// Superclass. + typedef basic_symbol<by_state> super_type; + /// Construct an empty symbol. + stack_symbol_type (); + /// Move or copy construction. + stack_symbol_type (YY_RVREF (stack_symbol_type) that); + /// Steal the contents from \a sym to build this. + stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) sym); +#if YY_CPLUSPLUS < 201103L + /// Assignment, needed by push_back by some old implementations. + /// Moves the contents of that. + stack_symbol_type& operator= (stack_symbol_type& that); + + /// Assignment, needed by push_back by other implementations. + /// Needed by some other old implementations. + stack_symbol_type& operator= (const stack_symbol_type& that); +#endif + }; + + /// A stack with random access from its top. + template <typename T, typename S = std::vector<T> > + class stack + { + public: + // Hide our reversed order. + typedef typename S::iterator iterator; + typedef typename S::const_iterator const_iterator; + typedef typename S::size_type size_type; + typedef typename std::ptrdiff_t index_type; + + stack (size_type n = 200) + : seq_ (n) + {} + +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + stack (const stack&) = delete; + /// Non copyable. + stack& operator= (const stack&) = delete; +#endif + + /// Random access. + /// + /// Index 0 returns the topmost element. + const T& + operator[] (index_type i) const + { + return seq_[size_type (size () - 1 - i)]; + } + + /// Random access. + /// + /// Index 0 returns the topmost element. + T& + operator[] (index_type i) + { + return seq_[size_type (size () - 1 - i)]; + } + + /// Steal the contents of \a t. + /// + /// Close to move-semantics. + void + push (YY_MOVE_REF (T) t) + { + seq_.push_back (T ()); + operator[] (0).move (t); + } + + /// Pop elements from the stack. + void + pop (std::ptrdiff_t n = 1) YY_NOEXCEPT + { + for (; 0 < n; --n) + seq_.pop_back (); + } + + /// Pop all elements from the stack. + void + clear () YY_NOEXCEPT + { + seq_.clear (); + } + + /// Number of elements on the stack. + index_type + size () const YY_NOEXCEPT + { + return index_type (seq_.size ()); + } + + /// Iterator on top of the stack (going downwards). + const_iterator + begin () const YY_NOEXCEPT + { + return seq_.begin (); + } + + /// Bottom of the stack. + const_iterator + end () const YY_NOEXCEPT + { + return seq_.end (); + } + + /// Present a slice of the top of a stack. + class slice + { + public: + slice (const stack& stack, index_type range) + : stack_ (stack) + , range_ (range) + {} + + const T& + operator[] (index_type i) const + { + return stack_[range_ - i]; + } + + private: + const stack& stack_; + index_type range_; + }; + + private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. + stack (const stack&); + /// Non copyable. + stack& operator= (const stack&); +#endif + /// The wrapped container. + S seq_; + }; + + + /// Stack type. + typedef stack<stack_symbol_type> stack_type; + + /// The stack. + stack_type yystack_; + + /// Push a new state on the stack. + /// \param m a debug message to display + /// if null, no trace is output. + /// \param sym the symbol + /// \warning the contents of \a s.value is stolen. + void yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym); + + /// Push a new look ahead token on the state on the stack. + /// \param m a debug message to display + /// if null, no trace is output. + /// \param s the state + /// \param sym the symbol (for its value and location). + /// \warning the contents of \a sym.value is stolen. + void yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym); + + /// Pop \a n symbols from the stack. + void yypop_ (int n = 1); + + /// Constants. + enum + { + yylast_ = 279, ///< Last index in yytable_. + yynnts_ = 4, ///< Number of nonterminal symbols. + yyfinal_ = 24 ///< Termination state number. + }; + + + // User arguments. + QL::Result &result; + + }; + + inline + Parser::symbol_kind_type + Parser::yytranslate_ (int t) + { + // YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to + // TOKEN-NUM as returned by yylex. + static + const signed char + translate_table[] = + { + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62 + }; + // Last valid token kind. + const int code_max = 317; + + if (t <= 0) + return symbol_kind::S_YYEOF; + else if (t <= code_max) + return YY_CAST (symbol_kind_type, translate_table[t]); + else + return symbol_kind::S_YYUNDEF; + } + + // basic_symbol. + template <typename Base> + Parser::basic_symbol<Base>::basic_symbol (const basic_symbol& that) + : Base (that) + , value () + { + switch (this->kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.copy< Expression * > (YY_MOVE (that.value)); + break; + + default: + break; + } + + } + + + + template <typename Base> + Parser::symbol_kind_type + Parser::basic_symbol<Base>::type_get () const YY_NOEXCEPT + { + return this->kind (); + } + + template <typename Base> + bool + Parser::basic_symbol<Base>::empty () const YY_NOEXCEPT + { + return this->kind () == symbol_kind::S_YYEMPTY; + } + + template <typename Base> + void + Parser::basic_symbol<Base>::move (basic_symbol& s) + { + super_type::move (s); + switch (this->kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.move< Expression * > (YY_MOVE (s.value)); + break; + + default: + break; + } + + } + + // by_kind. + inline + Parser::by_kind::by_kind () + : kind_ (symbol_kind::S_YYEMPTY) + {} + +#if 201103L <= YY_CPLUSPLUS + inline + Parser::by_kind::by_kind (by_kind&& that) + : kind_ (that.kind_) + { + that.clear (); + } +#endif + + inline + Parser::by_kind::by_kind (const by_kind& that) + : kind_ (that.kind_) + {} + + inline + Parser::by_kind::by_kind (token_kind_type t) + : kind_ (yytranslate_ (t)) + {} + + inline + void + Parser::by_kind::clear () YY_NOEXCEPT + { + kind_ = symbol_kind::S_YYEMPTY; + } + + inline + void + Parser::by_kind::move (by_kind& that) + { + kind_ = that.kind_; + that.clear (); + } + + inline + Parser::symbol_kind_type + Parser::by_kind::kind () const YY_NOEXCEPT + { + return kind_; + } + + inline + Parser::symbol_kind_type + Parser::by_kind::type_get () const YY_NOEXCEPT + { + return this->kind (); + } + +#line 50 "QLParser.yy" +} // QL +#line 2034 "QLParser.tab.hh" + + + + +#endif // !YY_YY_QLPARSER_TAB_HH_INCLUDED diff --git a/gprofng/src/QLParser.yy b/gprofng/src/QLParser.yy new file mode 100644 index 0000000..689d0e1 --- /dev/null +++ b/gprofng/src/QLParser.yy @@ -0,0 +1,390 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +// To rebuild QLParser.tab.cc and QLParser.tab.hh, use bison 3.6 or newer: +// cd gprofng/src && bison QLParser.yy + +// For "api.parser.class" +%require "3.3" +%language "C++" + +%code top { +#include <stdio.h> +#include <string.h> +#include <string> +} +%code requires { +#include "QLParser.h" +#include "DbeSession.h" +#include "Expression.h" +#include "Table.h" +#include "i18n.h" +} + +%code +{ +namespace QL +{ + static QL::Parser::symbol_type yylex (QL::Result &result); +} +} + +%defines +%define api.namespace {QL} +%define api.parser.class {Parser} +%define api.token.constructor +%define api.value.type variant +// Later: api.value.automove +%define api.token.prefix {L_} +%define parse.assert +%param {QL::Result &result} + +%start S + +%token LPAR "(" + RPAR ")" + NUM "number" + NAME "name" + FNAME + HASPROP + JGROUP + JPARENT + QSTR + FILEIOVFD + +%nonassoc IN SOME ORDR +%left COMMA "," +%right QWE "?" + COLON ":" +%left AND "&&" + OR "|" + EQV NEQV + BITAND BITOR + BITXOR "^" +%nonassoc EQ "=" + NE "!=" + LT "<" + GT ">" + LE "<=" + GE ">=" +%left LS "<<" + RS ">>" + ADD "+" + MINUS "-" + MUL "*" + DIV "/" + REM "%" +%right DEG + NOT "!" + BITNOT "~" + +%type <Expression *> QSTR NUM NAME FNAME JGROUP JPARENT FILEIOVFD exp term + +%destructor { delete $$; } <*>; + +%% + +S: /* empty */ { result.out = new Expression (Expression::OP_NUM, (uint64_t) 1); } +| exp { result.out = new Expression ($1); } + +exp: exp DEG exp { $$ = new Expression (Expression::OP_DEG, $1, $3); } /* dead? */ + | exp MUL exp { $$ = new Expression (Expression::OP_MUL, $1, $3); } + | exp DIV exp { $$ = new Expression (Expression::OP_DIV, $1, $3); } + | exp REM exp { $$ = new Expression (Expression::OP_REM, $1, $3); } + | exp ADD exp { $$ = new Expression (Expression::OP_ADD, $1, $3); } + | exp MINUS exp { $$ = new Expression (Expression::OP_MINUS, $1, $3); } + | exp LS exp { $$ = new Expression (Expression::OP_LS, $1, $3); } + | exp RS exp { $$ = new Expression (Expression::OP_RS, $1, $3); } + | exp LT exp { $$ = new Expression (Expression::OP_LT, $1, $3); } + | exp LE exp { $$ = new Expression (Expression::OP_LE, $1, $3); } + | exp GT exp { $$ = new Expression (Expression::OP_GT, $1, $3); } + | exp GE exp { $$ = new Expression (Expression::OP_GE, $1, $3); } + | exp EQ exp { $$ = new Expression (Expression::OP_EQ, $1, $3); } + | exp NE exp { $$ = new Expression (Expression::OP_NE, $1, $3); } + | exp BITAND exp { $$ = new Expression (Expression::OP_BITAND, $1, $3); } + | exp BITXOR exp { $$ = new Expression (Expression::OP_BITXOR, $1, $3); } + | exp BITOR exp { $$ = new Expression (Expression::OP_BITOR, $1, $3); } + | exp AND exp { $$ = new Expression (Expression::OP_AND, $1, $3); } + | exp OR exp { $$ = new Expression (Expression::OP_OR, $1, $3); } + | exp NEQV exp { $$ = new Expression (Expression::OP_NEQV, $1, $3); } /* dead? */ + | exp EQV exp { $$ = new Expression (Expression::OP_EQV, $1, $3); } /* dead? */ + | exp QWE exp COLON exp { Expression colon = Expression (Expression::OP_COLON, $3, $5); + $$ = new Expression (Expression::OP_QWE, $1, &colon); } + | exp COMMA exp { $$ = new Expression (Expression::OP_COMMA, $1, $3); } + | exp IN exp { $$ = new Expression (Expression::OP_IN, $1, $3); } + | exp SOME IN exp { $$ = new Expression (Expression::OP_SOMEIN, $1, $4); } + | exp ORDR IN exp { $$ = new Expression (Expression::OP_ORDRIN, $1, $4); } + | term { $$ = new Expression ($1); } + +term: MINUS term { Expression num = Expression (Expression::OP_NUM, (uint64_t) 0); + $$ = new Expression (Expression::OP_MINUS, &num, $2); } + | NOT term { $$ = new Expression (Expression::OP_NOT, $2, NULL); } + | BITNOT term { $$ = new Expression (Expression::OP_BITNOT, $2, NULL); } + | LPAR exp RPAR { $$ = new Expression ($2); } + | FNAME LPAR QSTR RPAR { $$ = new Expression (Expression::OP_FUNC, $1, $3); } + | HASPROP LPAR NAME RPAR { $$ = new Expression (Expression::OP_HASPROP, $3, NULL); } + | JGROUP LPAR QSTR RPAR { $$ = new Expression (Expression::OP_JAVA, $1, $3); } + | JPARENT LPAR QSTR RPAR { $$ = new Expression (Expression::OP_JAVA, $1, $3); } + | FILEIOVFD LPAR QSTR RPAR { $$ = new Expression (Expression::OP_FILE, $1, $3); } + | NUM { $$ = new Expression ($1); } + | NAME { $$ = new Expression ($1); } + +%% + +namespace QL +{ + static Parser::symbol_type + unget_ret (std::istream &in, char c, Parser::symbol_type tok) + { + in.putback (c); + return tok; + } + + static Expression * + processName (char *name) + { + int propID = dbeSession->getPropIdByName (name); + if (propID != PROP_NONE) + { + Expression *expr = new Expression (Expression::OP_NUM, (uint64_t) propID); + Expression *ret = new Expression (Expression::OP_NAME, expr); + delete expr; + return ret; + } + + // If a name is not statically known try user defined objects + Expression *expr = dbeSession->findObjDefByName (name); + if (expr != NULL) + return expr->copy(); + + throw Parser::syntax_error ("Name not found"); + } + + static Parser::symbol_type + yylex (QL::Result &result) + { + int base = 0; + int c; + + do + c = result.in.get (); + while (result.in && (c == ' ' || c == '\t')); + if (!result.in) + return Parser::make_YYEOF (); + + switch (c) + { + case '\n': return Parser::make_YYEOF (); + case '(': return Parser::make_LPAR () ; + case ')': return Parser::make_RPAR (); + case ',': return Parser::make_COMMA (); + case '%': return Parser::make_REM (); + case '/': return Parser::make_DIV (); + case '*': return Parser::make_MUL (); + case '-': return Parser::make_MINUS (); + case '+': return Parser::make_ADD (); + case '~': return Parser::make_BITNOT (); + case '^': return Parser::make_BITXOR (); + case '?': return Parser::make_QWE (); + case ':': return Parser::make_COLON (); + case '|': + c = result.in.get (); + if (c == '|') + return Parser::make_OR (); + else + return unget_ret (result.in, c, Parser::make_BITOR ()); + case '&': + c = result.in.get (); + if (c == '&') + return Parser::make_AND (); + else + return unget_ret (result.in, c, Parser::make_BITAND ()); + case '!': + c = result.in.get (); + if (c == '=') + return Parser::make_NE (); + else + return unget_ret (result.in, c, Parser::make_NOT ()); + case '=': + c = result.in.get (); + if (c == '=') + return Parser::make_EQ (); + else + throw Parser::syntax_error ("Syntax error after ="); + case '<': + c = result.in.get (); + if (c == '=') + return Parser::make_LE (); + else if (c == '<') + return Parser::make_LS (); + else + return unget_ret (result.in, c, Parser::make_LT ()); + case '>': + c = result.in.get (); + if (c == '=') + return Parser::make_GE (); + else if (c == '>') + return Parser::make_RS (); + else + return unget_ret (result.in, c, Parser::make_GT ()); + case '"': + { + int maxsz = 16; + char *str = (char *) malloc (maxsz); + char *ptr = str; + + for (;;) + { + c = result.in.get (); + if (!result.in) + { + free (str); + throw Parser::syntax_error ("Unclosed \""); + } + + switch (c) + { + case '"': + *ptr = (char)0; + // XXX omazur: need new string type + return Parser::make_QSTR (new Expression (Expression::OP_NUM, (uint64_t) str)); + case 0: + case '\n': + free (str); + throw Parser::syntax_error ("Multiline strings are not supported"); + default: + if (ptr - str >= maxsz) + { + size_t len = ptr - str; + maxsz = maxsz > 8192 ? maxsz + 8192 : maxsz * 2; + char *new_s = (char *) realloc (str, maxsz); + str = new_s; + ptr = str + len; + } + *ptr++ = c; + } + } + } + default: + if (c == '0') + { + base = 8; + c = result.in.get (); + if ( c == 'x' ) + { + base = 16; + c = result.in.get (); + } + } + else if (c >= '1' && c <='9') + base = 10; + + if (base) + { + uint64_t lval = 0; + for (;;) + { + int digit = -1; + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + digit = c - '0'; + break; + case '8': case '9': + if (base > 8) + digit = c - '0'; + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + if (base == 16) + digit = c - 'a' + 10; + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + if (base == 16) + digit = c - 'A' + 10; + break; + } + if (digit == -1) + { + result.in.putback (c); + break; + } + lval = lval * base + digit; + c = result.in.get (); + } + return Parser::make_NUM (new Expression (Expression::OP_NUM, lval)); + } + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + { + char name[32]; // omazur XXX: accept any length + name[0] = (char)c; + for (size_t i = 1; i < sizeof (name); i++) + { + c = result.in.get (); + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || (c == '_')) + name[i] = c; + else + { + name[i] = (char)0; + result.in.putback (c); + break; + } + } + + if (strcasecmp (name, NTXT ("IN")) == 0) + return Parser::make_IN (); + else if (strcasecmp (name, NTXT ("SOME")) == 0) + return Parser::make_SOME (); + else if (strcasecmp (name, NTXT ("ORDERED")) == 0) + return Parser::make_ORDR (); + else if (strcasecmp (name, NTXT ("TRUE")) == 0) + return Parser::make_NUM (new Expression (Expression::OP_NUM, (uint64_t) 1)); + else if (strcasecmp (name, NTXT ("FALSE")) == 0) + return Parser::make_NUM (new Expression (Expression::OP_NUM, (uint64_t) 0)); + else if (strcasecmp (name, NTXT ("FNAME")) == 0) + return Parser::make_FNAME (new Expression (Expression::OP_NUM, Expression::FUNC_FNAME)); + else if (strcasecmp (name, NTXT ("HAS_PROP")) == 0) + return Parser::make_HASPROP (); + else if (strcasecmp (name, NTXT ("JGROUP")) == 0) + return Parser::make_JGROUP (new Expression (Expression::OP_NUM, Expression::JAVA_JGROUP)); + else if (strcasecmp (name, NTXT ("JPARENT")) == 0 ) + return Parser::make_JPARENT (new Expression (Expression::OP_NUM, Expression::JAVA_JPARENT)); + else if (strcasecmp (name, NTXT ("DNAME")) == 0) + return Parser::make_FNAME (new Expression (Expression::OP_NUM, Expression::FUNC_DNAME)); + else if (strcasecmp (name, NTXT ("FILEIOVFD")) == 0 ) + return Parser::make_FILEIOVFD (new Expression (Expression::OP_NUM, (uint64_t) 0)); + + return Parser::make_NAME (processName (name)); + } + + throw Parser::syntax_error ("Syntax error"); + } + } + void + Parser::error (const std::string &) + { + // do nothing for now + } +} + diff --git a/gprofng/src/SAXParser.h b/gprofng/src/SAXParser.h new file mode 100644 index 0000000..dde3e06 --- /dev/null +++ b/gprofng/src/SAXParser.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * javax/xml/parsers/SAXParser.java + * + * Based on JavaTM 2 Platform Standard Ed. 5.0 + */ + +#ifndef _SAXParser_h +#define _SAXParser_h + +class File; +class DefaultHandler; +class SAXException; + +class SAXParser +{ +public: + + virtual ~SAXParser () { } + virtual void reset () { } + virtual void parse (File*, DefaultHandler*) = 0; + virtual bool isNamespaceAware () = 0; + virtual bool isValidating () = 0; + +protected: + + SAXParser () { } +}; + +#endif /* _SAXParser_h */ diff --git a/gprofng/src/SAXParserFactory.cc b/gprofng/src/SAXParserFactory.cc new file mode 100644 index 0000000..7d9e851 --- /dev/null +++ b/gprofng/src/SAXParserFactory.cc @@ -0,0 +1,666 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> + +#include "util.h" +#include "vec.h" +#include "DefaultHandler.h" +#include "SAXParser.h" +#include "SAXParserFactory.h" +#include "StringBuilder.h" + +/* + * Private implementation of Attributes + */ +class AttributesP : public Attributes +{ +public: + AttributesP (); + ~AttributesP (); + int getLength (); + const char *getQName (int index); + const char *getValue (int index); + int getIndex (const char *qName); + const char *getValue (const char *qName); + void append (char *qName, char *value); + +private: + Vector<char*> *names; + Vector<char*> *values; +}; + +AttributesP::AttributesP () +{ + names = new Vector<char*>; + values = new Vector<char*>; +} + +AttributesP::~AttributesP () +{ + Destroy (names); + Destroy (values); +} + +int +AttributesP::getLength () +{ + return names->size (); +} + +const char * +AttributesP::getQName (int index) +{ + if (index < 0 || index >= names->size ()) + return NULL; + return names->fetch (index); +} + +const char * +AttributesP::getValue (int index) +{ + if (index < 0 || index >= values->size ()) + return NULL; + return values->fetch (index); +} + +int +AttributesP::getIndex (const char *qName) +{ + for (int idx = 0; idx < names->size (); idx++) + if (strcmp (names->fetch (idx), qName) == 0) + return idx; + return -1; +} + +const char * +AttributesP::getValue (const char *qName) +{ + for (int idx = 0; idx < names->size (); idx++) + if (strcmp (names->fetch (idx), qName) == 0) + return values->fetch (idx); + return NULL; +} + +void +AttributesP::append (char *qName, char *value) +{ + names->append (qName); + values->append (value); +} + +/* + * Implementation of SAXException + */ +SAXException::SAXException () +{ + message = strdup ("null"); +} + +SAXException::SAXException (const char *_message) +{ + if (_message == NULL) + message = strdup ("null"); + else + message = strdup (_message); +} + +SAXException::~SAXException () +{ + free (message); +} + +char * +SAXException::getMessage () +{ + return message; +} + +/* + * SAXParseException + */ +SAXParseException::SAXParseException (char *message, int _lineNumber, int _columnNumber) +: SAXException (message == NULL ? GTXT ("XML parse error") : message) +{ + lineNumber = _lineNumber; + columnNumber = _columnNumber; +} + +/* + * Private implementation of SAXParser + */ +class SAXParserP : public SAXParser +{ +public: + SAXParserP (); + ~SAXParserP (); + void reset (); + void parse (File*, DefaultHandler*); + + bool + isNamespaceAware () + { + return false; + } + + bool + isValidating () + { + return false; + } + +private: + + static const int CH_EOF = -1; + + void nextch (); + bool isWSpace (); + void skipWSpaces (); + void scanString (const char *str); + char *parseName (); + char *parseString (); + char *decodeString (char *str); + Attributes *parseAttributes (); + void parseTag (); + void parseDocument (); + void parsePart (int idx); + + DefaultHandler *dh; + int bufsz; + char *buffer; + int cntsz; + int idx; + int curch; + int line; + int column; +}; + +SAXParserP::SAXParserP () +{ + dh = NULL; + bufsz = 0x2000; + buffer = (char*) malloc (bufsz); + cntsz = 0; + idx = 0; + line = 1; + column = 0; +} + +SAXParserP::~SAXParserP () +{ + free (buffer); +} + +void +SAXParserP::reset () +{ + dh = NULL; + bufsz = 8192; + buffer = (char*) realloc (buffer, bufsz); + cntsz = 0; + idx = 0; + line = 1; + column = 0; +} + +void +SAXParserP::parse (File *f, DefaultHandler *_dh) +{ + if (_dh == NULL) + return; + dh = _dh; + FILE *file = (FILE*) f; + int rem = bufsz; + cntsz = 0; + idx = 0; + for (;;) + { + int n = (int) fread (buffer + cntsz, 1, rem, file); + if (ferror (file) || n <= 0) + break; + cntsz += n; + if (feof (file)) + break; + rem -= n; + if (rem == 0) + { + int oldbufsz = bufsz; + bufsz = bufsz >= 0x100000 ? bufsz + 0x100000 : bufsz * 2; + buffer = (char*) realloc (buffer, bufsz); + rem = bufsz - oldbufsz; + } + } + nextch (); + parseDocument (); +} + +static int +hex (char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return 10 + (c - 'a'); + return -1; +} + +void +SAXParserP::nextch () +{ + curch = idx >= cntsz ? CH_EOF : buffer[idx++]; + if (curch == '\n') + { + line += 1; + column = 0; + } + else + column += 1; +} + +bool +SAXParserP::isWSpace () +{ + return curch == ' ' || curch == '\t' || curch == '\n' || curch == '\r'; +} + +void +SAXParserP::skipWSpaces () +{ + while (isWSpace ()) + nextch (); +} + +void +SAXParserP::scanString (const char *str) +{ + if (str == NULL || *str == '\0') + return; + for (;;) + { + if (curch == CH_EOF) + break; + else if (curch == *str) + { + const char *p = str; + for (;;) + { + p += 1; + nextch (); + if (*p == '\0') + return; + if (curch != *p) + break; + } + } + nextch (); + } +} + +char * +SAXParserP::parseName () +{ + StringBuilder *name = new StringBuilder (); + + if ((curch >= 'A' && curch <= 'Z') || (curch >= 'a' && curch <= 'z')) + { + name->append ((char) curch); + nextch (); + while (isalnum (curch) != 0 || curch == '_') + { + name->append ((char) curch); + nextch (); + } + } + + char *res = name->toString (); + delete name; + return res; +} + +/** + * Replaces encoded XML characters with original characters + * Attention: this method reuses the same string that is passed as the argument + * @param str + * @return str + */ +char * +SAXParserP::decodeString (char * str) +{ + // Check if string has %22% and replace it with double quotes + // Also replace all other special combinations. + char *from = str; + char *to = str; + if (strstr (from, "%") || strstr (from, "&")) + { + int len = strlen (from); + for (int i = 0; i < len; i++) + { + int nch = from[i]; + // Process &...; combinations + if (nch == '&' && i + 3 < len) + { + if (from[i + 2] == 't' && from[i + 3] == ';') + { + // check < > + if (from[i + 1] == 'l') + { + nch = '<'; + i += 3; + } + else if (from[i + 1] == 'g') + { + nch = '>'; + i += 3; + } + } + else if (i + 4 < len && from[i + 4] == ';') + { + // check & + if (from[i + 1] == 'a' && from[i + 2] == 'm' && from[i + 3] == 'p') + { + nch = '&'; + i += 4; + } + } + else if ((i + 5 < len) && (from[i + 5] == ';')) + { + // check ' " + if (from[i + 1] == 'a' && from[i + 2] == 'p' + && from[i + 3] == 'o' && from[i + 4] == 's') + { + nch = '\''; + i += 5; + } + if (from[i + 1] == 'q' && from[i + 2] == 'u' && from[i + 3] == 'o' && from[i + 4] == 't') + { + nch = '"'; + i += 5; + } + } + } + // Process %XX% combinations + if (nch == '%' && i + 3 < len && from[i + 3] == '%') + { + int ch = hex (from[i + 1]); + if (ch >= 0) + { + int ch2 = hex (from[i + 2]); + if (ch2 >= 0) + { + ch = ch * 16 + ch2; + nch = ch; + i += 3; + } + } + } + *to++ = (char) nch; + } + *to = '\0'; + } + return str; +} + +char * +SAXParserP::parseString () +{ + StringBuilder *str = new StringBuilder (); + int quote = '>'; + if (curch == '"') + { + quote = curch; + nextch (); + } + for (;;) + { + if (curch == CH_EOF) + break; + if (curch == quote) + { + nextch (); + break; + } + str->append ((char) curch); + nextch (); + } + + char *res = str->toString (); + // Decode XML characters + res = decodeString (res); + delete str; + return res; +} + +Attributes * +SAXParserP::parseAttributes () +{ + AttributesP *attrs = new AttributesP (); + + for (;;) + { + skipWSpaces (); + char *name = parseName (); + if (name == NULL || *name == '\0') + { + free (name); + break; + } + skipWSpaces (); + if (curch != '=') + { + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + scanString (">"); + free (name); + return attrs; + } + nextch (); + skipWSpaces (); + char *value = parseString (); + attrs->append (name, value); + } + return attrs; +} + +void +SAXParserP::parseTag () +{ + skipWSpaces (); + bool empty = false; + char *name = parseName (); + if (name == NULL || *name == '\0') + { + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + scanString (">"); + free (name); + return; + } + + Attributes *attrs = parseAttributes (); + if (curch == '/') + { + nextch (); + empty = true; + } + if (curch == '>') + nextch (); + else + { + empty = false; + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + scanString (">"); + } + if (curch == CH_EOF) + { + free (name); + delete attrs; + return; + } + dh->startElement (NULL, NULL, name, attrs); + if (empty) + { + dh->endElement (NULL, NULL, name); + free (name); + delete attrs; + return; + } + + StringBuilder *chars = new StringBuilder (); + bool wspaces = true; + for (;;) + { + if (curch == CH_EOF) + break; + else if (curch == '<') + { + if (chars->length () > 0) + { + char *str = chars->toString (); + // Decode XML characters + str = decodeString (str); + if (wspaces) + dh->ignorableWhitespace (str, 0, chars->length ()); + else + dh->characters (str, 0, chars->length ()); + free (str); + chars->setLength (0); + wspaces = true; + } + nextch (); + if (curch == '/') + { + nextch (); + char *ename = parseName (); + if (ename && *ename != '\0') + { + if (strcmp (name, ename) == 0) + { + skipWSpaces (); + if (curch == '>') + { + nextch (); + dh->endElement (NULL, NULL, name); + free (ename); + break; + } + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + } + else + { + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + } + scanString (">"); + } + free (ename); + } + else + parseTag (); + } + else + { + if (!isWSpace ()) + wspaces = false; + chars->append ((char) curch); + nextch (); + } + } + + free (name); + delete attrs; + delete chars; + return; +} + +void +SAXParserP::parseDocument () +{ + dh->startDocument (); + for (;;) + { + if (curch == CH_EOF) + break; + if (curch == '<') + { + nextch (); + if (curch == '?') + scanString ("?>"); + else if (curch == '!') + scanString (">"); + else + parseTag (); + } + else + nextch (); + } + dh->endDocument (); +} + +/* + * Private implementation of SAXParserFactory + */ +class SAXParserFactoryP : public SAXParserFactory +{ +public: + SAXParserFactoryP () { } + ~SAXParserFactoryP () { } + SAXParser *newSAXParser (); + + void + setFeature (const char *, bool) { } + + bool + getFeature (const char *) + { + return false; + } +}; + +SAXParser * +SAXParserFactoryP::newSAXParser () +{ + return new SAXParserP (); +} + +/* + * SAXParserFactory + */ +const char *SAXParserFactory::DEFAULT_PROPERTY_NAME = "javax.xml.parsers.SAXParserFactory"; + +SAXParserFactory * +SAXParserFactory::newInstance () +{ + return new SAXParserFactoryP (); +} + +void +DefaultHandler::dump_startElement (const char *qName, Attributes *attrs) +{ + fprintf (stderr, NTXT ("DefaultHandler::startElement qName='%s'\n"), STR (qName)); + for (int i = 0, sz = attrs ? attrs->getLength () : 0; i < sz; i++) + { + const char *qn = attrs->getQName (i); + const char *vl = attrs->getValue (i); + fprintf (stderr, NTXT (" %d '%s' = '%s'\n"), i, STR (qn), STR (vl)); + } +} diff --git a/gprofng/src/SAXParserFactory.h b/gprofng/src/SAXParserFactory.h new file mode 100644 index 0000000..8e2c366 --- /dev/null +++ b/gprofng/src/SAXParserFactory.h @@ -0,0 +1,75 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * javax/xml/parsers/SAXParserFactory.java + * + * Based on JavaTM 2 Platform Standard Ed. 5.0 + */ + +#ifndef _SAXParserFactory_h +#define _SAXParserFactory_h + +class SAXParser; + +class SAXParserFactory +{ +public: + static SAXParserFactory *newInstance (); + + virtual ~SAXParserFactory () { } + virtual SAXParser *newSAXParser () = 0; + virtual void setFeature (const char *name, bool value) = 0; + virtual bool getFeature (const char *name) = 0; + + void + setNamespaceAware (bool awareness) + { + namespaceAware = awareness; + } + + void + setValidating (bool _validating) + { + validating = _validating; + } + + bool + isNamespaceAware () + { + return namespaceAware; + } + + bool + isValidating () + { + return validating; + } + +protected: + SAXParserFactory () { } + +private: + static const char *DEFAULT_PROPERTY_NAME; + bool validating; + bool namespaceAware; +}; + +#endif /* _SAXParserFactory_h */ diff --git a/gprofng/src/Sample.cc b/gprofng/src/Sample.cc new file mode 100644 index 0000000..7c00334 --- /dev/null +++ b/gprofng/src/Sample.cc @@ -0,0 +1,94 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <assert.h> +#include "Sample.h" +#include "util.h" +#include "Exp_Layout.h" + +Sample::Sample (int num) +{ + number = num; + prusage = NULL; + start_time = end_time = 0; + start_label = end_label = NULL; + validated = false; +} + +Sample::~Sample () +{ + delete prusage; + free (start_label); + free (end_label); +} + +PrUsage * +Sample::get_usage () +{ + if (validated == false) + { + validate_usage (); + validated = true; + } + return prusage; +} + +void +Sample::validate_usage () +{ + if (prusage == NULL || validated) + return; + validated = true; + + // Make sure that none of the times are negative, force to zero if so + if (prusage->pr_utime < 0) + prusage->pr_utime = 0; + if (prusage->pr_stime < 0) + prusage->pr_stime = 0; + if (prusage->pr_ttime < 0) + prusage->pr_ttime = 0; + if (prusage->pr_tftime < 0) + prusage->pr_tftime = 0; + if (prusage->pr_dftime < 0) + prusage->pr_dftime = 0; + if (prusage->pr_kftime < 0) + prusage->pr_kftime = 0; + if (prusage->pr_ltime < 0) + prusage->pr_ltime = 0; + if (prusage->pr_slptime < 0) + prusage->pr_slptime = 0; + if (prusage->pr_wtime < 0) + prusage->pr_wtime = 0; + if (prusage->pr_stoptime < 0) + prusage->pr_stoptime = 0; + if (prusage->pr_rtime < 0) + prusage->pr_rtime = 0; + + // Now make sure that the sum of states is >= prusage->pr_rtime + hrtime_t sum = prusage->pr_utime + prusage->pr_stime + prusage->pr_ttime + + prusage->pr_tftime + prusage->pr_dftime + prusage->pr_kftime + + prusage->pr_ltime + prusage->pr_slptime + prusage->pr_wtime + + prusage->pr_stoptime; + + sum = sum - prusage->pr_rtime; + if (sum < 0)// increment sleep time to make it match + prusage->pr_slptime = prusage->pr_slptime - sum; +} diff --git a/gprofng/src/Sample.h b/gprofng/src/Sample.h new file mode 100644 index 0000000..312bdcc --- /dev/null +++ b/gprofng/src/Sample.h @@ -0,0 +1,80 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _SAMPLE_H +#define _SAMPLE_H + +// A data Sample object represents a single sample's worth of data. This +// object is private and is only used by Experiment and Sample_sel. + +#include "dbe_types.h" + +class PrUsage; + +class Sample +{ + friend class Experiment; // see post_process(), read_overview_file() +public: + Sample (int num); + ~Sample (); + PrUsage *get_usage (); + + char * + get_start_label () + { + return start_label; + } + + char * + get_end_label () + { + return end_label; + } + + hrtime_t + get_start_time () + { + return start_time; + } + + hrtime_t + get_end_time () + { + return end_time; + } + + int + get_number () + { + return number; + } + +private: + void validate_usage (); // Make sure usage data is consistent + bool validated; // if validation performed + char *start_label; // sample start label + char *end_label; // sample end label + hrtime_t start_time; // sample start time + hrtime_t end_time; // sample end time + PrUsage *prusage; // process usage data + int number; // sample number +}; + +#endif /* _SAMPLE_H */ diff --git a/gprofng/src/SegMem.h b/gprofng/src/SegMem.h new file mode 100644 index 0000000..fbfe727 --- /dev/null +++ b/gprofng/src/SegMem.h @@ -0,0 +1,76 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _SEGMEM_H +#define _SEGMEM_H + +#include "dbe_types.h" +class Histable; + +class SegMem +{ +public: + + // The various segments types. + enum Seg_mode + { + READ, + WRITE, + EXEC, + UNKNOWN + }; + + void + set_file_offset (uint64_t fo) + { + file_offset = fo; + } + + uint64_t + get_file_offset () + { + return file_offset; + } + + void + set_mode (Seg_mode sm) + { + mode = sm; + } + + Seg_mode + get_mode () + { + return mode; + } + + Size size; // Size of this instance + Histable *obj; // Pointer to Segment/Function object + Vaddr base; // Base address + hrtime_t load_time; + hrtime_t unload_time; + Size page_size; + +private: + uint64_t file_offset; + Seg_mode mode; +}; + +#endif /* _SEGMEM_H */ diff --git a/gprofng/src/Settings.cc b/gprofng/src/Settings.cc new file mode 100644 index 0000000..965b917 --- /dev/null +++ b/gprofng/src/Settings.cc @@ -0,0 +1,1586 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/param.h> + +#include "enums.h" +#include "Settings.h" +#include "DbeSession.h" +#include "Command.h" +#include "Application.h" +#include "MemorySpace.h" +#include "StringBuilder.h" +#include "Table.h" +#include "Emsg.h" +#include "util.h" +#include "i18n.h" + +// Commands for compiler commentary +static const char *comp_cmd[] = { + NTXT ("basic"), + NTXT ("version"), + NTXT ("warn"), + NTXT ("parallel"), + NTXT ("query"), + NTXT ("loop"), + NTXT ("pipe"), + NTXT ("inline"), + NTXT ("memops"), + NTXT ("fe"), + NTXT ("codegen"), + NTXT ("src"), + NTXT ("asrc"), + NTXT ("nosrc"), + NTXT ("hex"), + NTXT ("nohex"), + NTXT ("threshold"), + NTXT ("cf") +}; + +static const int comp_vis[] = { + CCMV_BASIC, + CCMV_VER, + CCMV_WARN, + CCMV_PAR, + CCMV_QUERY, + CCMV_LOOP, + CCMV_PIPE, + CCMV_INLINE, + CCMV_MEMOPS, + CCMV_FE, + CCMV_CG, + COMP_SRC, + COMP_SRC_METRIC, + COMP_NOSRC, + COMP_HEX, + COMP_NOHEX, + COMP_THRESHOLD, + COMP_CMPLINE +}; + +const int comp_size = sizeof (comp_cmd) / sizeof (char *); + +// Commands for timeline +typedef enum +{ + TLCMD_INVALID, + TLCMD_ENTITY_MODE, + TLCMD_ALIGN, + TLCMD_DEPTH +} TLModeSubcommand; + +typedef struct +{ + const char * cmdText; + TLModeSubcommand cmdType; + int cmdId; +} TLModeCmd; +static const TLModeCmd tlmode_cmd[] = { + // MODE commands + {NTXT ("lwp"), TLCMD_ENTITY_MODE, PROP_LWPID}, + {NTXT ("thread"), TLCMD_ENTITY_MODE, PROP_THRID}, + {NTXT ("cpu"), TLCMD_ENTITY_MODE, PROP_CPUID}, + {NTXT ("experiment"), TLCMD_ENTITY_MODE, PROP_EXPID}, + // ALIGN commands + {NTXT ("root"), TLCMD_ALIGN, TLSTACK_ALIGN_ROOT}, + {NTXT ("leaf"), TLCMD_ALIGN, TLSTACK_ALIGN_LEAF}, + // DEPTH commands + {NTXT ("depth"), TLCMD_DEPTH, 0 /* don't care */} +}; + +static const int tlmode_size = sizeof (tlmode_cmd) / sizeof (TLModeCmd); + +// Constructor + +Settings::Settings (Application *_app) +{ + // Remember the application + app = _app; + + // Clear all default strings + str_vmode = NULL; + str_en_desc = NULL; + str_datamode = NULL; + str_scompcom = NULL; + str_sthresh = NULL; + str_dcompcom = NULL; + str_dthresh = NULL; + str_dmetrics = NULL; + str_dsort = NULL; + str_tlmode = NULL; + str_tldata = NULL; + str_tabs = NULL; + str_rtabs = NULL; + str_search_path = NULL; + str_name_format = NULL; + str_limit = NULL; + str_printmode = NULL; + str_compare = NULL; + preload_libdirs = NULL; + pathmaps = new Vector<pathmap_t*>; + lo_expands = new Vector<lo_expand_t*>; + lo_expand_default = LIBEX_SHOW; + is_loexpand_default = true; + tabs_processed = false; + + // set default-default values + name_format = Histable::NA; + view_mode = VMODE_USER; + en_desc = false; + en_desc_cmp = NULL; + en_desc_usr = NULL; + src_compcom = 2147483647; + dis_compcom = 2147483647; +#define DEFAULT_SRC_DIS_THRESHOLD 75 + threshold_src = DEFAULT_SRC_DIS_THRESHOLD; + threshold_dis = DEFAULT_SRC_DIS_THRESHOLD; + src_visible = true; + srcmetric_visible = false; + hex_visible = false; + cmpline_visible = true; + funcline_visible = true; + tldata = NULL; + tlmode = 0; + stack_align = 0; + stack_depth = 0; + limit = 0; + // print mode is initialized after the .rc files are read + print_delim = ','; + compare_mode = CMP_DISABLE; + machinemodel = NULL; + ignore_no_xhwcprof = false; + ignore_fs_warn = false; + + // construct the master list of tabs + buildMasterTabList (); + + indx_tab_state = new Vector<bool>; + indx_tab_order = new Vector<int>; + mem_tab_state = new Vector<bool>; + mem_tab_order = new Vector<int>; + + // note that the .rc files are not read here, but later +} + +// Constructor for duplicating an existing Settings class + +Settings::Settings (Settings * _settings) +{ + int index; + app = _settings->app; + + // Copy all default strings + str_vmode = dbe_strdup (_settings->str_vmode); + str_en_desc = dbe_strdup (_settings->str_en_desc); + str_datamode = dbe_strdup (_settings->str_datamode); + str_scompcom = dbe_strdup (_settings->str_scompcom); + str_sthresh = dbe_strdup (_settings->str_sthresh); + str_dcompcom = dbe_strdup (_settings->str_dcompcom); + str_dthresh = dbe_strdup (_settings->str_dthresh); + str_dmetrics = dbe_strdup (_settings->str_dmetrics); + str_dsort = dbe_strdup (_settings->str_dsort); + str_tlmode = dbe_strdup (_settings->str_tlmode); + str_tldata = dbe_strdup (_settings->str_tldata); + str_tabs = dbe_strdup (_settings->str_tabs); + str_rtabs = dbe_strdup (_settings->str_rtabs); + str_search_path = dbe_strdup (_settings->str_search_path); + str_name_format = dbe_strdup (_settings->str_name_format); + str_limit = dbe_strdup (_settings->str_limit); + str_printmode = dbe_strdup (_settings->str_printmode); + str_compare = dbe_strdup (_settings->str_compare); + preload_libdirs = dbe_strdup (_settings->preload_libdirs); + + // replicate the pathmap vector + pathmap_t *thismap; + pathmap_t *newmap; + pathmaps = new Vector<pathmap_t*>; + + Vec_loop (pathmap_t*, _settings->pathmaps, index, thismap) + { + newmap = new pathmap_t; + newmap->old_prefix = dbe_strdup (thismap->old_prefix); + newmap->new_prefix = dbe_strdup (thismap->new_prefix); + pathmaps->append (newmap); + } + + // replicate the lo_expand vector and default + lo_expand_t *this_lo_ex; + lo_expand_t *new_lo_ex; + lo_expand_default = _settings->lo_expand_default; + is_loexpand_default = _settings->is_loexpand_default; + lo_expands = new Vector<lo_expand_t*>; + + Vec_loop (lo_expand_t*, _settings->lo_expands, index, this_lo_ex) + { + new_lo_ex = new lo_expand_t; + new_lo_ex->libname = dbe_strdup (this_lo_ex->libname); + new_lo_ex->expand = this_lo_ex->expand; + lo_expands->append (new_lo_ex); + } + tabs_processed = _settings->tabs_processed; + + // Copy the various values from the _settings instance + name_format = _settings->name_format; + view_mode = _settings->view_mode; + en_desc = false; + en_desc_cmp = NULL; + en_desc_usr = NULL; + if (_settings->en_desc_usr) + set_en_desc (_settings->en_desc_usr, true); + src_compcom = _settings->src_compcom; + dis_compcom = _settings->dis_compcom; + threshold_src = _settings->threshold_src; + threshold_dis = _settings->threshold_dis; + src_visible = _settings->src_visible; + srcmetric_visible = _settings->srcmetric_visible; + hex_visible = _settings->hex_visible; + cmpline_visible = _settings->cmpline_visible; + funcline_visible = _settings->funcline_visible; + tldata = dbe_strdup (_settings->tldata); + tlmode = _settings->tlmode; + stack_align = _settings->stack_align; + stack_depth = _settings->stack_depth; + limit = _settings->limit; + print_mode = _settings->print_mode; + print_delim = _settings->print_delim; + compare_mode = _settings->compare_mode; + machinemodel = dbe_strdup (_settings->machinemodel); + ignore_no_xhwcprof = _settings->ignore_no_xhwcprof; + ignore_fs_warn = _settings->ignore_fs_warn; + + // copy the tab list, too + tab_list = new Vector<DispTab*>; + DispTab *dsptab; + + Vec_loop (DispTab*, _settings->tab_list, index, dsptab) + { + DispTab *ntab; + ntab = new DispTab (dsptab->type, dsptab->order, dsptab->visible, dsptab->cmdtoken); + ntab->setAvailability (dsptab->available); + tab_list->append (ntab); + } + + // construct the master list of memory tabs & copy order + index = _settings->mem_tab_state->size (); + mem_tab_state = new Vector<bool>(index); + mem_tab_order = new Vector<int>(index); + for (int i = 0; i < index; i++) + { + mem_tab_state->append (false); + mem_tab_order->append (_settings->mem_tab_order->fetch (i)); + } + + // construct the master list of index tabs & copy order + index = _settings->indx_tab_state->size (); + indx_tab_state = new Vector<bool>(index); + indx_tab_order = new Vector<int>(index); + for (int i = 0; i < index; i++) + indx_tab_order->append (_settings->indx_tab_order->fetch (i)); + set_IndxTabState (_settings->indx_tab_state); +} + +Settings::~Settings () +{ + for (int i = 0; i < pathmaps->size (); ++i) + { + pathmap_t *pmap = pathmaps->fetch (i); + free (pmap->old_prefix); + free (pmap->new_prefix); + delete pmap; + } + delete pathmaps; + + for (int i = 0; i < lo_expands->size (); ++i) + { + lo_expand_t *lo_ex = lo_expands->fetch (i); + free (lo_ex->libname); + delete lo_ex; + } + delete lo_expands; + + tab_list->destroy (); + delete tab_list; + delete indx_tab_state; + delete indx_tab_order; + delete mem_tab_state; + delete mem_tab_order; + + free (str_vmode); + free (str_en_desc); + free (str_datamode); + free (str_scompcom); + free (str_sthresh); + free (str_dcompcom); + free (str_dthresh); + free (str_dmetrics); + free (str_dsort); + free (str_tlmode); + free (str_tldata); + free (str_tabs); + free (str_rtabs); + free (str_search_path); + free (str_name_format); + free (str_limit); + free (str_compare); + free (str_printmode); + free (preload_libdirs); + free (tldata); + free (en_desc_usr); + if (en_desc_cmp) + { + regfree (en_desc_cmp); + delete en_desc_cmp; + } +} + +/** + * Read .er.rc file from the specified location + * @param path + * @return + */ +char * +Settings::read_rc (char *path) +{ + StringBuilder sb; + Emsgqueue *commentq = new Emsgqueue (NTXT ("setting_commentq")); + + // Check file name + if (NULL == path) + return dbe_strdup (GTXT ("Error: empty file name")); + bool override = true; + set_rc (path, true, commentq, override); + Emsg *msg = commentq->fetch (); + while (msg != NULL) + { + char *str = msg->get_msg (); + sb.append (str); + msg = msg->next; + } + return sb.toString (); +} + +void +Settings::read_rc (bool ipc_or_rdt_mode) +{ + bool override = false; + + // Read file from the current working directory + char *rc_path = realpath (NTXT ("./.gprofng.rc"), NULL); + if (rc_path) + set_rc (rc_path, true, app->get_comments_queue (), override, ipc_or_rdt_mode); + + // Read file from the user's home directory + char *home = getenv (NTXT ("HOME")); + if (home) + { + char *strbuf = dbe_sprintf (NTXT ("%s/.gprofng.rc"), home); + char *home_rc_path = realpath (strbuf, NULL); + if (home_rc_path) + { + if (rc_path == NULL || strcmp (rc_path, home_rc_path) != 0) + set_rc (home_rc_path, true, app->get_comments_queue (), override, ipc_or_rdt_mode); + free (home_rc_path); + } + free (strbuf); + } + free (rc_path); + + // Read system-wide file + rc_path = dbe_sprintf (NTXT ("%s/../etc/gprofng.rc"), app->get_run_dir ()); + if (access (rc_path, R_OK | F_OK) != 0) + { + StringBuilder sb; + sb.sprintf (GTXT ("Warning: Default gprofng.rc file (%s) missing; configuration error "), rc_path); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + app->get_comments_queue ()->append (m); + } + else + set_rc (rc_path, false, app->get_comments_queue (), override); + free (rc_path); + is_loexpand_default = true; + if (str_printmode == NULL) + { + // only if there's none set + print_mode = PM_TEXT; + str_printmode = dbe_strdup (NTXT ("text")); + } +} + + +// Handle various settings from reading the name .rc file +// This function is called for each .rc file read, and, for +// some settings, it accumulates the strings from the files. +// For others, it accepts the first appearance for a setting in a +// .rc file, and ignores subsequent appearances from other files. +// Error messages are appended to the Emsgqueue specified by the caller + +#define MAXARGS 20 + +void +Settings::set_rc (const char *path, bool msg, Emsgqueue *commentq, + bool override, bool ipc_or_rdt_mode) +{ + CmdType cmd_type; + int arg_count, cparam; + char *cmd, *end_cmd, *strbuf; + char *arglist[MAXARGS]; + StringBuilder sb; + + FILE *fptr = fopen (path, NTXT ("r")); + if (fptr == NULL) + return; + + if (msg) + { + sb.sprintf (GTXT ("Processed %s for default settings"), path); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + } + int line_no = 0; + end_cmd = NULL; + while (!feof (fptr)) + { + char *script = read_line (fptr); + if (script == NULL) + continue; + line_no++; + strtok (script, NTXT ("\n")); + + // extract the command + cmd = strtok (script, NTXT (" \t")); + if (cmd == NULL || *cmd == '#' || *cmd == '\n') + { + free (script); + continue; + } + char *remainder = strtok (NULL, NTXT ("\n")); + // now extract the arguments + int nargs = 0; + for (;;) + { + if (nargs >= MAXARGS) + { + if (!msg) + { + msg = true; // suppress repeats of header + Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); + commentq->append (m); + } + sb.sprintf (GTXT ("Warning: more than %d arguments to %s command, line %d\n"), + MAXARGS, cmd, line_no); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + break; + } + + char *nextarg = strtok (remainder, NTXT ("\n")); + if (nextarg == NULL || *nextarg == '#') + break; + arglist[nargs++] = parse_qstring (nextarg, &end_cmd); + remainder = end_cmd; + if (remainder == NULL) + break; + // skip any blanks or tabs to get to next argument + while (*remainder == ' ' || *remainder == '\t') + remainder++; + } + cmd_type = Command::get_command (cmd, arg_count, cparam); + // check for extra arguments + if ((cmd_type != UNKNOWN_CMD && cmd_type != INDXOBJDEF) && (nargs > arg_count)) + { + if (!msg) + { + msg = true; // suppress repeats of header + Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); + commentq->append (m); + } + sb.sprintf (GTXT ("Warning: extra arguments to %s command, line %d\n"), cmd, line_no); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + } + if (nargs < arg_count) + { + if (!msg) + { + msg = true; // suppress repeats of header + Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); + commentq->append (m); + } + sb.sprintf (GTXT ("Error: missing arguments to %s command, line %d\n"), + cmd, line_no); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + + // ignore this command + free (script); + continue; + } + if (ipc_or_rdt_mode && (cmd_type != ADDPATH) && (cmd_type != PATHMAP)) + { + free (script); + continue; + } + switch (cmd_type) + { + case SCOMPCOM: + if (!str_scompcom || override) + { + str_scompcom = dbe_strdup (arglist[0]); + proc_compcom (arglist[0], true, true); + } + break; + case STHRESH: + if (!str_sthresh || override) + { + str_sthresh = dbe_strdup (arglist[0]); + proc_thresh (arglist[0], true, true); + break; + } + break; + case DCOMPCOM: + if (!str_dcompcom || override) + { + str_dcompcom = dbe_strdup (arglist[0]); + proc_compcom (arglist[0], false, true); + } + break; + case COMPCOM: + // process as if it were for both source and disassembly + // note that if it is set, subsequent SCOMPCOM and DCOMPCOM + // will be ignored + if (!str_scompcom || override) + { + str_scompcom = dbe_strdup (arglist[0]); + proc_compcom (arglist[0], true, true); + } + if (!str_dcompcom || override) + { + str_dcompcom = dbe_strdup (arglist[0]); + proc_compcom (arglist[0], false, true); + } + break; + case DTHRESH: + if (!str_dthresh || override) + { + str_dthresh = dbe_strdup (arglist[0]); + proc_thresh (arglist[0], false, true); + } + break; + case DMETRICS: + // append new settings to old, if necessary + if (str_dmetrics) + { + char *name = strstr (str_dmetrics, ":name"); + if (name == NULL) + strbuf = dbe_sprintf ("%s:%s", str_dmetrics, arglist[0]); + else + { + char * next = strstr (name + 1, ":"); + if (next == NULL) + { + name[0] = '\0'; + strbuf = dbe_sprintf ("%s:%s:name", str_dmetrics, arglist[0]); + } + else + strbuf = dbe_sprintf ("%s:%s", str_dmetrics, arglist[0]); + } + free (str_dmetrics); + str_dmetrics = strbuf; + } + else + str_dmetrics = dbe_strdup (arglist[0]); + break; + case DSORT: + // append new settings to old, if necessary + if (str_dsort) + { + strbuf = dbe_sprintf (NTXT ("%s:%s"), str_dsort, arglist[0]); + free (str_dsort); + str_dsort = strbuf; + } + else + str_dsort = dbe_strdup (arglist[0]); + break; + case TLMODE: + if (!str_tlmode || override) + { + str_tlmode = dbe_strdup (arglist[0]); + proc_tlmode (arglist[0], true); + } + break; + case TLDATA: + if (!str_tldata || override) + { + str_tldata = dbe_strdup (arglist[0]); + proc_tldata (arglist[0], true); + } + break; + case TABS: + if (!str_tabs || override) + // the string is processed later, after all .rc files are read + str_tabs = dbe_strdup (arglist[0]); + break; + case RTABS: + if (!str_rtabs || override) + // the string is processed later, after all .rc files are read + str_rtabs = dbe_strdup (arglist[0]); + break; + case ADDPATH: + if (str_search_path) + { + strbuf = dbe_sprintf (NTXT ("%s:%s"), str_search_path, arglist[0]); + free (str_search_path); + str_search_path = strbuf; + } + else + str_search_path = dbe_strdup (arglist[0]); + break; + case PATHMAP: + { + char *err = add_pathmap (pathmaps, arglist[0], arglist[1]); + free (err); // XXX error is not reported + break; + } + case LIBDIRS: + if (preload_libdirs == NULL) + preload_libdirs = dbe_strdup (arglist[0]); + break; + case NAMEFMT: + if (name_format == Histable::NA) + set_name_format (arglist[0]); + break; + case VIEWMODE: + if (!str_vmode || override) + { + str_vmode = dbe_strdup (arglist[0]); + set_view_mode (arglist[0], true); + } + break; + case EN_DESC: + if (!str_en_desc || override) + { + str_en_desc = dbe_strdup (arglist[0]); + set_en_desc (arglist[0], true); + } + break; + case LIMIT: + if (!str_limit || override) + { + str_limit = dbe_strdup (arglist[0]); + set_limit (arglist[0], true); + } + break; + case PRINTMODE: + if (!str_printmode || override) + set_printmode (arglist[0]); + break; + case COMPARE: + if (!str_compare || override) + { + char *s = arglist[0]; + if (s) + str_compare = dbe_strdup (s); + else + s = NTXT (""); + if (strcasecmp (s, NTXT ("OFF")) == 0 + || strcmp (s, NTXT ("0")) == 0) + set_compare_mode (CMP_DISABLE); + else if (strcasecmp (s, NTXT ("ON")) == 0 + || strcmp (s, NTXT ("1")) == 0) + set_compare_mode (CMP_ENABLE); + else if (strcasecmp (s, NTXT ("DELTA")) == 0) + set_compare_mode (CMP_DELTA); + else if (strcasecmp (s, NTXT ("RATIO")) == 0) + set_compare_mode (CMP_RATIO); + else + { + sb.sprintf (GTXT (" .er.rc:%d The argument of 'compare' should be 'on', 'off', 'delta', or 'ratio'"), + (int) line_no); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + } + } + break; + + case INDXOBJDEF: + { + char *ret = dbeSession->indxobj_define (arglist[0], NULL, arglist[1], (nargs >= 3) ? PTXT (arglist[2]) : NULL, (nargs >= 4) ? PTXT (arglist[3]) : NULL); + if (ret != NULL) + { + sb.sprintf (GTXT (" %s: line %d `%s %s %s'\n"), + ret, line_no, cmd, arglist[0], arglist[1]); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + } + break; + } +#ifdef sparc + //XXX: should be conditional on the experiment ARCH, not dbe ARCH + case IGNORE_NO_XHWCPROF: + // ignore absence of -xhwcprof info for dataspace profiling + set_ignore_no_xhwcprof (true); + break; +#endif // sparc + case IGNORE_FS_WARN: + // ignore file system warning in experiments + set_ignore_fs_warn (true); + break; + case OBJECT_SHOW: + // Add the named libraries to the lib_expands array + set_libexpand (arglist[0], LIBEX_SHOW, true); + break; + case OBJECT_HIDE: + // Add the named libraries to the lib_expands array + set_libexpand (arglist[0], LIBEX_HIDE, true); + break; + case OBJECT_API: + // Add the named libraries to the lib_expands array + set_libexpand (arglist[0], LIBEX_API, true); + break; + case COMMENT: + // ignore the line + break; + default: + { + // unexpected command in an rc file + if (!msg) + { + // if quiet, can remain so no longer + msg = true; + Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); + commentq->append (m); + } + sb.sprintf (GTXT (" Unrecognized .gprofng.rc command on line %d: `%.64s'"), + line_no, cmd); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + break; + } + } + free (script); + } + fclose (fptr); +} + +Cmd_status +Settings::set_view_mode (char *arg, bool rc) +{ + if (!strcasecmp (arg, NTXT ("user"))) + view_mode = VMODE_USER; + else if (!strcasecmp (arg, NTXT ("expert"))) + view_mode = VMODE_EXPERT; + else if (!strcasecmp (arg, NTXT ("machine"))) + view_mode = VMODE_MACHINE; + else if (!rc) + return CMD_BAD_ARG; + return CMD_OK; +} + +Cmd_status +Settings::set_en_desc (char *arg, bool rc) +{ + regex_t *regex_desc = NULL; + + // cases below should be similar to Coll_Ctrl::set_follow_mode() cases + if (!strcasecmp (arg, NTXT ("on"))) + en_desc = true; + else if (!strcasecmp (arg, NTXT ("off"))) + en_desc = false; + else if (arg[0] == '=' && arg[1] != 0) + { + // user has specified a string matching specification + int ercode; + { // compile regex_desc + char * str = dbe_sprintf (NTXT ("^%s$"), arg + 1); + regex_desc = new regex_t; + memset (regex_desc, 0, sizeof (regex_t)); + ercode = regcomp (regex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + } + if (ercode) + { + // syntax error in parsing string + delete regex_desc; + if (!rc) + return CMD_BAD_ARG; + return CMD_OK; + } + en_desc = true; + } + else + { + if (!rc) + return CMD_BAD_ARG; + return CMD_OK; + } + free (en_desc_usr); + en_desc_usr = dbe_strdup (arg); + if (en_desc_cmp) + { + regfree (en_desc_cmp); + delete en_desc_cmp; + } + en_desc_cmp = regex_desc; + return CMD_OK; +} + +// See if a descendant matches either the lineage or the executable name +bool +Settings::check_en_desc (const char *lineage, const char *targname) +{ + bool rc; + if (en_desc_cmp == NULL) + return en_desc; // no specification was set, use the binary on/off value + if (lineage == NULL) // user doesn't care about specification + return en_desc; // use the binary on/off specification + if (!regexec (en_desc_cmp, lineage, 0, NULL, 0)) + rc = true; // this one matches user specification + else if (targname == NULL) + rc = false; //a NULL name does not match any expression + else if (!regexec (en_desc_cmp, targname, 0, NULL, 0)) + rc = true; // this one matches the executable name + else + rc = false; + return rc; +} + +char * +Settings::set_limit (char *arg, bool) +{ + limit = (int) strtol (arg, (char **) NULL, 10); + return NULL; +} + +char * +Settings::set_printmode (char *arg) +{ + if (arg == NULL) + return dbe_sprintf (GTXT ("The argument to '%s' must be '%s' or '%s' or a single-character"), + NTXT ("printmode"), NTXT ("text"), NTXT ("html")); + if (strlen (arg) == 1) + { + print_mode = PM_DELIM_SEP_LIST; + print_delim = arg[0]; + } + else if (!strcasecmp (arg, NTXT ("text"))) + print_mode = PM_TEXT; + else if (!strcasecmp (arg, NTXT ("html"))) + print_mode = PM_HTML; + else + return dbe_sprintf (GTXT ("The argument to '%s' must be '%s' or '%s' or a single-character"), + NTXT ("printmode"), NTXT ("text"), NTXT ("html")); + free (str_printmode); + str_printmode = dbe_strdup (arg); + return NULL; +} + +Cmd_status +Settings::proc_compcom (const char *cmd, bool isSrc, bool rc) +{ + int ck_compcom_bits, ck_threshold; + bool ck_hex_visible = false; + bool ck_src_visible = false; + bool ck_srcmetric_visible = false; + bool got_compcom_bits, got_threshold, got_src_visible, got_srcmetric_visible; + bool got_hex_visible, got; + int len, i; + char *mcmd, *param; + int flag, value = 0; + Cmd_status status; + char buf[BUFSIZ], *list; + + if (cmd == NULL) + return CMD_BAD; + ck_compcom_bits = 0; + ck_threshold = 0; + got_compcom_bits = got_threshold = got_src_visible = false; + got_srcmetric_visible = got_hex_visible = false; + snprintf (buf, sizeof (buf), NTXT ("%s"), cmd); + list = buf; + while ((mcmd = strtok (list, NTXT (":"))) != NULL) + { + list = NULL; + // if "all" or "none" + if (!strcasecmp (mcmd, Command::ALL_CMD)) + { + got_compcom_bits = true; + ck_compcom_bits = CCMV_ALL; + continue; + } + else if (!strcasecmp (mcmd, Command::NONE_CMD)) + { + got_compcom_bits = true; + ck_compcom_bits = 0; + continue; + } + + // Find parameter after '=' + param = strchr (mcmd, '='); + if (param) + { + *param = '\0'; + param++; + } + status = CMD_OK; + got = false; + flag = 0; + len = (int) strlen (mcmd); + for (i = 0; status == CMD_OK && i < comp_size; i++) + if (!strncasecmp (mcmd, comp_cmd[i], len)) + { + if (got) // Ambiguous comp_com command + status = CMD_AMBIGUOUS; + else + { + got = true; + flag = comp_vis[i]; + // Check argument + if (flag == COMP_THRESHOLD) + { + if (param == NULL) + status = CMD_BAD_ARG; + else + { + value = (int) strtol (param, ¶m, 10); + if (value < 0 || value > 100) + status = CMD_OUTRANGE; + } + } + else if (param != NULL) + status = CMD_BAD_ARG; + } + } + + // Not valid comp_com command + if (!got) + status = CMD_INVALID; + if (status != CMD_OK) + { + if (!rc) + return status; + continue; + } + + // Set bits + switch (flag) + { + case COMP_CMPLINE: + cmpline_visible = true; + break; + case COMP_FUNCLINE: + funcline_visible = true; + break; + case COMP_THRESHOLD: + got_threshold = true; + ck_threshold = value; + break; + case COMP_SRC: + got_src_visible = true; + ck_src_visible = true; + break; + case COMP_SRC_METRIC: + got_srcmetric_visible = true; + ck_srcmetric_visible = true; + got_src_visible = true; + ck_src_visible = true; + break; + case COMP_NOSRC: + got_src_visible = true; + ck_src_visible = false; + break; + case COMP_HEX: + got_hex_visible = true; + ck_hex_visible = true; + break; + case COMP_NOHEX: + got_hex_visible = true; + ck_hex_visible = false; + break; + case CCMV_BASIC: + got_compcom_bits = true; + ck_compcom_bits = CCMV_BASIC; + break; + default: + got_compcom_bits = true; + ck_compcom_bits |= flag; + } + } + + // No error, update + if (got_compcom_bits) + { + if (isSrc) + src_compcom = ck_compcom_bits; + else + dis_compcom = ck_compcom_bits; + } + if (got_threshold) + { + if (isSrc) + threshold_src = ck_threshold; + else + threshold_dis = ck_threshold; + } + if (got_src_visible) + src_visible = ck_src_visible; + if (got_srcmetric_visible) + srcmetric_visible = ck_srcmetric_visible; + if (got_hex_visible) + hex_visible = ck_hex_visible; + return CMD_OK; +} + +// Process a threshold setting +Cmd_status +Settings::proc_thresh (char *cmd, bool isSrc, bool rc) +{ + int value; + if (cmd == NULL) + value = DEFAULT_SRC_DIS_THRESHOLD; // the default + else + value = (int) strtol (cmd, &cmd, 10); + if (value < 0 || value > 100) + { + if (!rc) + return CMD_OUTRANGE; + value = DEFAULT_SRC_DIS_THRESHOLD; + } + if (isSrc) + threshold_src = value; + else + threshold_dis = value; + return CMD_OK; +} + +// return any error string from processing visibility settings +char * +Settings::get_compcom_errstr (Cmd_status status, const char *cmd) +{ + int i; + StringBuilder sb; + switch (status) + { + case CMD_BAD: + sb.append (GTXT ("No commentary classes has been specified.")); + break; + case CMD_AMBIGUOUS: + sb.append (GTXT ("Ambiguous commentary classes: ")); + break; + case CMD_BAD_ARG: + sb.append (GTXT ("Invalid argument for commentary classes: ")); + break; + case CMD_OUTRANGE: + sb.append (GTXT ("Out of range commentary classes argument: ")); + break; + case CMD_INVALID: + sb.append (GTXT ("Invalid commentary classes: ")); + break; + case CMD_OK: + break; + } + if (cmd) + sb.append (cmd); + sb.append (GTXT ("\nAvailable commentary classes: ")); + for (i = 0; i < comp_size; i++) + { + sb.append (comp_cmd[i]); + if (i == comp_size - 1) + sb.append (NTXT ("=#\n")); + else + sb.append (NTXT (":")); + } + return sb.toString (); +} + +// Process a timeline-mode setting +Cmd_status +Settings::proc_tlmode (char *cmd, bool rc) +{ + bool got_tlmode, got_stack_align, got_stack_depth, got; + int ck_tlmode = 0, ck_stack_align = 0, ck_stack_depth = 0; + int len, i; + char *mcmd, *param; + int cmd_id, value = 0; + TLModeSubcommand cmd_type; + Cmd_status status; + char buf[BUFSIZ], *list; + if (cmd == NULL) + return CMD_BAD; + got_tlmode = got_stack_align = got_stack_depth = false; + snprintf (buf, sizeof (buf), NTXT ("%s"), cmd); + list = buf; + while ((mcmd = strtok (list, NTXT (":"))) != NULL) + { + list = NULL; + + // Find parameter after '=' + param = strchr (mcmd, '='); + if (param) + { + *param = '\0'; + param++; + } + status = CMD_OK; + got = false; + cmd_id = 0; + cmd_type = TLCMD_INVALID; + len = (int) strlen (mcmd); + for (i = 0; status == CMD_OK && i < tlmode_size; i++) + { + if (!strncasecmp (mcmd, tlmode_cmd[i].cmdText, len)) + { + if (got) // Ambiguous timeline mode + status = CMD_AMBIGUOUS; + else + { + got = true; + cmd_type = tlmode_cmd[i].cmdType; + cmd_id = tlmode_cmd[i].cmdId; + + // Check argument + if (cmd_type == TLCMD_DEPTH) + { + if (param == NULL) + status = CMD_BAD_ARG; + else + { + value = (int) strtol (param, ¶m, 10); + if (value <= 0 || value > 256) + status = CMD_OUTRANGE; + } + } + else if (param != NULL) + status = CMD_BAD_ARG; + } + } + } + + // Not valid timeline mode + if (!got) + status = CMD_INVALID; + if (status != CMD_OK) + { + if (!rc) + return status; + continue; + } + + // Set bits + switch (cmd_type) + { + case TLCMD_ENTITY_MODE: + got_tlmode = true; + ck_tlmode = cmd_id; + break; + case TLCMD_ALIGN: + got_stack_align = true; + ck_stack_align = cmd_id; + break; + case TLCMD_DEPTH: + got_stack_depth = true; + ck_stack_depth = value; + break; + default: + break; + } + } + + // No error, update + if (got_tlmode) + tlmode = ck_tlmode; + if (got_stack_align) + stack_align = ck_stack_align; + if (got_stack_depth) + stack_depth = ck_stack_depth; + return CMD_OK; +} + +// Process timeline data specification +Cmd_status +Settings::proc_tldata (const char *cmd, bool /* if true, ignore any error */) +{ + free (tldata); + tldata = dbe_strdup (cmd); // let GUI parse it + return CMD_OK; +} + +void +Settings::set_tldata (const char* _tldata_str) +{ + free (tldata); + tldata = dbe_strdup (_tldata_str); +} + +char* +Settings::get_tldata () +{ + return dbe_strdup (tldata); +} + +Cmd_status +Settings::set_name_format (char *arg) +{ + char *colon = strchr (arg, ':'); + size_t arg_len = (colon) ? (colon - arg) : strlen (arg); + Histable::NameFormat fname_fmt = Histable::NA; + if (!strncasecmp (arg, NTXT ("long"), arg_len)) + fname_fmt = Histable::LONG; + else if (!strncasecmp (arg, NTXT ("short"), arg_len)) + fname_fmt = Histable::SHORT; + else if (!strncasecmp (arg, NTXT ("mangled"), arg_len)) + fname_fmt = Histable::MANGLED; + else + return CMD_BAD_ARG; + + bool soname_fmt = false; + if (colon && (colon + 1)) + { + colon++; + if (!strcasecmp (colon, NTXT ("soname"))) + soname_fmt = true; + else if (!strcasecmp (colon, NTXT ("nosoname"))) + soname_fmt = false; + else + return CMD_BAD_ARG; + } + name_format = Histable::make_fmt (fname_fmt, soname_fmt); + return CMD_OK; +} + +void +Settings::buildMasterTabList () +{ + tab_list = new Vector<DispTab*>; + int i = -1; + + // Add tabs for all the known reports + tab_list->append (new DispTab (DSP_DEADLOCKS, i, false, DEADLOCK_EVNTS)); + tab_list->append (new DispTab (DSP_FUNCTION, i, false, FUNCS)); + tab_list->append (new DispTab (DSP_TIMELINE, i, false, TIMELINE)); + tab_list->append (new DispTab (DSP_CALLTREE, i, false, CALLTREE)); + tab_list->append (new DispTab (DSP_CALLFLAME, i, false, CALLFLAME)); + tab_list->append (new DispTab (DSP_DUALSOURCE, i, false, DUALSOURCE)); + tab_list->append (new DispTab (DSP_SOURCE_DISASM, i, false, SOURCEDISAM)); + tab_list->append (new DispTab (DSP_SOURCE, i, false, SOURCE)); + tab_list->append (new DispTab (DSP_LINE, i, false, HOTLINES)); + tab_list->append (new DispTab (DSP_DISASM, i, false, DISASM)); + tab_list->append (new DispTab (DSP_PC, i, false, HOTPCS)); + tab_list->append (new DispTab (DSP_LEAKLIST, i, false, LEAKS)); + tab_list->append (new DispTab (DSP_IOACTIVITY, i, false, IOACTIVITY)); + tab_list->append (new DispTab (DSP_HEAPCALLSTACK, i, false, HEAP)); + tab_list->append (new DispTab (DSP_IFREQ, i, false, IFREQ)); + tab_list->append (new DispTab (DSP_CALLER, i, false, GPROF)); + tab_list->append (new DispTab (DSP_STATIS, i, false, STATISTICS)); + tab_list->append (new DispTab (DSP_EXP, i, false, HEADER)); +} + +// Update tablist based on data availability +void +Settings::updateTabAvailability () +{ + int index; + DispTab *dsptab; + + Vec_loop (DispTab*, tab_list, index, dsptab) + { + if (dsptab->type == DSP_DATAOBJ) + dsptab->setAvailability (dbeSession->is_datamode_available ()); + else if (dsptab->type == DSP_DLAYOUT) + dsptab->setAvailability (dbeSession->is_datamode_available ()); + else if (dsptab->type == DSP_LEAKLIST) + dsptab->setAvailability (false); + else if (dsptab->type == DSP_IOACTIVITY) + dsptab->setAvailability (dbeSession->is_iodata_available ()); + else if (dsptab->type == DSP_HEAPCALLSTACK) + dsptab->setAvailability (dbeSession->is_heapdata_available ()); + else if (dsptab->type == DSP_TIMELINE) + dsptab->setAvailability (dbeSession->is_timeline_available ()); + else if (dsptab->type == DSP_IFREQ) + dsptab->setAvailability (dbeSession->is_ifreq_available ()); + else if (dsptab->type == DSP_RACES) + dsptab->setAvailability (dbeSession->is_racelist_available ()); + else if (dsptab->type == DSP_DEADLOCKS) + dsptab->setAvailability (dbeSession->is_deadlocklist_available ()); + else if (dsptab->type == DSP_DUALSOURCE) + dsptab->setAvailability (dbeSession->is_racelist_available () + || dbeSession->is_deadlocklist_available ()); + } +} + +// Process a tab setting +Cmd_status +Settings::proc_tabs (bool _rdtMode) +{ + int arg_cnt, cparam; + int count = 0; + int index; + DispTab *dsptab; + char *cmd; + if (tabs_processed == true) + return CMD_OK; + tabs_processed = true; + if (_rdtMode == true) + { + if (str_rtabs == NULL) + str_rtabs = strdup ("header"); + cmd = str_rtabs; + } + else + { + if (str_tabs == NULL) + str_tabs = strdup ("header"); + cmd = str_tabs; + } + if (strcmp (cmd, NTXT ("none")) == 0) + return CMD_OK; + Vector <char *> *tokens = split_str (cmd, ':'); + for (long j = 0, sz = VecSize (tokens); j < sz; j++) + { + char *tabname = tokens->get (j); + // search for this tab command token + CmdType c = Command::get_command (tabname, arg_cnt, cparam); + if (c == INDXOBJ) + { + // set the bit for this subtype + indx_tab_state->store (cparam, true); + indx_tab_order->store (cparam, count++); + } + else + { + // search for this tab type in the regular tabs + Vec_loop (DispTab*, tab_list, index, dsptab) + { + if (dsptab->cmdtoken == c) + { + dsptab->visible = true; + dsptab->order = count++; + break; + } + } + } + free (tabname); + } + delete tokens; + return CMD_OK; +} + +void +Settings::set_MemTabState (Vector<bool>*selected) +{ + if (selected->size () == 0) + return; + for (int j = 0; j < mem_tab_state->size (); j++) + mem_tab_state->store (j, selected->fetch (j)); +} + +// define a new memory object type + +void +Settings::mobj_define (MemObjType_t */* mobj */, bool state) +{ + if (mem_tab_state->size () == 0) + state = true; + mem_tab_state->append (state); + mem_tab_order->append (-1); +} + +void +Settings::set_IndxTabState (Vector<bool>*selected) +{ + for (int j = 0; j < selected->size (); j++) + indx_tab_state->store (j, selected->fetch (j)); +} + +// define a new index object type +void +Settings::indxobj_define (int type, bool state) +{ + indx_tab_state->store (type, state); + indx_tab_order->store (type, -1); +} + +void +Settings::set_pathmaps (Vector<pathmap_t*> *newPathMap) +{ + if (pathmaps) + { + pathmaps->destroy (); + delete pathmaps; + } + pathmaps = newPathMap; +} + +static char * +get_canonical_name (const char *fname) +{ + char *nm = dbe_strdup (fname); + for (size_t len = strlen (nm); (len > 0) && (nm[len - 1] == '/'); len--) + nm[len - 1] = 0; + return nm; +} + +char * +Settings::add_pathmap (Vector<pathmap_t*> *v, const char *from, const char *to) +{ + // Check for errors + if (from == NULL || to == NULL) + return dbe_strdup (GTXT ("Pathmap can have neither from nor to as NULL\n")); + if (strcmp (from, to) == 0) + return dbe_strdup (GTXT ("Pathmap from must differ from to\n")); + char *old_prefix = get_canonical_name (from); + char *new_prefix = get_canonical_name (to); + + // Check the pathmap list + for (int i = 0, sz = v->size (); i < sz; i++) + { + pathmap_t *pmp = v->get (i); + if ((strcmp (pmp->old_prefix, old_prefix) == 0) &&(strcmp (pmp->new_prefix, new_prefix) == 0)) + { + char *s = dbe_sprintf (GTXT ("Pathmap from `%s' to `%s' already exists\n"), old_prefix, new_prefix); + free (old_prefix); + free (new_prefix); + return s; + } + } + // construct a map for this pair + pathmap_t *thismap = new pathmap_t; + thismap->old_prefix = old_prefix; + thismap->new_prefix = new_prefix; + v->append (thismap); + return NULL; +} + +// Set all shared object expands back to .rc file defaults, +// as stored in the DbeSession Settings +bool +Settings::set_libdefaults () +{ + // See if this is unchanged + if (is_loexpand_default == true) + return false; // no change + + // replicate the DbeSession's lo_expand vector and default settings + lo_expand_t *this_lo_ex; + lo_expand_t *new_lo_ex; + int index; + lo_expand_default = dbeSession->get_settings ()->lo_expand_default; + lo_expands = new Vector<lo_expand_t*>; + Vec_loop (lo_expand_t*, dbeSession->get_settings ()->lo_expands, index, this_lo_ex) + { + new_lo_ex = new lo_expand_t; + new_lo_ex->libname = dbe_strdup (this_lo_ex->libname); + new_lo_ex->expand = this_lo_ex->expand; + lo_expands->append (new_lo_ex); + } + is_loexpand_default = true; + return true; +} + +bool +Settings::set_libexpand (char *cov, enum LibExpand expand, bool rc) +{ + int index; + lo_expand_t *loe; + bool change = false; + if (cov == NULL || !strcasecmp (cov, Command::ALL_CMD)) + { // set all libraries + // set the default + if (lo_expand_default != expand) + { + lo_expand_default = expand; + change = true; + is_loexpand_default = false; + } + + // and force any explicit settings to match, too + Vec_loop (lo_expand_t*, lo_expands, index, loe) + { + if (loe->expand != expand) + { + loe->expand = expand; + change = true; + is_loexpand_default = false; + } + } + + } + else + { // parsing coverage + Vector <char *> *tokens = split_str (cov, ','); + for (long j = 0, sz = VecSize (tokens); j < sz; j++) + { + char *lo_name = tokens->get (j); + char *newname = get_basename (lo_name); + bool found = false; + Vec_loop (lo_expand_t*, lo_expands, index, loe) + { + if (strcmp (loe->libname, newname) == 0) + { + if (loe->expand != expand) + { + if (rc == false) + { + loe->expand = expand; + change = true; + is_loexpand_default = false; + } + } + found = true; + break; + } + } + + if (found == false) + { + // construct a map for this pair + lo_expand_t *thisloe; + thisloe = new lo_expand_t; + thisloe->libname = dbe_strdup (newname); + thisloe->expand = expand; + change = true; + is_loexpand_default = false; + + // add it to the vector + lo_expands->append (thisloe); + } + free (lo_name); + } + delete tokens; + } + return change; +} + +enum LibExpand +Settings::get_lo_setting (char *name) +{ + int index; + lo_expand_t *loe; + char *lo_name = get_basename (name); + Vec_loop (lo_expand_t*, lo_expands, index, loe) + { + if (strcmp (loe->libname, lo_name) == 0) + return loe->expand; + } + return lo_expand_default; +} diff --git a/gprofng/src/Settings.h b/gprofng/src/Settings.h new file mode 100644 index 0000000..fc696e8 --- /dev/null +++ b/gprofng/src/Settings.h @@ -0,0 +1,425 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include <stdio.h> +#include <regex.h> + +#include "gp-defs.h" +#include "Histable.h" +#include "MemorySpace.h" +#include "Metric.h" +#include "dbe_types.h" +#include "dbe_structs.h" +#include "enums.h" +#include "vec.h" + +class Emsgqueue; +class Application; + +struct DispTab; + +// Settings object + +class Settings +{ +public: + friend class DbeView; + friend class DbeSession; + + Settings (Application *_app); + Settings (Settings *_settings); + virtual ~Settings (); + void read_rc (bool ipc_or_rdt_mode); // read all rc files + char *read_rc (char *path); // read rc file + void buildMasterTabList (); // build list of Tabs that can be invoked + void updateTabAvailability (); // update for datamode, leaklist + Cmd_status set_name_format (char *str); // from a string + + Vector<DispTab*> * + get_TabList () // Get the list of tabs for this view + { + return tab_list; + } + + Vector<bool> * + get_MemTabState () // Get the list and order of memory tabs for this view + { + return mem_tab_state; + } + + Vector<int> * + get_MemTabOrder () + { + return mem_tab_order; + } + + // Set the list of memory tabs for this view + void set_MemTabState (Vector<bool>*sel); + + // add a newly-defined memory object tab + void mobj_define (MemObjType_t *, bool state); + + // add a newly-defined index object tab + void indxobj_define (int type, bool state); + + Vector<bool> * + get_IndxTabState () // Get the list and order of index tabs for this view + { + return indx_tab_state; + } + + Vector<int> * + get_IndxTabOrder () + { + return indx_tab_order; + } + + // Set the list of index tabs for this view + void set_IndxTabState (Vector<bool>*sel); + + void + set_name_format (int fname_fmt, bool soname_fmt) + { + name_format = Histable::make_fmt (fname_fmt, soname_fmt); + } + + Histable::NameFormat + get_name_format () + { + return name_format; + } + + // public methods for setting and accessing the settings + Cmd_status set_view_mode (char *str, bool rc); // from a string + + void + set_view_mode (VMode mode) + { + view_mode = mode; + } + + VMode + get_view_mode () + { + return view_mode; + } + + // set the en_desc expression/on/off + Cmd_status set_en_desc (char *str, bool rc); // from a string + // check if the lineage or the target name matches the en_desc expression + bool check_en_desc (const char *lineage, const char *targname); + + char *set_limit (char *str, bool rc); // from a string + + char * + set_limit (int _limit) + { + limit = _limit; + return NULL; + } + + int + get_limit () + { + return limit; + } + + char *set_printmode (char *_pmode); + + // processing compiler commentary visibility bits + Cmd_status proc_compcom (const char *cmd, bool isSrc, bool rc); + + // return any error string from processing visibility settings + char *get_compcom_errstr (Cmd_status status, const char *cmd); + + // methods for setting and getting strings, and individual settings + + char * + get_str_scompcom () + { + return str_scompcom; + } + + char * + get_str_dcompcom () + { + return str_dcompcom; + } + + int + get_src_compcom () + { + return src_compcom; + } + + int + get_dis_compcom () + { + return dis_compcom; + } + + void + set_cmpline_visible (bool v) + { + cmpline_visible = v; + } + + void + set_funcline_visible (bool v) + { + funcline_visible = v; + } + + void + set_src_visible (int v) + { + src_visible = v; + } + + int + get_src_visible () + { + return src_visible; + } + + void + set_srcmetric_visible (bool v) + { + srcmetric_visible = v; + } + + bool + get_srcmetric_visible () + { + return srcmetric_visible; + } + + void + set_hex_visible (bool v) + { + hex_visible = v; + } + + bool + get_hex_visible () + { + return hex_visible; + } + + // processing and accessing the threshold settings + Cmd_status proc_thresh (char *cmd, bool isSrc, bool rc); + + int + get_thresh_src () + { + return threshold_src; + } + + int + get_thresh_dis () + { + return threshold_dis; + } + + // process a tlmode setting + Cmd_status proc_tlmode (char *cmd, bool rc); + + void + set_tlmode (int _tlmode) + { + tlmode = _tlmode; + } + + int + get_tlmode () + { + return tlmode; + } + + void + set_stack_align (int _stack_align) + { + stack_align = _stack_align; + } + + int + get_stack_align () + { + return stack_align; + } + + void + set_stack_depth (int _stack_depth) + { + stack_depth = _stack_depth; + } + + int + get_stack_depth () + { + return stack_depth; + } + + // process a tabs setting: called when the tab list is requested + Cmd_status proc_tabs (bool _rdtMode); + + Cmd_status proc_tldata (const char *cmd, bool rc); // process a tldata setting + void set_tldata (const char* tldata_string); + char *get_tldata (); + + char * + get_default_metrics () + { + return str_dmetrics; + } + + char * + get_default_sort () + { + return str_dsort; + } + + void + set_ignore_no_xhwcprof (bool v) // ignore no xhwcprof errors for dataspace + { + ignore_no_xhwcprof = v; + } + + bool + get_ignore_no_xhwcprof () + { + return ignore_no_xhwcprof; + } + + void + set_ignore_fs_warn (bool v) // ignore filesystem warnings in experiments + { + ignore_fs_warn = v; + } + + bool + get_ignore_fs_warn () + { + return ignore_fs_warn; + } + + // add a pathmap + static char *add_pathmap (Vector<pathmap_t*> *v, const char *from, const char *to); + void set_pathmaps (Vector<pathmap_t*> *newPathMap); + + // add a LoadObject expansion setting + bool set_libexpand (char *, enum LibExpand, bool); + enum LibExpand get_lo_setting (char *); + + // set LoadObject expansion defaults back to .rc specifications + bool set_libdefaults (); + + void + set_compare_mode (int mode) + { + compare_mode = mode; + } + + int + get_compare_mode () + { + return compare_mode; + } + + char * + get_machinemodel () + { + return dbe_strdup (machinemodel); + } + + char *preload_libdirs; + +protected: // data + Application *app; + + // default strings from .rc file + char *str_vmode; + char *str_en_desc; + char *str_datamode; + char *str_scompcom; + char *str_sthresh; + char *str_dcompcom; + char *str_dthresh; + char *str_dmetrics; + char *str_dsort; + char *str_tlmode; + char *str_tldata; + char *str_tabs; + char *str_rtabs; + char *str_search_path; + char *str_name_format; + char *str_limit; + char *str_printmode; + char *str_compare; + + bool tabs_processed; + + // Processed settings + bool en_desc; // controls for reading descendant processes + char * en_desc_usr; // selective descendants: user specificaton + regex_t * en_desc_cmp; // selective descendants: compiled specification + Histable::NameFormat name_format; // long/short/mangled naming for C++/Java + VMode view_mode; // Java mode + int src_compcom; // compiler commentary visibility for anno-src + int dis_compcom; // compiler commentary visibility for anno-dis + int threshold_src; // threshold for anno-src + int threshold_dis; // threshold for anno-dis + int cmpline_visible; // show compile-line flags + int funcline_visible; // show compile-line flags + int src_visible; // show source in disasm + bool srcmetric_visible; // show metrics for source in disasm + bool hex_visible; // show hex code in disasm + char* tldata; // timeline data type string + int tlmode; // timeline mode for bars + int stack_align; // timeline stack alignment + int stack_depth; // timeline stack depth + int limit; // print limit + enum PrintMode print_mode;// print mode + char print_delim; // the delimiter, if print mode = PM_DELIM_SEP_LIST + int compare_mode; // compare mode + + char *machinemodel; // machine model for Memory Objects + + bool ignore_no_xhwcprof; // ignore no -xhwcprof data in dataspace + bool ignore_fs_warn; // ignore file-system recording warning + + void set_rc (const char *path, bool msg, Emsgqueue *commentq, + bool override, bool ipc_or_rdt_mode = false); + + Vector<DispTab*> *tab_list; + Vector<pathmap_t*> *pathmaps; + Vector<lo_expand_t*> *lo_expands; + enum LibExpand lo_expand_default; + bool is_loexpand_default; + Vector<bool> *mem_tab_state; + Vector<int> *mem_tab_order; + Vector<bool> *indx_tab_state; + Vector<int> *indx_tab_order; +}; + +#endif /* ! _SETTINGS_H */ diff --git a/gprofng/src/SourceFile.cc b/gprofng/src/SourceFile.cc new file mode 100644 index 0000000..bd0a1f1 --- /dev/null +++ b/gprofng/src/SourceFile.cc @@ -0,0 +1,229 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <unistd.h> + +#include "util.h" +#include "DbeSession.h" +#include "Function.h" +#include "SourceFile.h" +#include "DefaultMap.h" +#include "DbeFile.h" +#include "LoadObject.h" +#include "Module.h" + +int SourceFile::curId = 0; + +SourceFile::SourceFile (const char *file_name) +{ + status = OS_NOTREAD; + srcLines = NULL; + srcInode = -1; + lines = NULL; + dbeLines = NULL; + functions = new DefaultMap<Function *, Function *>(); + dbeFile = new DbeFile (file_name); + dbeFile->filetype |= DbeFile::F_SOURCE | DbeFile::F_FILE; + set_name ((char *) file_name); + srcMTime = (time_t) 0; + isTmpFile = false; + flags = 0; + read_stabs = false; + id = (uint64_t) ((Histable::SOURCEFILE << 24) + curId) << 32; + curId++; +} + +SourceFile::~SourceFile () +{ + destroy_map (DbeLine *, dbeLines); + delete functions; + delete dbeFile; + if (lines) + { + lines->destroy (); + delete lines; + } + if (srcLines) + { + free (srcLines->get (0)); + delete srcLines; + } + if (isTmpFile) + unlink (name); +} + +void +SourceFile::set_name (char* _name) +{ + name = dbe_strdup (_name); +} + +char* +SourceFile::get_name (NameFormat) +{ + return name; +} + +bool +SourceFile::readSource () +{ + if (srcLines) + return true; + status = OS_NOSRC; + char *location = dbeFile->get_location (); + if (location == NULL) + return false; + if (!isTmpFile) + srcMTime = dbeFile->sbuf.st_mtime; + srcInode = dbeFile->sbuf.st_ino; + size_t srcLen = dbeFile->sbuf.st_size; + int fd = open64 (location, O_RDONLY); + if (fd == -1) + { + status = OS_NOSRC; + return false; + } + char *srcMap = (char *) malloc (srcLen + 1); + int64_t sz = read_from_file (fd, srcMap, srcLen); + if (sz != (int64_t) srcLen) + append_msg (CMSG_ERROR, GTXT ("%s: Can read only %lld bytes instead %lld"), + location, (long long) sz, (long long) srcLen); + srcMap[sz] = 0; + close (fd); + + // Count the number of lines in the file, converting <nl> to zero + srcLines = new Vector<char*>(); + srcLines->append (srcMap); + for (int64_t i = 0; i < sz; i++) + { + if (srcMap[i] == '\r') + { // Window style + srcMap[i] = 0; + if (i + 1 < sz && srcMap[i + 1] != '\n') + srcLines->append (srcMap + i + 1); + } + else if (srcMap[i] == '\n') + { + srcMap[i] = '\0'; + if (i + 1 < sz) + srcLines->append (srcMap + i + 1); + } + } + if (dbeLines) + { + Vector<DbeLine *> *v = dbeLines->values (); + for (long i = 0, sz1 = v ? v->size () : 0; i < sz1; i++) + { + DbeLine *p = v->get (i); + if (p->lineno >= srcLines->size ()) + append_msg (CMSG_ERROR, GTXT ("Wrong line number %d. '%s' has only %d lines"), + p->lineno, dbeFile->get_location (), srcLines->size ()); + } + delete v; + } + status = OS_OK; + return true; +} + +char * +SourceFile::getLine (int lineno) +{ + assert (srcLines != NULL); + if (lineno > 0 && lineno <= srcLines->size ()) + return srcLines->get (lineno - 1); + return NTXT (""); +} + +DbeLine * +SourceFile::find_dbeline (Function *func, int lineno) +{ + if (lineno < 0 || (lineno == 0 && func == NULL)) + return NULL; + DbeLine *dbeLine = NULL; + if (lines) + { // the source is available + if (lineno > lines->size ()) + { + if (dbeLines) + dbeLine = dbeLines->get (lineno); + if (dbeLine == NULL) + append_msg (CMSG_ERROR, + GTXT ("Wrong line number %d. '%s' has only %d lines"), + lineno, dbeFile->get_location (), lines->size ()); + } + else + { + dbeLine = lines->fetch (lineno); + if (dbeLine == NULL) + { + dbeLine = new DbeLine (NULL, this, lineno); + lines->store (lineno, dbeLine); + } + } + } + if (dbeLine == NULL) + { // the source is not yet read or lineno is wrong + if (dbeLines == NULL) + dbeLines = new DefaultMap<int, DbeLine *>(); + dbeLine = dbeLines->get (lineno); + if (dbeLine == NULL) + { + dbeLine = new DbeLine (NULL, this, lineno); + dbeLines->put (lineno, dbeLine); + } + } + + for (DbeLine *last = dbeLine;; last = last->dbeline_func_next) + { + if (last->func == func) + return last; + if (last->dbeline_func_next == NULL) + { + DbeLine *dl = new DbeLine (func, this, lineno); + if (functions->get (func) == NULL) + functions->put (func, func); + last->dbeline_func_next = dl; + dl->dbeline_base = dbeLine; + return dl; + } + } +} + +Vector<Function *> * +SourceFile::get_functions () +{ + if (!read_stabs) + { + // Create all DbeLines for this Source + read_stabs = true; + Vector<LoadObject *> *lobjs = dbeSession->get_LoadObjects (); + for (long i = 0, sz = VecSize (lobjs); i < sz; i++) + { + LoadObject *lo = lobjs->get (i); + for (long i1 = 0, sz1 = VecSize (lo->seg_modules); i1 < sz1; i1++) + { + Module *mod = lo->seg_modules->get (i1); + mod->read_stabs (); + } + } + } + return functions->keySet (); +} diff --git a/gprofng/src/SourceFile.h b/gprofng/src/SourceFile.h new file mode 100644 index 0000000..8e90682 --- /dev/null +++ b/gprofng/src/SourceFile.h @@ -0,0 +1,117 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _SOURCEFILE_H +#define _SOURCEFILE_H + +#include "Histable.h" +#include "Map.h" + +template <typename Key_t, typename Value_t> class Map; + +#define SOURCE_FLAG_UNKNOWN 0x01 + +class SourceFile : public HistableFile +{ +public: + + enum OpenStatus + { + OS_OK, + OS_NOTREAD, + OS_NOSRC, + OS_TIMESRC + }; + + SourceFile (const char *file_name); + virtual ~SourceFile (); + virtual void set_name (char *); + virtual char *get_name (NameFormat = NA); + + bool readSource (); + Vector<Function *> *get_functions (); + DbeLine *find_dbeline (Function *func, int lineno); + char *getLine (int lineno); + + int + getLineCount () + { + return srcLines ? srcLines->size () : 0; + } + + ino64_t + getInode () + { + return srcInode; + } + + time_t + getMTime () + { + return srcMTime; + } + + void + setMTime (time_t tm) + { + srcMTime = tm; + } + + bool + isTmp () + { + return isTmpFile; + } + + void + setTmp (bool set) + { + isTmpFile = set; + } + + Histable_type + get_type () + { + return SOURCEFILE; + } + + DbeLine * + find_dbeline (int lineno) + { + return find_dbeline (NULL, lineno); + } + + unsigned int flags; + +private: + static int curId; // object id + OpenStatus status; + ino64_t srcInode; // Inode number of source file + time_t srcMTime; // Creating time for source + Vector<char *> *srcLines; // array of pointers to lines in source + bool isTmpFile; // Temporary src file to be deleted + + Vector<DbeLine*> *lines; + Map<int, DbeLine*> *dbeLines; + Map<Function *, Function *> *functions; + bool read_stabs; +}; + +#endif diff --git a/gprofng/src/Stabs.cc b/gprofng/src/Stabs.cc new file mode 100644 index 0000000..9f1247d --- /dev/null +++ b/gprofng/src/Stabs.cc @@ -0,0 +1,2650 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <sys/param.h> + +#include "util.h" +#include "Elf.h" +#include "Dwarf.h" +#include "stab.h" +#include "DbeSession.h" +#include "CompCom.h" +#include "Stabs.h" +#include "LoadObject.h" +#include "Module.h" +#include "Function.h" +#include "info.h" +#include "StringBuilder.h" +#include "DbeFile.h" +#include "StringMap.h" + +#define DISASM_REL_NONE 0 /* symtab search only */ +#define DISASM_REL_ONLY 1 /* relocation search only */ +#define DISASM_REL_TARG 2 /* relocatoin then symtab */ + +/////////////////////////////////////////////////////////////////////////////// +// class StabReader +class StabReader +{ +public: + StabReader (Elf *_elf, Platform_t platform, int StabSec, int StabStrSec); + ~StabReader () { }; + char *get_type_name (int t); + char *get_stab (struct stab *np, bool comdat); + void parse_N_OPT (Module *mod, char *str); + int stabCnt; + int stabNum; + +private: + Elf *elf; + char *StabData; + char *StabStrtab; + char *StabStrtabEnd; + int StrTabSize; + int StabEntSize; +}; + +/////////////////////////////////////////////////////////////////////////////// +// class Symbol + +class Symbol +{ +public: + Symbol (Vector<Symbol*> *vec = NULL); + + ~Symbol () + { + free (name); + } + + inline Symbol * + cardinal () + { + return alias ? alias : this; + } + + static void dump (Vector<Symbol*> *vec, char*msg); + + Function *func; + Sp_lang_code lang_code; + uint64_t value; // st_value used in sym_name() + uint64_t save; + int64_t size; + uint64_t img_offset; // image offset in the ELF file + char *name; + Symbol *alias; + int local_ind; + int flags; + bool defined; +}; + +Symbol::Symbol (Vector<Symbol*> *vec) +{ + func = NULL; + lang_code = Sp_lang_unknown; + value = 0; + save = 0; + size = 0; + img_offset = 0; + name = NULL; + alias = NULL; + local_ind = -1; + flags = 0; + defined = false; + if (vec) + vec->append (this); +} + +void +Symbol::dump (Vector<Symbol*> *vec, char*msg) +{ + if (!DUMP_ELF_SYM || vec == NULL || vec->size () == 0) + return; + printf (NTXT ("======= Symbol::dump: %s =========\n" + " value | img_offset | flags|local_ind|\n"), msg); + for (int i = 0; i < vec->size (); i++) + { + Symbol *sp = vec->fetch (i); + printf (NTXT (" %3d %8lld |0x%016llx |%5d |%8d |%s\n"), + i, (long long) sp->value, (long long) sp->img_offset, sp->flags, + sp->local_ind, sp->name ? sp->name : NTXT ("NULL")); + } + printf (NTXT ("\n===== END of Symbol::dump: %s =========\n\n"), msg); +} + +// end of class Symbol +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// class Reloc +class Reloc +{ +public: + Reloc (); + ~Reloc (); + uint64_t type; + uint64_t value; + uint64_t addend; + char *name; +}; + +Reloc::Reloc () +{ + type = 0; + value = 0; + addend = 0; + name = NULL; +} + +Reloc::~Reloc () +{ + free (name); +} +// end of class Reloc +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + SYM_PLT = 1 << 0, + SYM_UNDEF = 1 << 1 +}; + +enum Section_type +{ + COMM1_SEC = 0x10000000, + COMM_SEC = 0x20000000, + INFO_SEC = 0x30000000, + LOOP_SEC = 0x40000000 +}; + +struct cpf_stabs_t +{ + uint32_t type; // Archive::AnalyzerInfoType + uint32_t offset; // offset in .__analyzer_info + Module *module; // table for appropriate Module +}; + +static char *get_info_com (int type, int32_t copy_inout); +static char *get_lp_com (unsigned hints, int parallel, char *dep); +static int ComCmp (const void *a, const void *b); +static ino64_t _src_inode = 0; +static char *_src_name; + +// Comparing name +static int +SymNameCmp (const void *a, const void *b) +{ + Symbol *item1 = *((Symbol **) a); + Symbol *item2 = *((Symbol **) b); + return (item1->name == NULL) ? -1 : + (item2->name == NULL) ? 1 : strcmp (item1->name, item2->name); +} + +// Comparing value: for sorting +static int +SymValueCmp (const void *a, const void *b) +{ + Symbol *item1 = *((Symbol **) a); + Symbol *item2 = *((Symbol **) b); + return (item1->value > item2->value) ? 1 : + (item1->value == item2->value) ? SymNameCmp (a, b) : -1; +} + +// Comparing value: for searching (source name is always NULL) +static int +SymFindCmp (const void *a, const void *b) +{ + Symbol *item1 = *((Symbol **) a); + Symbol *item2 = *((Symbol **) b); + if (item1->value < item2->value) + return -1; + if (item1->value < item2->value + item2->size + || item1->value == item2->value) // item2->size == 0 + return 0; + return 1; +} + +// Comparing value for sorting. It is used only for searching aliases. +static int +SymImgOffsetCmp (const void *a, const void *b) +{ + Symbol *item1 = *((Symbol **) a); + Symbol *item2 = *((Symbol **) b); + return (item1->img_offset > item2->img_offset) ? 1 : + (item1->img_offset == item2->img_offset) ? SymNameCmp (a, b) : -1; +} + +static int +RelValueCmp (const void *a, const void *b) +{ + Reloc *item1 = *((Reloc **) a); + Reloc *item2 = *((Reloc **) b); + return (item1->value > item2->value) ? 1 : + (item1->value == item2->value) ? 0 : -1; +} + +Stabs * +Stabs::NewStabs (char *_path, char *lo_name) +{ + Stabs *stabs = new Stabs (_path, lo_name); + if (stabs->status != Stabs::DBGD_ERR_NONE) + { + delete stabs; + return NULL; + } + return stabs; +} + +Stabs::Stabs (char *_path, char *_lo_name) +{ + path = dbe_strdup (_path); + lo_name = dbe_strdup (_lo_name); + SymLstByName = NULL; + pltSym = NULL; + SymLst = new Vector<Symbol*>; + RelLst = new Vector<Reloc*>; + RelPLTLst = new Vector<Reloc*>; + LocalLst = new Vector<Symbol*>; + LocalFile = new Vector<char*>; + LocalFileIdx = new Vector<int>; + last_PC_to_sym = NULL; + dwarf = NULL; + elfDbg = NULL; + elfDis = NULL; + stabsModules = NULL; + textsz = 0; + wsize = Wnone; + st_check_symtab = st_check_relocs = false; + status = DBGD_ERR_NONE; + + if (openElf (false) == NULL) + return; + switch (elfDis->elf_getclass ()) + { + case ELFCLASS32: + wsize = W32; + break; + case ELFCLASS64: + wsize = W64; + break; + } + isRelocatable = elfDis->elf_getehdr ()->e_type == ET_REL; + for (unsigned int pnum = 0; pnum < elfDis->elf_getehdr ()->e_phnum; pnum++) + { + Elf_Internal_Phdr *phdr = elfDis->get_phdr (pnum); + if (phdr->p_type == PT_LOAD && phdr->p_flags == (PF_R | PF_X)) + { + if (textsz == 0) + textsz = phdr->p_memsz; + else + { + textsz = 0; + break; + } + } + } +} + +Stabs::~Stabs () +{ + delete pltSym; + delete SymLstByName; + Destroy (SymLst); + Destroy (RelLst); + Destroy (RelPLTLst); + Destroy (LocalFile); + delete elfDis; + delete dwarf; + delete LocalLst; + delete LocalFileIdx; + delete stabsModules; + free (path); + free (lo_name); +} + +Elf * +Stabs::openElf (char *fname, Stab_status &st) +{ + Elf::Elf_status elf_status; + Elf *elf = Elf::elf_begin (fname, &elf_status); + if (elf == NULL) + { + switch (elf_status) + { + case Elf::ELF_ERR_CANT_OPEN_FILE: + case Elf::ELF_ERR_CANT_MMAP: + case Elf::ELF_ERR_BIG_FILE: + st = DBGD_ERR_CANT_OPEN_FILE; + break; + case Elf::ELF_ERR_BAD_ELF_FORMAT: + default: + st = DBGD_ERR_BAD_ELF_FORMAT; + break; + } + return NULL; + } + if (elf->elf_version (EV_CURRENT) == EV_NONE) + { + // ELF library out of date + delete elf; + st = DBGD_ERR_BAD_ELF_LIB; + return NULL; + } + + Elf_Internal_Ehdr *ehdrp = elf->elf_getehdr (); + if (ehdrp == NULL) + { + // check machine + delete elf; + st = DBGD_ERR_BAD_ELF_FORMAT; + return NULL; + } + switch (ehdrp->e_machine) + { + case EM_SPARC: + platform = Sparc; + break; + case EM_SPARC32PLUS: + platform = Sparcv8plus; + break; + case EM_SPARCV9: + platform = Sparcv9; + break; + case EM_386: + // case EM_486: + platform = Intel; + break; + case EM_X86_64: + platform = Amd64; + break; + case EM_AARCH64: + platform = Aarch64; + break; + default: + platform = Unknown; + break; + } + return elf; +} + +Elf * +Stabs::openElf (bool dbg_info) +{ + if (status != DBGD_ERR_NONE) + return NULL; + if (elfDis == NULL) + { + elfDis = openElf (path, status); + if (elfDis == NULL) + return NULL; + } + if (!dbg_info) + return elfDis; + if (elfDbg == NULL) + { + elfDbg = elfDis->find_ancillary_files (lo_name); + if (elfDbg == NULL) + elfDbg = elfDis; + } + return elfDbg; +} + +bool +Stabs::read_symbols (Vector<Function*> *functions) +{ + if (openElf (true) == NULL) + return false; + check_Symtab (); + check_Relocs (); + if (functions) + { + Function *fp; + int index; + Vec_loop (Function*, functions, index, fp) + { + fp->img_fname = path; + } + } + return true; +} + +char * +Stabs::sym_name (uint64_t target, uint64_t instr, int flag) +{ + long index; + if (flag == DISASM_REL_ONLY || flag == DISASM_REL_TARG) + { + Reloc *relptr = new Reloc; + relptr->value = instr; + index = RelLst->bisearch (0, -1, &relptr, RelValueCmp); + if (index >= 0) + { + delete relptr; + return RelLst->fetch (index)->name; + } + if (!is_relocatable ()) + { + relptr->value = target; + index = RelPLTLst->bisearch (0, -1, &relptr, RelValueCmp); + if (index >= 0) + { + delete relptr; + return RelPLTLst->fetch (index)->name; + } + } + delete relptr; + } + if (flag == DISASM_REL_NONE || flag == DISASM_REL_TARG || !is_relocatable ()) + { + Symbol *sptr; + sptr = map_PC_to_sym (target); + if (sptr && sptr->value == target) + return sptr->name; + } + return NULL; +} + +Symbol * +Stabs::map_PC_to_sym (uint64_t pc) +{ + if (pc == 0) + return NULL; + if (last_PC_to_sym && last_PC_to_sym->value <= pc + && last_PC_to_sym->value + last_PC_to_sym->size > pc) + return last_PC_to_sym; + Symbol *sym = new Symbol; + sym->value = pc; + long index = SymLst->bisearch (0, -1, &sym, SymFindCmp); + delete sym; + if (index >= 0) + { + last_PC_to_sym = SymLst->fetch (index)->cardinal (); + return last_PC_to_sym; + } + return NULL; +} + +Function * +Stabs::map_PC_to_func (uint64_t pc, uint64_t &low_pc, Vector<Function*> *functions) +{ + int index; + Function *func; + Symbol *sptr = map_PC_to_sym (pc); + if (sptr == NULL) + return NULL; + if (sptr->func) + { + low_pc = sptr->value; + return sptr->func; + } + if (functions) + { + Vec_loop (Function*, functions, index, func) + { + if (func->img_offset == sptr->img_offset) + { + sptr->func = func->cardinal (); + low_pc = sptr->value; + return sptr->func; + } + } + } + return NULL; +} + +Stabs::Stab_status +Stabs::read_stabs (ino64_t srcInode, Module *module, Vector<ComC*> *comComs, + bool readDwarf) +{ + if (module) + module->setIncludeFile (NULL); + + if (openElf (true) == NULL) + return status; + check_Symtab (); + + // read compiler commentary from .compcom1, .compcom, + // .info, .loops, and .loopview sections + if (comComs) + { + _src_inode = srcInode; + _src_name = module && module->file_name ? get_basename (module->file_name) : NULL; + if (!check_Comm (comComs)) + // .loops, and .loopview are now in .compcom + check_Loop (comComs); + + // should not read it after .info goes into .compcom + check_Info (comComs); + comComs->sort (ComCmp); + } + + // get stabs info + Stab_status statusStabs = DBGD_ERR_NO_STABS; +#define SRC_LINE_STABS(sec, secStr, comdat) \ + if ((elfDbg->sec) && (elfDbg->secStr) && \ + srcline_Stabs(module, elfDbg->sec, elfDbg->secStr, comdat) == DBGD_ERR_NONE) \ + statusStabs = DBGD_ERR_NONE + + SRC_LINE_STABS (stabExcl, stabExclStr, false); + SRC_LINE_STABS (stab, stabStr, false); + SRC_LINE_STABS (stabIndex, stabIndexStr, true); + + // read Dwarf, if any sections found + if (elfDbg->dwarf && readDwarf) + { + openDwarf ()->srcline_Dwarf (module); + if (dwarf && dwarf->status == DBGD_ERR_NONE) + return DBGD_ERR_NONE; + } + return statusStabs; +} + +static int +ComCmp (const void *a, const void *b) +{ + ComC *item1 = *((ComC **) a); + ComC *item2 = *((ComC **) b); + return (item1->line > item2->line) ? 1 : + (item1->line < item2->line) ? -1 : + (item1->sec > item2->sec) ? 1 : + (item1->sec < item2->sec) ? -1 : 0; +} + +static int +check_src_name (char *srcName) +{ + if (_src_name && srcName && streq (_src_name, get_basename (srcName))) + return 1; + if (_src_inode == (ino64_t) - 1) + return 0; + DbeFile *dbeFile = dbeSession->getDbeFile (srcName, DbeFile::F_SOURCE); + char *path = dbeFile->get_location (); + return (path == NULL || dbeFile->sbuf.st_ino != _src_inode) ? 0 : 1; +} + +bool +Stabs::check_Comm (Vector<ComC*> *comComs) +{ + int sz = comComs->size (); + Elf *elf = openElf (true); + if (elf == NULL) + return false; + + for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++) + { + char *name = elf->get_sec_name (sec); + if (name == NULL) + continue; + Section_type sec_type; + if (streq (name, NTXT (".compcom"))) + sec_type = COMM_SEC; + else if (streq (name, NTXT (".compcom1"))) + sec_type = COMM1_SEC; + else + continue; + + // find header, set messages id & visibility if succeed + CompComment *cc = new CompComment (elf, sec); + int cnt = cc->compcom_open ((CheckSrcName) check_src_name); + // process messages + for (int index = 0; index < cnt; index++) + { + int visible; + compmsg msg; + char *str = cc->compcom_format (index, &msg, visible); + if (str) + { + ComC *citem = new ComC; + citem->sec = sec_type + index; + citem->type = msg.msg_type; + citem->visible = visible; + citem->line = (msg.lineno < 1) ? 1 : msg.lineno; + citem->com_str = str; + comComs->append (citem); + } + } + delete cc; + } + return (sz != comComs->size ()); +} + +static int +targetOffsetCmp (const void *a, const void *b) +{ + uint32_t o1 = ((target_info_t *) a)->offset; + uint32_t o2 = ((target_info_t *) b)->offset; + return (o1 >= o2); +} + +void +Stabs::check_AnalyzerInfo () +{ + Elf *elf = openElf (true); + if ((elf == NULL) || (elf->analyzerInfo == 0)) + { + Dprintf (DEBUG_STABS, NTXT ("Stabs::check_AnalyzerInfo: Null AnalyzerInfo section\n")); + return; // inappropriate, but ignored anyway + } + Elf_Data *data = elf->elf_getdata (elf->analyzerInfo); + int InfoSize = (int) data->d_size; + char *InfoData = (char *) data->d_buf; + int InfoAlign = (int) data->d_align; + AnalyzerInfoHdr h; + unsigned infoHdr_sz = sizeof (AnalyzerInfoHdr); + int table, entry; + int read = 0; + Module *mitem; + int index = 0; + if (InfoSize <= 0) + return; + uint64_t baseAddr = elf->get_baseAddr (); + Dprintf (DEBUG_STABS, NTXT ("Stabs::check_AnalyzerInfo size=%d @0x%lx (align=%d) base=0x%llx\n"), + InfoSize, (ul_t) InfoData, InfoAlign, (long long) baseAddr); + Dprintf (DEBUG_STABS, NTXT ("analyzerInfoMap has %lld entries\n"), (long long) analyzerInfoMap.size ()); + if (analyzerInfoMap.size () == 0) + { + Dprintf (DEBUG_STABS, NTXT ("No analyzerInfoMap available!\n")); + return; + } + + // verify integrity of analyzerInfoMap before reading analyzerInfo + unsigned count = 0; + Module *lastmod = NULL; + for (index = 0; index < analyzerInfoMap.size (); index++) + { + cpf_stabs_t map = analyzerInfoMap.fetch (index); + if (map.type > 3) + { + Dprintf (DEBUG_STABS, NTXT ("analyzerInfo contains table of unknown type %d for %s\n"), + map.type, map.module->get_name ()); + return; + } + if (map.module != lastmod) + { + if (lastmod != NULL) + Dprintf (DEBUG_STABS, "analyzerInfo contains %d 0x0 offset tables for %s\n", + count, lastmod->get_name ()); + count = 0; + } + count += (map.offset == 0x0); // only check for 0x0 tables for now + if (count > 4) + { + Dprintf (DEBUG_STABS, NTXT ("analyzerInfo contains too many 0x0 offset tables for %s\n"), + map.module->get_name ()); + return; + } + lastmod = map.module; + } + + index = 0; + while ((index < analyzerInfoMap.size ()) && (read < InfoSize)) + { + for (table = 0; table < 3; table++) + { // memory operations (ld, st, prefetch) + // read the table header + memcpy ((void *) &h, (const void *) InfoData, infoHdr_sz); + InfoData += infoHdr_sz; + read += infoHdr_sz; + + // use map for appropriate module + cpf_stabs_t map = analyzerInfoMap.fetch (index); + index++; + mitem = map.module; + Dprintf (DEBUG_STABS, "Table %d offset=0x%04x " + "text_labelref=0x%08llx entries=%d version=%d\n" + "itype %d offset=0x%04x module=%s\n", table, read, + (long long) (h.text_labelref - baseAddr), h.entries, + h.version, map.type, map.offset, map.module->get_name ()); + // read the table entries + for (entry = 0; entry < h.entries; entry++) + { + memop_info_t *m = new memop_info_t; + unsigned memop_info_sz = sizeof (memop_info_t); + memcpy ((void *) m, (const void *) InfoData, memop_info_sz); + InfoData += memop_info_sz; + read += memop_info_sz; + m->offset += (uint32_t) (h.text_labelref - baseAddr); + Dprintf (DEBUG_STABS, NTXT ("%4d(%d): offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n"), + entry, table, m->offset, m->id, m->signature, m->datatype_id); + switch (table) + { + case CPF_INSTR_TYPE_LD: + mitem->ldMemops.append (m); + break; + case CPF_INSTR_TYPE_ST: + mitem->stMemops.append (m); + break; + case CPF_INSTR_TYPE_PREFETCH: + mitem->pfMemops.append (m); + break; + } + } + // following re-alignment should be redundant + //InfoData+=(read%InfoAlign); read+=(read%InfoAlign); // re-align + } + for (table = 3; table < 4; table++) + { // branch targets + memcpy ((void *) &h, (const void *) InfoData, infoHdr_sz); + InfoData += infoHdr_sz; + read += infoHdr_sz; + + // use map for appropriate module + cpf_stabs_t map = analyzerInfoMap.fetch (index); + index++; + mitem = map.module; + Dprintf (DEBUG_STABS, "Table %d offset=0x%04x " + "text_labelref=0x%08llx entries=%d version=%d\n" + "itype %d offset=0x%04x module=%s\n", table, read, + (long long) (h.text_labelref - baseAddr), h.entries, + h.version, map.type, map.offset, map.module->get_name ()); + for (entry = 0; entry < h.entries; entry++) + { + target_info_t *t = new target_info_t; + unsigned target_info_sz = sizeof (target_info_t); + memcpy ((void *) t, (const void *) InfoData, target_info_sz); + InfoData += target_info_sz; + read += target_info_sz; + t->offset += (uint32_t) (h.text_labelref - baseAddr); + Dprintf (DEBUG_STABS, NTXT ("%4d(%d): offset=0x%04x\n"), entry, + table, t->offset); + // the list of branch targets needs to be in offset sorted order + // and doing it here before archiving avoids the need to do it + // each time the archive is read. + mitem->bTargets.incorporate (t, targetOffsetCmp); + } + Dprintf (DEBUG_STABS, NTXT ("bTargets for %s has %lld items (last=0x%04x)\n"), + mitem->get_name (), (long long) mitem->bTargets.size (), + (mitem->bTargets.fetch (mitem->bTargets.size () - 1))->offset); + Dprintf (DEBUG_STABS, "read=%d at end of bTargets (InfoData=0x%lx)\n", + read, (ul_t) InfoData); + InfoData += (read % InfoAlign); + read += (read % InfoAlign); // re-align + Dprintf (DEBUG_STABS, "read=%d at end of bTargets (InfoData=0x%lx)\n", + read, (ul_t) InfoData); + } + Dprintf (DEBUG_STABS, "Stabs::check_AnalyzerInfo bytes read=%lld (index=%lld/%lld)\n", + (long long) read, (long long) index, + (long long) analyzerInfoMap.size ()); + } +} + +void +Stabs::check_Info (Vector<ComC*> *comComs) +{ + Elf *elf = openElf (true); + if (elf == NULL || elf->info == 0) + return; + Elf_Data *data = elf->elf_getdata (elf->info); + uint64_t InfoSize = data->d_size; + char *InfoData = (char *) data->d_buf; + bool get_src = false; + for (int h_num = 0; InfoSize; h_num++) + { + if (InfoSize < sizeof (struct info_header)) + return; + struct info_header *h = (struct info_header*) InfoData; + if (h->endian != '\0' || h->magic[0] != 'S' || h->magic[1] != 'U' + || h->magic[2] != 'N') + return; + if (h->len < InfoSize || h->len < sizeof (struct info_header) || (h->len & 3)) + return; + + char *fname = InfoData + sizeof (struct info_header); + InfoData += h->len; + InfoSize -= h->len; + get_src = check_src_name (fname); + for (uint32_t e_num = 0; e_num < h->cnt; ++e_num) + { + if (InfoSize < sizeof (struct entry_header)) + return; + struct entry_header *e = (struct entry_header*) InfoData; + if (InfoSize < e->len) + return; + int32_t copy_inout = 0; + if (e->len > sizeof (struct entry_header)) + if (e->type == F95_COPYINOUT) + copy_inout = *(int32_t*) (InfoData + sizeof (struct entry_header)); + InfoData += e->len; + InfoSize -= e->len; + if (get_src) + { + ComC *citem = new ComC; + citem->sec = INFO_SEC + h_num; + citem->type = e->msgnum & 0xFFFFFF; + citem->visible = CCMV_ALL; + citem->line = e->line; + citem->com_str = get_info_com (citem->type, copy_inout); + comComs->append (citem); + } + } + if (get_src) + break; + } +} + +static char * +get_info_com (int type, int32_t copy_inout) +{ + switch (type) + { + case 1: + return dbe_sprintf (GTXT ("In the call below, parameter number %d caused a copy-in -- loop(s) inserted"), + copy_inout); + case 2: + return dbe_sprintf (GTXT ("In the call below, parameter number %d caused a copy-out -- loop(s) inserted"), + copy_inout); + case 3: + return dbe_sprintf (GTXT ("In the call below, parameter number %d caused a copy-in and a copy-out -- loops inserted"), + copy_inout); + case 4: + return dbe_strdup (GTXT ("Alignment of variables in common block may cause performance degradation")); + case 5: + return dbe_strdup (GTXT ("DO statement bounds lead to no executions of the loop")); + default: + return dbe_strdup (NTXT ("")); + } +} + +void +Stabs::check_Loop (Vector<ComC*> *comComs) +{ + Elf *elf = openElf (true); + if (elf == NULL) + return; + + StringBuilder sb; + for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++) + { + char *name = elf->get_sec_name (sec); + if (name == NULL) + continue; + if (!streq (name, NTXT (".loops")) && !streq (name, NTXT (".loopview"))) + continue; + + Elf_Data *data = elf->elf_getdata (sec); + size_t LoopSize = (size_t) data->d_size, len; + char *LoopData = (char *) data->d_buf; + int remainder, i; + char src[2 * MAXPATHLEN], buf1[MAXPATHLEN], buf2[MAXPATHLEN]; + char **dep_str = NULL; + bool get_src = false; + while ((LoopSize > 0) && !get_src && + (strncmp (LoopData, NTXT ("Source:"), 7) == 0)) + { + // The first three items in a .loops subsection are three strings. + // Source: ... + // Version: ... + // Number of loops: ... + sscanf (LoopData, NTXT ("%*s%s"), src); + len = strlen (LoopData) + 1; + LoopData += len; + LoopSize -= len; + sscanf (LoopData, NTXT ("%*s%*s%s"), buf1); + // double version = atof(buf1); + len = strlen (LoopData) + 1; + LoopData += len; + LoopSize -= len; + get_src = check_src_name (src); + sscanf (LoopData, NTXT ("%*s%*s%*s%s%s"), buf1, buf2); + int n_loop = atoi (buf1); + int n_depend = atoi (buf2); + len = strlen (LoopData) + 1; + LoopData += len; + LoopSize -= len; + if (get_src && (n_loop > 0)) + { + dep_str = new char*[n_loop]; + for (i = 0; i < n_loop; i++) + dep_str[i] = NULL; + } + + // printf("Source: %s\nVersion: %f\nLoop#: %d\nDepend#: %d\n", + // src, version, n_loop, n_depend); + + // Read in the strings that contain the list of variables that cause + // data dependencies inside of loops. Not every loop has such a list + // of variables. + // + // Example: if loop #54 has data dependencies caused by the + // variables named i, j and foo, then the string that represents + // this in the .loops section looks like this: + // + // .asciz "54:i.j.foo" + // + // The variable names are delimited with . + // + // For now, store these strings in an array, and add them into + // the loop structure when we read in the numeric loop info + // (that's what we read in next.) + // + // printf("\tDependenncies:\n"); + for (i = 0; i < n_depend; i++) + { + len = strlen (LoopData) + 1; + LoopData += len; + LoopSize -= len; + if (dep_str != NULL) + { + char *dep_buf1 = dbe_strdup (LoopData); + char *ptr = strtok (dep_buf1, NTXT (":")); + if (ptr != NULL) + { + int index = atoi (ptr); + bool dep_first = true; + sb.setLength (0); + while ((ptr = strtok (NULL, NTXT (", "))) != NULL) + { + if (dep_first) + dep_first = false; + else + sb.append (NTXT (", ")); + sb.append (ptr); + } + if (sb.length () > 0 && index < n_loop) + dep_str[index] = sb.toString (); + } + free (dep_buf1); + } + } + + // Adjust Data pointer so that it is word aligned. + remainder = (int) (((unsigned long) LoopData) % 4); + if (remainder != 0) + { + len = 4 - remainder; + LoopData += len; + LoopSize -= len; + } + + // Read in the loop info, one loop at a time. + for (i = 0; i < n_loop; i++) + { + int loopid = *((int *) LoopData); + LoopData += 4; + int line_no = *((int *) LoopData); + if (line_no < 1) // compiler has trouble on this + line_no = 1; + LoopData += 4; + // int nest = *((int *) LoopData); + LoopData += 4; + int parallel = *((int *) LoopData); + LoopData += 4; + unsigned hints = *((unsigned *) LoopData); + LoopData += 4; + // int count = *((int *) LoopData); + LoopData += 4; + LoopSize -= 24; + if (!get_src || (loopid >= n_loop)) + continue; + ComC *citem = new ComC; + citem->sec = LOOP_SEC + i; + citem->type = hints; + citem->visible = CCMV_ALL; + citem->line = line_no; + citem->com_str = get_lp_com (hints, parallel, dep_str[loopid]); + comComs->append (citem); + } + if (dep_str) + { + for (i = 0; i < n_loop; i++) + free (dep_str[i]); + delete[] dep_str; + dep_str = NULL; + } + } + } +} + +static char * +get_lp_com (unsigned hints, int parallel, char *dep) +{ + StringBuilder sb; + if (parallel == -1) + sb.append (GTXT ("Loop below is serial, but parallelizable: ")); + else if (parallel == 0) + sb.append (GTXT ("Loop below is not parallelized: ")); + else + sb.append (GTXT ("Loop below is parallelized: ")); + switch (hints) + { + case 0: + // No loop mesg will print + // strcat(com, GTXT("no hint available")); + break; + case 1: + sb.append (GTXT ("loop contains procedure call")); + break; + case 2: + sb.append (GTXT ("compiler generated two versions of this loop")); + break; + case 3: + { + StringBuilder sb_tmp; + sb_tmp.sprintf (GTXT ("the variable(s) \"%s\" cause a data dependency in this loop"), + dep ? dep : GTXT ("<Unknown>")); + sb.append (&sb_tmp); + } + break; + case 4: + sb.append (GTXT ("loop was significantly transformed during optimization")); + break; + case 5: + sb.append (GTXT ("loop may or may not hold enough work to be profitably parallelized")); + break; + case 6: + sb.append (GTXT ("loop was marked by user-inserted pragma")); + break; + case 7: + sb.append (GTXT ("loop contains multiple exits")); + break; + case 8: + sb.append (GTXT ("loop contains I/O, or other function calls, that are not MT safe")); + break; + case 9: + sb.append (GTXT ("loop contains backward flow of control")); + break; + case 10: + sb.append (GTXT ("loop may have been distributed")); + break; + case 11: + sb.append (GTXT ("two loops or more may have been fused")); + break; + case 12: + sb.append (GTXT ("two or more loops may have been interchanged")); + break; + default: + break; + } + return sb.toString (); +} + +StabReader::StabReader (Elf *_elf, Platform_t platform, int StabSec, int StabStrSec) +{ + stabCnt = -1; + stabNum = 0; + if (_elf == NULL) + return; + elf = _elf; + + // Get ELF data + Elf_Data *data = elf->elf_getdata (StabSec); + if (data == NULL) + return; + uint64_t stabSize = data->d_size; + StabData = (char *) data->d_buf; + Elf_Internal_Shdr *shdr = elf->get_shdr (StabSec); + if (shdr == NULL) + return; + + // GCC bug: sh_entsize is 20 for 64 apps on Linux + StabEntSize = (platform == Amd64 || platform == Sparcv9) ? 12 : (unsigned) shdr->sh_entsize; + if (stabSize == 0 || StabEntSize == 0) + return; + data = elf->elf_getdata (StabStrSec); + if (data == NULL) + return; + shdr = elf->get_shdr (StabStrSec); + if (shdr == NULL) + return; + StabStrtab = (char *) data->d_buf; + StabStrtabEnd = StabStrtab + shdr->sh_size; + StrTabSize = 0; + stabCnt = (int) (stabSize / StabEntSize); +} + +char * +StabReader::get_stab (struct stab *np, bool comdat) +{ + struct stab *stbp = (struct stab *) (StabData + stabNum * StabEntSize); + stabNum++; + *np = *stbp; + np->n_desc = elf->decode (stbp->n_desc); + np->n_strx = elf->decode (stbp->n_strx); + np->n_value = elf->decode (stbp->n_value); + switch (np->n_type) + { + case N_UNDF: + case N_ILDPAD: + // Start of new stab section (or padding) + StabStrtab += StrTabSize; + StrTabSize = np->n_value; + } + + char *str = NULL; + if (np->n_strx) + { + if (comdat && np->n_type == N_FUN && np->n_other == 1) + { + if (np->n_strx == 1) + StrTabSize++; + str = StabStrtab + StrTabSize; + // Each COMDAT string must be sized to find the next string: + StrTabSize += strlen (str) + 1; + } + else + str = StabStrtab + np->n_strx; + if (str >= StabStrtabEnd) + str = NULL; + } + if (DEBUG_STABS) + { + char buf[128]; + char *s = get_type_name (np->n_type); + if (s == NULL) + { + snprintf (buf, sizeof (buf), NTXT ("n_type=%d"), np->n_type); + s = buf; + } + if (str) + { + Dprintf (DEBUG_STABS, NTXT ("%4d: .stabs \"%s\",%s,0x%x,0x%x,0x%x\n"), + stabNum - 1, str, s, (int) np->n_other, (int) np->n_desc, + (int) np->n_value); + } + else + Dprintf (DEBUG_STABS, NTXT ("%4d: .stabn %s,0x%x,0x%x,0x%x\n"), + stabNum - 1, s, (int) np->n_other, (int) np->n_desc, + (int) np->n_value); + } + return str; +} + +void +StabReader::parse_N_OPT (Module *mod, char *str) +{ + if (mod == NULL || str == NULL) + return; + for (char *s = str; 1; s++) + { + switch (*s) + { + case 'd': + if (s[1] == 'i' && s[2] == ';') + { + delete mod->dot_o_file; + mod->dot_o_file = NULL; + } + break; + case 's': + if ((s[1] == 'i' || s[1] == 'n') && s[2] == ';') + { + delete mod->dot_o_file; + mod->dot_o_file = NULL; + } + break; + } + s = strchr (s, ';'); + if (s == NULL) + break; + } +} + +Stabs::Stab_status +Stabs::srcline_Stabs (Module *module, unsigned int StabSec, + unsigned int StabStrSec, bool comdat) +{ + StabReader *stabReader = new StabReader (openElf (true), platform, StabSec, StabStrSec); + int tot = stabReader->stabCnt; + if (tot < 0) + { + delete stabReader; + return DBGD_ERR_NO_STABS; + } + int n, lineno; + char *sbase, *n_so = NTXT (""), curr_src[2 * MAXPATHLEN]; + Function *newFunc; + Sp_lang_code _lang_code = module->lang_code; + Vector<Function*> *functions = module->functions; + bool no_stabs = true; + *curr_src = '\0'; + Function *func = NULL; + int phase = 0; + int stabs_level = 0; + int xline = 0; + + // Find module + for (n = 0; n < tot; n++) + { + struct stab stb; + char *str = stabReader->get_stab (&stb, comdat); + if (stb.n_type == N_UNDF) + phase = 0; + else if (stb.n_type == N_SO) + { + if (str == NULL || *str == '\0') + continue; + if (phase == 0) + { + phase = 1; + n_so = str; + continue; + } + phase = 0; + sbase = str; + if (*str == '/') + { + if (streq (sbase, module->file_name)) + break; + } + else + { + size_t last = strlen (n_so); + if (n_so[last - 1] == '/') + last--; + if (strncmp (n_so, module->file_name, last) == 0 && + module->file_name[last] == '/' && + streq (sbase, module->file_name + last + 1)) + break; + } + } + } + if (n >= tot) + { + delete stabReader; + return DBGD_ERR_NO_STABS; + } + + Include *includes = new Include; + includes->new_src_file (module->getMainSrc (), 0, NULL); + module->hasStabs = true; + *curr_src = '\0'; + phase = 0; + for (n++; n < tot; n++) + { + struct stab stb; + char *str = stabReader->get_stab (&stb, comdat); + int n_desc = (int) ((unsigned short) stb.n_desc); + switch (stb.n_type) + { + case N_UNDF: + case N_SO: + case N_ENDM: + n = tot; + break; + case N_ALIAS: + if (str == NULL) + break; + if (is_fortran (_lang_code)) + { + char *p = strchr (str, ':'); + if (p && streq (p + 1, NTXT ("FMAIN"))) + { + Function *afunc = find_func (NTXT ("MAIN"), functions, true); + if (afunc) + afunc->set_match_name (dbe_strndup (str, p - str)); + break; + } + } + case N_FUN: + case N_OUTL: + if (str == NULL) + break; + if (*str == '@') + { + str++; + if (*str == '>' || *str == '<') + str++; + } + if (stabs_level != 0) + break; + + // find address of the enclosed function + newFunc = find_func (str, functions, is_fortran (_lang_code)); + if (newFunc == NULL) + break; + if (func) + while (func->popSrcFile ()) + ; + func = newFunc; + + // First line info to cover function from the beginning + lineno = xline + n_desc; + if (lineno > 0) + { + // Set the chain of includes for the new function + includes->push_src_files (func); + func->add_PC_info (0, lineno); + no_stabs = false; + } + break; + case N_ENTRY: + break; + case N_CMDLINE: + if (str && !module->comp_flags) + { + char *comp_flags = strchr (str, ';'); + if (comp_flags) + { + module->comp_flags = dbe_strdup (comp_flags + 1); + module->comp_dir = dbe_strndup (str, comp_flags - str); + } + } + break; + case N_LBRAC: + stabs_level++; + break; + case N_RBRAC: + stabs_level--; + break; + case N_XLINE: + xline = n_desc << 16; + break; + case N_SLINE: + if (func == NULL) + break; + no_stabs = false; + lineno = xline + n_desc; + if (func->line_first <= 0) + { + // Set the chain of includes for the new function + includes->push_src_files (func); + func->add_PC_info (0, lineno); + break; + } + if (func->curr_srcfile == NULL) + includes->push_src_files (func); + if (func->line_first != lineno || + !streq (curr_src, func->getDefSrc ()->get_name ())) + func->add_PC_info (stb.n_value, lineno); + break; + case N_OPT: + if ((str != NULL) && streq (str, NTXT ("gcc2_compiled."))) + _lang_code = Sp_lang_gcc; + switch (elfDbg->elf_getehdr ()->e_type) + { + case ET_EXEC: + case ET_DYN: + // set the real object timestamp from the executable's N_OPT stab + // due to bug #4796329 + module->real_timestamp = stb.n_value; + break; + default: + module->curr_timestamp = stb.n_value; + break; + } + break; + case N_GSYM: + if ((str == NULL) || strncmp (str, NTXT ("__KAI_K"), 7)) + break; + str += 7; + if (!strncmp (str, NTXT ("CC_"), 3)) + _lang_code = Sp_lang_KAI_KCC; + else if (!strncmp (str, NTXT ("cc_"), 3)) + _lang_code = Sp_lang_KAI_Kcc; + else if (!strncmp (str, NTXT ("PTS_"), 4) && + (_lang_code != Sp_lang_KAI_KCC) && + (_lang_code != Sp_lang_KAI_Kcc)) + _lang_code = Sp_lang_KAI_KPTS; + break; + case N_BINCL: + includes->new_include_file (module->setIncludeFile (str), func); + break; + case N_EINCL: + includes->end_include_file (func); + break; + case N_SOL: + if (str == NULL) + break; + lineno = xline + n_desc; + if (lineno > 0 && func && func->line_first <= 0) + { + includes->push_src_files (func); + func->add_PC_info (0, lineno); + no_stabs = false; + } + if (streq (sbase, str)) + { + module->setIncludeFile (NULL); + snprintf (curr_src, sizeof (curr_src), NTXT ("%s"), module->file_name); + includes->new_src_file (module->getMainSrc (), lineno, func); + } + else + { + if (streq (sbase, get_basename (str))) + { + module->setIncludeFile (NULL); + snprintf (curr_src, sizeof (curr_src), NTXT ("%s"), module->file_name); + includes->new_src_file (module->setIncludeFile (curr_src), lineno, func); + } + else + { + if (*str == '/') + snprintf (curr_src, sizeof (curr_src), NTXT ("%s"), str); + else + { + size_t last = strlen (n_so); + if (last == 0 || n_so[last - 1] != '/') + snprintf (curr_src, sizeof (curr_src), NTXT ("%s/%s"), n_so, str); + else + snprintf (curr_src, sizeof (curr_src), NTXT ("%s%s"), n_so, str); + } + includes->new_src_file (module->setIncludeFile (curr_src), lineno, func); + } + } + break; + } + } + delete includes; + delete stabReader; + return no_stabs ? DBGD_ERR_NO_STABS : DBGD_ERR_NONE; +}//srcline_Stabs + +static bool +cmp_func_name (char *fname, size_t len, char *name, bool fortran) +{ + return (strncmp (name, fname, len) == 0 + && (name[len] == 0 + || (fortran && name[len] == '_' && name[len + 1] == 0))); +} + +Function * +Stabs::find_func (char *fname, Vector<Function*> *functions, bool fortran, bool inner_names) +{ + char *arg, *name; + Function *item; + int index; + size_t len; + + len = strlen (fname); + arg = strchr (fname, ':'); + if (arg != NULL) + { + if (arg[1] == 'P') // Prototype for function + return NULL; + len -= strlen (arg); + } + + Vec_loop (Function*, functions, index, item) + { + name = item->get_mangled_name (); + if (cmp_func_name (fname, len, name, fortran)) + return item->cardinal (); + } + + if (inner_names) + { + // Dwarf subprograms may only have plain (non-linker) names + // Retry with inner names only + + Vec_loop (Function*, functions, index, item) + { + name = strrchr (item->get_mangled_name (), '.'); + if (!name) continue; + name++; + if (cmp_func_name (fname, len, name, fortran)) + return item->cardinal (); + } + } + return NULL; +} + +Map<const char*, Symbol*> * +Stabs::get_elf_symbols () +{ + Elf *elf = openElf (false); + if (elf->elfSymbols == NULL) + { + Map<const char*, Symbol*> *elfSymbols = new StringMap<Symbol*>(128, 128); + elf->elfSymbols = elfSymbols; + for (int i = 0, sz = SymLst ? SymLst->size () : 0; i < sz; i++) + { + Symbol *sym = SymLst->fetch (i); + elfSymbols->put (sym->name, sym); + } + } + return elf->elfSymbols; +} + +void +Stabs::read_dwarf_from_dot_o (Module *mod) +{ + Dprintf (DEBUG_STABS, NTXT ("stabsModules: %s\n"), STR (mod->get_name ())); + Vector<Module*> *mods = mod->dot_o_file->seg_modules; + char *bname = get_basename (mod->get_name ()); + for (int i1 = 0, sz1 = mods ? mods->size () : 0; i1 < sz1; i1++) + { + Module *m = mods->fetch (i1); + Dprintf (DEBUG_STABS, NTXT (" MOD: %s\n"), STR (m->get_name ())); + if (dbe_strcmp (bname, get_basename (m->get_name ())) == 0) + { + mod->indexStabsLink = m; + m->indexStabsLink = mod; + break; + } + } + if (mod->indexStabsLink) + { + mod->dot_o_file->objStabs->openDwarf ()->srcline_Dwarf (mod->indexStabsLink); + Map<const char*, Symbol*> *elfSymbols = get_elf_symbols (); + Vector<Function*> *funcs = mod->indexStabsLink->functions; + for (int i1 = 0, sz1 = funcs ? funcs->size () : 0; i1 < sz1; i1++) + { + Function *f1 = funcs->fetch (i1); + Symbol *sym = elfSymbols->get (f1->get_mangled_name ()); + if (sym == NULL) + continue; + Dprintf (DEBUG_STABS, NTXT (" Symbol: %s func=%p\n"), STR (sym->name), sym->func); + Function *f = sym->func; + if (f->indexStabsLink) + continue; + f->indexStabsLink = f1; + f1->indexStabsLink = f; + f->copy_PCInfo (f1); + } + } +} + +Stabs::Stab_status +Stabs::read_archive (LoadObject *lo) +{ + if (openElf (true) == NULL) + return status; + check_Symtab (); + if (elfDbg->dwarf) + openDwarf ()->archive_Dwarf (lo); + + // get Module/Function lists from stabs info + Stab_status statusStabs = DBGD_ERR_NO_STABS; +#define ARCHIVE_STABS(sec, secStr, comdat) \ + if ((elfDbg->sec) != 0 && (elfDbg->secStr) != 0 && \ + archive_Stabs(lo, elfDbg->sec, elfDbg->secStr, comdat) == DBGD_ERR_NONE) \ + statusStabs = DBGD_ERR_NONE + + // prefer index stabs (where they exist) since they're most appropriate + // for loadobjects and might have N_CPROF stabs for ABS/CPF + ARCHIVE_STABS (stabIndex, stabIndexStr, true); + ARCHIVE_STABS (stabExcl, stabExclStr, false); + ARCHIVE_STABS (stab, stabStr, false); + + // Add all unassigned functions to the <unknown> module + Symbol *sitem, *alias; + int index; + Vec_loop (Symbol*, SymLst, index, sitem) + { + if (sitem->func || (sitem->size == 0) || (sitem->flags & SYM_UNDEF)) + continue; + alias = sitem->alias; + if (alias) + { + if (alias->func == NULL) + { + alias->func = createFunction (lo, lo->noname, alias); + alias->func->alias = alias->func; + } + if (alias != sitem) + { + sitem->func = createFunction (lo, alias->func->module, sitem); + sitem->func->alias = alias->func; + } + } + else + sitem->func = createFunction (lo, lo->noname, sitem); + } + if (pltSym) + { + pltSym->func = createFunction (lo, lo->noname, pltSym); + pltSym->func->flags |= FUNC_FLAG_PLT; + } + + // need Module association, so this must be done after handling Modules + check_AnalyzerInfo (); + + if (dwarf && dwarf->status == DBGD_ERR_NONE) + return DBGD_ERR_NONE; + return statusStabs; +}//read_archive + +Function * +Stabs::createFunction (LoadObject *lo, Module *module, Symbol *sym) +{ + Function *func = dbeSession->createFunction (); + func->module = module; + func->img_fname = path; + func->img_offset = (off_t) sym->img_offset; + func->save_addr = sym->save; + func->size = (uint32_t) sym->size; + func->set_name (sym->name); + func->elfSym = sym; + module->functions->append (func); + lo->functions->append (func); + return func; +} + +void +Stabs::fixSymtabAlias () +{ + int ind, i, k; + Symbol *sym, *bestAlias; + SymLst->sort (SymImgOffsetCmp); + ind = SymLst->size () - 1; + for (i = 0; i < ind; i++) + { + bestAlias = SymLst->fetch (i); + if (bestAlias->img_offset == 0) // Ignore this bad symbol + continue; + sym = SymLst->fetch (i + 1); + if (bestAlias->img_offset != sym->img_offset) + { + if ((bestAlias->size == 0) || + (sym->img_offset < bestAlias->img_offset + bestAlias->size)) + bestAlias->size = sym->img_offset - bestAlias->img_offset; + continue; + } + + // Find a "best" alias + size_t bestLen = strlen (bestAlias->name); + int64_t maxSize = bestAlias->size; + for (k = i + 1; k <= ind; k++) + { + sym = SymLst->fetch (k); + if (bestAlias->img_offset != sym->img_offset) + { // no more aliases + if ((maxSize == 0) || + (sym->img_offset < bestAlias->img_offset + maxSize)) + maxSize = sym->img_offset - bestAlias->img_offset; + break; + } + if (maxSize < sym->size) + maxSize = sym->size; + size_t len = strlen (sym->name); + if (len < bestLen) + { + bestAlias = sym; + bestLen = len; + } + } + for (; i < k; i++) + { + sym = SymLst->fetch (i); + sym->alias = bestAlias; + sym->size = maxSize; + } + i--; + } +} + +void +Stabs::check_Symtab () +{ + if (st_check_symtab) + return; + st_check_symtab = true; + + Elf *elf = openElf (true); + if (elf == NULL) + return; + if (elfDis->plt != 0) + { + Elf_Internal_Shdr *shdr = elfDis->get_shdr (elfDis->plt); + if (shdr) + { + pltSym = new Symbol (); + pltSym->value = shdr->sh_addr; + pltSym->size = shdr->sh_size; + pltSym->img_offset = shdr->sh_offset; + pltSym->name = dbe_strdup (NTXT ("@plt")); + pltSym->flags |= SYM_PLT; + } + } + if (elf->symtab) + readSymSec (elf->symtab, elf); + else + { + readSymSec (elf->SUNW_ldynsym, elf); + readSymSec (elf->dynsym, elf); + } +} + +void +Stabs::readSymSec (unsigned int sec, Elf *elf) +{ + Symbol *sitem; + Sp_lang_code local_lcode; + if (sec == 0) + return; + // Get ELF data + Elf_Data *data = elf->elf_getdata (sec); + if (data == NULL) + return; + uint64_t SymtabSize = data->d_size; + Elf_Internal_Shdr *shdr = elf->get_shdr (sec); + + if ((SymtabSize == 0) || (shdr->sh_entsize == 0)) + return; + Elf_Data *data_str = elf->elf_getdata (shdr->sh_link); + if (data_str == NULL) + return; + char *Strtab = (char *) data_str->d_buf; + + // read func symbolic table + for (unsigned int n = 0, tot = SymtabSize / shdr->sh_entsize; n < tot; n++) + { + Elf_Internal_Sym Sym; + elf->elf_getsym (data, n, &Sym); + const char *st_name = Sym.st_name < data_str->d_size ? + (Strtab + Sym.st_name) : NTXT ("no_name"); + switch (GELF_ST_TYPE (Sym.st_info)) + { + case STT_FUNC: + // Skip UNDEF symbols (bug 4817083) + if (Sym.st_shndx == 0) + { + if (Sym.st_value == 0) + break; + sitem = new Symbol (SymLst); + sitem->flags |= SYM_UNDEF; + if (pltSym) + sitem->img_offset = (uint32_t) (pltSym->img_offset + + Sym.st_value - pltSym->value); + } + else + { + Elf_Internal_Shdr *shdrp = elfDis->get_shdr (Sym.st_shndx); + if (shdrp == NULL) + break; + sitem = new Symbol (SymLst); + sitem->img_offset = (uint32_t) (shdrp->sh_offset + + Sym.st_value - shdrp->sh_addr); + } + sitem->size = Sym.st_size; + sitem->name = dbe_strdup (st_name); + sitem->value = is_relocatable () ? sitem->img_offset : Sym.st_value; + if (GELF_ST_BIND (Sym.st_info) == STB_LOCAL) + { + sitem->local_ind = LocalFile->size () - 1; + LocalLst->append (sitem); + } + break; + case STT_NOTYPE: + if (streq (st_name, NTXT ("gcc2_compiled."))) + { + sitem = new Symbol (SymLst); + sitem->lang_code = Sp_lang_gcc; + sitem->name = dbe_strdup (st_name); + sitem->local_ind = LocalFile->size () - 1; + LocalLst->append (sitem); + } + break; + case STT_OBJECT: + if (!strncmp (st_name, NTXT ("__KAI_KPTS_"), 11)) + local_lcode = Sp_lang_KAI_KPTS; + else if (!strncmp (st_name, NTXT ("__KAI_KCC_"), 10)) + local_lcode = Sp_lang_KAI_KCC; + else if (!strncmp (st_name, NTXT ("__KAI_Kcc_"), 10)) + local_lcode = Sp_lang_KAI_Kcc; + else + break; + sitem = new Symbol (LocalLst); + sitem->lang_code = local_lcode; + sitem->name = dbe_strdup (st_name); + break; + case STT_FILE: + { + int last = LocalFile->size () - 1; + if (last >= 0 && LocalFileIdx->fetch (last) == LocalLst->size ()) + { + // There were no local functions in the latest file. + free (LocalFile->get (last)); + LocalFile->store (last, dbe_strdup (st_name)); + } + else + { + LocalFile->append (dbe_strdup (st_name)); + LocalFileIdx->append (LocalLst->size ()); + } + break; + } + } + } + fixSymtabAlias (); + SymLst->sort (SymValueCmp); + get_save_addr (elf->need_swap_endian); + dump (); +}//check_Symtab + +void +Stabs::check_Relocs () +{ + // We may have many relocation tables to process: .rela.text%foo, + // rela.text%bar, etc. On Intel, compilers generate .rel.text sections + // which have to be processed as well. A lot of rework is needed here. + Symbol *sptr = NULL; + if (st_check_relocs) + return; + st_check_relocs = true; + + Elf *elf = openElf (false); + if (elf == NULL) + return; + for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++) + { + bool use_rela, use_PLT; + char *name = elf->get_sec_name (sec); + if (name == NULL) + continue; + if (strncmp (name, NTXT (".rela.text"), 10) == 0) + { + use_rela = true; + use_PLT = false; + } + else if (streq (name, NTXT (".rela.plt"))) + { + use_rela = true; + use_PLT = true; + } + else if (strncmp (name, NTXT (".rel.text"), 9) == 0) + { + use_rela = false; + use_PLT = false; + } + else if (streq (name, NTXT (".rel.plt"))) + { + use_rela = false; + use_PLT = true; + } + else + continue; + + Elf_Internal_Shdr *shdr = elf->get_shdr (sec); + if (shdr == NULL) + continue; + + // Get ELF data + Elf_Data *data = elf->elf_getdata (sec); + if (data == NULL) + continue; + uint64_t ScnSize = data->d_size; + uint64_t EntSize = shdr->sh_entsize; + if ((ScnSize == 0) || (EntSize == 0)) + continue; + int tot = (int) (ScnSize / EntSize); + + // Get corresponding text section + Elf_Internal_Shdr *shdr_txt = elf->get_shdr (shdr->sh_info); + if (shdr_txt == NULL) + continue; + if (!(shdr_txt->sh_flags & SHF_EXECINSTR)) + continue; + + // Get corresponding symbol table section + Elf_Internal_Shdr *shdr_sym = elf->get_shdr (shdr->sh_link); + if (shdr_sym == NULL) + continue; + Elf_Data *data_sym = elf->elf_getdata (shdr->sh_link); + + // Get corresponding string table section + Elf_Data *data_str = elf->elf_getdata (shdr_sym->sh_link); + if (data_str == NULL) + continue; + char *Strtab = (char*) data_str->d_buf; + for (int n = 0; n < tot; n++) + { + Elf_Internal_Sym sym; + Elf_Internal_Rela rela; + char *symName; + if (use_rela) + elf->elf_getrela (data, n, &rela); + else + { + // GElf_Rela is extended GElf_Rel + elf->elf_getrel (data, n, &rela); + rela.r_addend = 0; + } + + int ndx = (int) GELF_R_SYM (rela.r_info); + elf->elf_getsym (data_sym, ndx, &sym); + switch (GELF_ST_TYPE (sym.st_info)) + { + case STT_FUNC: + case STT_OBJECT: + case STT_NOTYPE: + if (sym.st_name == 0 || sym.st_name >= data_str->d_size) + continue; + symName = Strtab + sym.st_name; + break; + case STT_SECTION: + { + Elf_Internal_Shdr *secHdr = elf->get_shdr (sym.st_shndx); + if (secHdr == NULL) + continue; + if (sptr == NULL) + sptr = new Symbol; + sptr->value = secHdr->sh_offset + rela.r_addend; + long index = SymLst->bisearch (0, -1, &sptr, SymFindCmp); + if (index == -1) + continue; + Symbol *sp = SymLst->fetch (index); + if (sptr->value != sp->value) + continue; + symName = sp->name; + break; + } + default: + continue; + } + Reloc *reloc = new Reloc; + reloc->name = dbe_strdup (symName); + reloc->type = GELF_R_TYPE (rela.r_info); + reloc->value = use_PLT ? rela.r_offset + : rela.r_offset + shdr_txt->sh_offset; + reloc->addend = rela.r_addend; + if (use_PLT) + RelPLTLst->append (reloc); + else + RelLst->append (reloc); + } + } + delete sptr; + RelLst->sort (RelValueCmp); +} //check_Relocs + +void +Stabs::get_save_addr (bool need_swap_endian) +{ + if (elfDis->is_Intel ()) + { + for (int j = 0, sz = SymLst ? SymLst->size () : 0; j < sz; j++) + { + Symbol *sitem = SymLst->fetch (j); + sitem->save = 0; + } + return; + } + for (int j = 0, sz = SymLst ? SymLst->size () : 0; j < sz; j++) + { + Symbol *sitem = SymLst->fetch (j); + sitem->save = FUNC_NO_SAVE; + + // If an image offset is not known skip it. + // Works for artificial symbols like '@plt' as well. + if (sitem->img_offset == 0) + continue; + + bool is_o7_moved = false; + int64_t off = sitem->img_offset; + for (int i = 0; i < sitem->size; i += 4) + { + unsigned int cmd; + if (elfDis->get_data (off, sizeof (cmd), &cmd) == NULL) + break; + if (need_swap_endian) + SWAP_ENDIAN (cmd); + off += sizeof (cmd); + if ((cmd & 0xffffc000) == 0x9de38000) + { // save %sp, ??, %sp + sitem->save = i; + break; + } + else if ((cmd & 0xc0000000) == 0x40000000 || // call ?? + (cmd & 0xfff80000) == 0xbfc00000) + { // jmpl ??, %o7 + if (!is_o7_moved) + { + sitem->save = FUNC_ROOT; + break; + } + } + else if ((cmd & 0xc1ffe01f) == 0x8010000f) // or %g0,%o7,?? + is_o7_moved = true; + } + } +} + +uint64_t +Stabs::mapOffsetToAddress (uint64_t img_offset) +{ + Elf *elf = openElf (false); + if (elf == NULL) + return 0; + if (is_relocatable ()) + return img_offset; + for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++) + { + Elf_Internal_Shdr *shdr = elf->get_shdr (sec); + if (shdr == NULL) + continue; + if (img_offset >= (uint64_t) shdr->sh_offset + && img_offset < (uint64_t) (shdr->sh_offset + shdr->sh_size)) + return shdr->sh_addr + (img_offset - shdr->sh_offset); + } + return 0; +} + +Stabs::Stab_status +Stabs::archive_Stabs (LoadObject *lo, unsigned int StabSec, + unsigned int StabStrSec, bool comdat) +{ + StabReader *stabReader = new StabReader (openElf (true), platform, StabSec, StabStrSec); + int tot = stabReader->stabCnt; + if (tot < 0) + { + delete stabReader; + return DBGD_ERR_NO_STABS; + } + + char *sbase = NTXT (""), *arg, *fname, sname[2 * MAXPATHLEN]; + int lastMod, phase, stabs_level, modCnt = 0; + Function *func = NULL; + Module *mod; +#define INIT_MOD phase = 0; stabs_level = 0; *sname = '\0'; mod = NULL + + bool updateStabsMod = false; + if (comdat && ((elfDbg->elf_getehdr ()->e_type == ET_EXEC) || (elfDbg->elf_getehdr ()->e_type == ET_DYN))) + { + if (stabsModules == NULL) + stabsModules = new Vector<Module*>(); + updateStabsMod = true; + } + INIT_MOD; + lastMod = lo->seg_modules->size (); + + for (int n = 0; n < tot; n++) + { + struct stab stb; + char *str = stabReader->get_stab (&stb, comdat); + switch (stb.n_type) + { + case N_FUN: + // Ignore a COMDAT function, if there are two or more modules in 'lo' + if (comdat && stb.n_other == 1 && modCnt > 1) + break; + case N_OUTL: + case N_ALIAS: + case N_ENTRY: + if (mod == NULL || str == NULL + || (stb.n_type != N_ENTRY && stabs_level != 0)) + break; + if (*str == '@') + { + str++; + if (*str == '>' || *str == '<') + str++; + } + + fname = dbe_strdup (str); + arg = strchr (fname, ':'); + if (arg != NULL) + { + if (!strncmp (arg, NTXT (":P"), 2)) + { // just prototype + free (fname); + break; + } + *arg = '\0'; + } + + func = append_Function (mod, fname); + free (fname); + break; + case N_CMDLINE: + if (str && mod) + { + char *comp_flags = strchr (str, ';'); + if (comp_flags) + { + mod->comp_flags = dbe_strdup (comp_flags + 1); + mod->comp_dir = dbe_strndup (str, comp_flags - str); + } + } + break; + case N_LBRAC: + stabs_level++; + break; + case N_RBRAC: + stabs_level--; + break; + case N_UNDF: + INIT_MOD; + break; + case N_ENDM: + INIT_MOD; + break; + case N_OPT: + stabReader->parse_N_OPT (mod, str); + if (mod && (str != NULL) && streq (str, NTXT ("gcc2_compiled."))) + // Is it anachronism ? + mod->lang_code = Sp_lang_gcc; + break; + case N_GSYM: + if (mod && (str != NULL)) + { + if (strncmp (str, NTXT ("__KAI_K"), 7)) + break; + str += 7; + if (!strncmp (str, NTXT ("CC_"), 3)) + mod->lang_code = Sp_lang_KAI_KCC; + else if (!strncmp (str, NTXT ("cc_"), 3)) + mod->lang_code = Sp_lang_KAI_Kcc; + else if (!strncmp (str, NTXT ("PTS_"), 4) && + (mod->lang_code != Sp_lang_KAI_KCC) && + (mod->lang_code != Sp_lang_KAI_Kcc)) + mod->lang_code = Sp_lang_KAI_KPTS; + } + break; + case N_SO: + if (str == NULL || *str == '\0') + { + INIT_MOD; + break; + } + if (phase == 0) + { + phase = 1; + sbase = str; + } + else + { + if (*str == '/') + sbase = str; + else + { + size_t last = strlen (sbase); + if (last == 0 || sbase[last - 1] != '/') + snprintf (sname, sizeof (sname), NTXT ("%s/%s"), sbase, str); + else + snprintf (sname, sizeof (sname), NTXT ("%s%s"), sbase, str); + sbase = sname; + } + mod = append_Module (lo, sbase, lastMod); + if (updateStabsMod) + stabsModules->append (mod); + mod->hasStabs = true; + modCnt++; + if ((mod->lang_code != Sp_lang_gcc) && + (mod->lang_code != Sp_lang_KAI_KPTS) && + (mod->lang_code != Sp_lang_KAI_KCC) && + (mod->lang_code != Sp_lang_KAI_Kcc)) + mod->lang_code = (Sp_lang_code) stb.n_desc; + *sname = '\0'; + phase = 0; + } + break; + case N_OBJ: + if (str == NULL) + break; + if (phase == 0) + { + phase = 1; + sbase = str; + } + else + { + if (*str == '/') + sbase = str; + else + { + size_t last = strlen (sbase); + if (last == 0 || sbase[last - 1] != '/') + snprintf (sname, sizeof (sname), NTXT ("%s/%s"), sbase, str); + else + snprintf (sname, sizeof (sname), NTXT ("%s%s"), sbase, str); + sbase = sname; + } + if (mod && (mod->dot_o_file == NULL)) + { + if (strcmp (sbase, NTXT ("/")) == 0) + mod->set_name (dbe_strdup (path)); + else + { + mod->set_name (dbe_strdup (sbase)); + mod->dot_o_file = mod->createLoadObject (sbase); + } + } + *sname = '\0'; + phase = 0; + } + break; + case N_CPROF: + cpf_stabs_t map; + Dprintf (DEBUG_STABS, NTXT ("N_CPROF n_desc=%x n_value=0x%04x mod=%s\n"), + stb.n_desc, stb.n_value, (mod == NULL) ? NTXT ("???") : mod->get_name ()); + map.type = stb.n_desc; + map.offset = stb.n_value; + map.module = mod; + analyzerInfoMap.append (map); + break; + } + } + delete stabReader; + return func ? DBGD_ERR_NONE : DBGD_ERR_NO_STABS; +} + +Module * +Stabs::append_Module (LoadObject *lo, char *name, int lastMod) +{ + Module *module; + int size; + Symbol *sitem; + + if (lo->seg_modules != NULL) + { + size = lo->seg_modules->size (); + if (size < lastMod) + lastMod = size; + for (int i = 0; i < lastMod; i++) + { + module = lo->seg_modules->fetch (i); + if (module->linkerStabName && streq (module->linkerStabName, name)) + return module; + } + } + module = dbeSession->createModule (lo, NULL); + module->set_file_name (dbe_strdup (name)); + module->linkerStabName = dbe_strdup (module->file_name); + + // Append all functions with 'local_ind == -1' to the module. + if (LocalLst->size () > 0) + { + sitem = LocalLst->fetch (0); + if (!sitem->defined && sitem->local_ind == -1) + // Append all functions with 'local_ind == -1' to the module. + append_local_funcs (module, 0); + } + + // Append local func + char *basename = get_basename (name); + size = LocalFile->size (); + for (int i = 0; i < size; i++) + { + if (streq (basename, LocalFile->fetch (i))) + { + int local_ind = LocalFileIdx->fetch (i); + if (local_ind >= LocalLst->size ()) + break; + sitem = LocalLst->fetch (local_ind); + if (!sitem->defined) + { + append_local_funcs (module, local_ind); + break; + } + } + } + return module; +} + +void +Stabs::append_local_funcs (Module *module, int first_ind) +{ + Symbol *sitem = LocalLst->fetch (first_ind); + int local_ind = sitem->local_ind; + int size = LocalLst->size (); + for (int i = first_ind; i < size; i++) + { + sitem = LocalLst->fetch (i); + if (sitem->local_ind != local_ind) + break; + sitem->defined = true; + + // 3rd party compiled. e.g., Gcc or KAI compiled + if (sitem->lang_code != Sp_lang_unknown) + { + if (module->lang_code == Sp_lang_unknown) + module->lang_code = sitem->lang_code; + continue; + } + if (sitem->func) + continue; + Function *func = dbeSession->createFunction (); + sitem->func = func; + func->img_fname = path; + func->img_offset = (off_t) sitem->img_offset; + func->save_addr = (uint32_t) sitem->save; + func->size = (uint32_t) sitem->size; + func->module = module; + func->set_name (sitem->name); + module->functions->append (func); + module->loadobject->functions->append (func); + } +} + +Function * +Stabs::append_Function (Module *module, char *fname) +{ + Symbol *sitem, *sptr; + Function *func; + long sid, index; + char *name; + if (SymLstByName == NULL) + { + SymLstByName = SymLst->copy (); + SymLstByName->sort (SymNameCmp); + } + sptr = new Symbol; + if (module->lang_code == N_SO_FORTRAN || module->lang_code == N_SO_FORTRAN90) + { + char *fortran = dbe_sprintf (NTXT ("%s_"), fname); // FORTRAN name + sptr->name = fortran; + sid = SymLstByName->bisearch (0, -1, &sptr, SymNameCmp); + if (sid == -1) + { + free (fortran); + sptr->name = fname; + sid = SymLstByName->bisearch (0, -1, &sptr, SymNameCmp); + } + else + fname = fortran; + } + else + { + sptr->name = fname; + sid = SymLstByName->bisearch (0, -1, &sptr, SymNameCmp); + } + sptr->name = NULL; + delete sptr; + + if (sid == -1) + { + Vec_loop (Symbol*, SymLstByName, index, sitem) + { + if (strncmp (sitem->name, NTXT ("$X"), 2) == 0 + || strncmp (sitem->name, NTXT (".X"), 2) == 0) + { + char *n = strchr (((sitem->name) + 2), (int) '.'); + if (n != NULL) + name = n + 1; + else + name = sitem->name; + } + else + name = sitem->name; + if (name != NULL && fname != NULL && (strcmp (name, fname) == 0)) + { + sid = index; + break; + } + } + } + if (sid != -1) + { + sitem = SymLstByName->fetch (sid); + if (sitem->alias) + sitem = sitem->alias; + if (sitem->func) + return sitem->func; + sitem->func = func = dbeSession->createFunction (); + func->img_fname = path; + func->img_offset = (off_t) sitem->img_offset; + func->save_addr = (uint32_t) sitem->save; + func->size = (uint32_t) sitem->size; + } + else + func = dbeSession->createFunction (); + + func->module = module; + func->set_name (fname); + module->functions->append (func); + module->loadobject->functions->append (func); + return func; +} + +Function * +Stabs::append_Function (Module *module, char *linkerName, uint64_t pc) +{ + Dprintf (DEBUG_STABS, NTXT ("Stabs::append_Function: module=%s linkerName=%s pc=0x%llx\n"), + STR (module->get_name ()), STR (linkerName), (unsigned long long) pc); + long i; + Symbol *sitem = NULL, *sp; + Function *func; + sp = new Symbol; + if (pc) + { + sp->value = pc; + i = SymLst->bisearch (0, -1, &sp, SymFindCmp); + if (i != -1) + sitem = SymLst->fetch (i); + } + + if (!sitem && linkerName) + { + if (SymLstByName == NULL) + { + SymLstByName = SymLst->copy (); + SymLstByName->sort (SymNameCmp); + } + sp->name = linkerName; + i = SymLstByName->bisearch (0, -1, &sp, SymNameCmp); + sp->name = NULL; + if (i != -1) + sitem = SymLstByName->fetch (i); + } + delete sp; + + if (!sitem) + return NULL; + if (sitem->alias) + sitem = sitem->alias; + if (sitem->func) + return sitem->func; + + sitem->func = func = dbeSession->createFunction (); + func->img_fname = path; + func->img_offset = (off_t) sitem->img_offset; + func->save_addr = (uint32_t) sitem->save; + func->size = (uint32_t) sitem->size; + func->module = module; + func->set_name (sitem->name); //XXXX ?? Now call it to set obj->name + module->functions->append (func); + module->loadobject->functions->append (func); + return func; +}// Stabs::append_Function + +Dwarf * +Stabs::openDwarf () +{ + if (dwarf == NULL) + { + dwarf = new Dwarf (this); + check_Symtab (); + } + return dwarf; +} + +void +Stabs::read_hwcprof_info (Module *module) +{ + openDwarf ()->read_hwcprof_info (module); +} + +void +Stabs::dump () +{ + if (!DUMP_ELF_SYM) + return; + printf (NTXT ("\n======= Stabs::dump: %s =========\n"), path ? path : NTXT ("NULL")); + int i, sz; + if (LocalFile) + { + sz = LocalFile->size (); + for (i = 0; i < sz; i++) + printf (" %3d: %5d '%s'\n", i, LocalFileIdx->fetch (i), + LocalFile->fetch (i)); + } + Symbol::dump (SymLst, NTXT ("SymLst")); + Symbol::dump (LocalLst, NTXT ("LocalLst")); + printf (NTXT ("\n===== END of Stabs::dump: %s =========\n\n"), + path ? path : NTXT ("NULL")); +} + +/////////////////////////////////////////////////////////////////////////////// +// Class Include +Include::Include () +{ + stack = new Vector<SrcFileInfo*>; +} + +Include::~Include () +{ + Destroy (stack); +} + +void +Include::new_src_file (SourceFile *source, int lineno, Function *func) +{ + for (int index = stack->size () - 1; index >= 0; index--) + { + if (source == stack->fetch (index)->srcfile) + { + for (int i = stack->size () - 1; i > index; i--) + { + delete stack->remove (i); + if (func && func->line_first > 0) + func->popSrcFile (); + } + return; + } + } + if (func && func->line_first > 0) + func->pushSrcFile (source, lineno); + + SrcFileInfo *sfinfo = new SrcFileInfo; + sfinfo->srcfile = source; + sfinfo->lineno = lineno; + stack->append (sfinfo); +} + +void +Include::push_src_files (Function *func) +{ + int index; + SrcFileInfo *sfinfo; + + if (func->line_first <= 0 && stack->size () > 0) + { + sfinfo = stack->fetch (stack->size () - 1); + func->setDefSrc (sfinfo->srcfile); + } + Vec_loop (SrcFileInfo*, stack, index, sfinfo) + { + func->pushSrcFile (sfinfo->srcfile, sfinfo->lineno); + } +} + +void +Include::new_include_file (SourceFile *source, Function *func) +{ + if (stack->size () == 1 && stack->fetch (0)->srcfile == source) + // workaroud for gcc; gcc creates 'N_BINCL' stab for main source + return; + if (func && func->line_first > 0) + func->pushSrcFile (source, 0); + + SrcFileInfo *sfinfo = new SrcFileInfo; + sfinfo->srcfile = source; + sfinfo->lineno = 0; + stack->append (sfinfo); +} + +void +Include::end_include_file (Function *func) +{ + int index = stack->size () - 1; + if (index > 0) + { + delete stack->remove (index); + if (func && func->line_first > 0) + func->popSrcFile (); + } +} + +#define RET_S(x) if (t == x) return (char *) #x +char * +StabReader::get_type_name (int t) +{ + RET_S (N_UNDF); + RET_S (N_ABS); + RET_S (N_TEXT); + RET_S (N_DATA); + RET_S (N_BSS); + RET_S (N_COMM); + RET_S (N_FN); + RET_S (N_EXT); + RET_S (N_TYPE); + RET_S (N_GSYM); + RET_S (N_FNAME); + RET_S (N_FUN); + RET_S (N_OUTL); + RET_S (N_STSYM); + RET_S (N_TSTSYM); + RET_S (N_LCSYM); + RET_S (N_TLCSYM); + RET_S (N_MAIN); + RET_S (N_ROSYM); + RET_S (N_FLSYM); + RET_S (N_TFLSYM); + RET_S (N_PC); + RET_S (N_CMDLINE); + RET_S (N_OBJ); + RET_S (N_OPT); + RET_S (N_RSYM); + RET_S (N_SLINE); + RET_S (N_XLINE); + RET_S (N_ILDPAD); + RET_S (N_SSYM); + RET_S (N_ENDM); + RET_S (N_SO); + RET_S (N_MOD); + RET_S (N_EMOD); + RET_S (N_READ_MOD); + RET_S (N_ALIAS); + RET_S (N_LSYM); + RET_S (N_BINCL); + RET_S (N_SOL); + RET_S (N_PSYM); + RET_S (N_EINCL); + RET_S (N_ENTRY); + RET_S (N_SINCL); + RET_S (N_LBRAC); + RET_S (N_EXCL); + RET_S (N_USING); + RET_S (N_ISYM); + RET_S (N_ESYM); + RET_S (N_PATCH); + RET_S (N_CONSTRUCT); + RET_S (N_DESTRUCT); + RET_S (N_CODETAG); + RET_S (N_FUN_CHILD); + RET_S (N_RBRAC); + RET_S (N_BCOMM); + RET_S (N_TCOMM); + RET_S (N_ECOMM); + RET_S (N_XCOMM); + RET_S (N_ECOML); + RET_S (N_WITH); + RET_S (N_LENG); + RET_S (N_CPROF); + RET_S (N_BROWS); + RET_S (N_FUN_PURE); + RET_S (N_FUN_ELEMENTAL); + RET_S (N_FUN_RECURSIVE); + RET_S (N_FUN_AMD64_PARMDUMP); + RET_S (N_SYM_OMP_TLS); + RET_S (N_SO_AS); + RET_S (N_SO_C); + RET_S (N_SO_ANSI_C); + RET_S (N_SO_CC); + RET_S (N_SO_FORTRAN); + RET_S (N_SO_FORTRAN77); + RET_S (N_SO_PASCAL); + RET_S (N_SO_FORTRAN90); + RET_S (N_SO_JAVA); + RET_S (N_SO_C99); + return NULL; +} diff --git a/gprofng/src/Stabs.h b/gprofng/src/Stabs.h new file mode 100644 index 0000000..1a7443a --- /dev/null +++ b/gprofng/src/Stabs.h @@ -0,0 +1,160 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _STABS_H +#define _STABS_H + +#include "dbe_structs.h" +#include "vec.h" + +enum cpf_instr_type_t { + CPF_INSTR_TYPE_LD = 0, // profiled load instruction + CPF_INSTR_TYPE_ST, // profiled store instruction + CPF_INSTR_TYPE_PREFETCH, // profiled prefetch instruction + CPF_INSTR_TYPE_BRTARGET, // branch target + CPF_INSTR_TYPE_UNKNOWN, // unidentified instruction + CPF_INSTR_TYPE_NTYPES // total # of instr types +}; + +class Function; +class LoadObject; +class Module; +class ComC; +class Elf; +class Dwarf; +class Symbol; +class Reloc; +struct cpf_stabs_t; +class SourceFile; +template <typename Key_t, typename Value_t> class Map; + +class Include { + public: + typedef struct { + SourceFile *srcfile; + int lineno; + } SrcFileInfo; + Include(); + ~Include(); + void new_src_file(SourceFile *source, int lineno, Function *func = NULL); + void new_include_file(SourceFile *source, Function *func); + void end_include_file(Function *func); + void push_src_files(Function *func); + + private: + Vector<SrcFileInfo*> *stack; +}; + +// Stabs object +class Stabs { + public: + + enum Stab_status { + DBGD_ERR_NONE, + DBGD_ERR_CANT_OPEN_FILE, + DBGD_ERR_BAD_ELF_LIB, + DBGD_ERR_BAD_ELF_FORMAT, + DBGD_ERR_NO_STABS, + DBGD_ERR_BAD_STABS, + DBGD_ERR_NO_DWARF, + DBGD_ERR_CHK_SUM + }; + + static Stabs *NewStabs(char *_path, char *lo_name); + Stabs(char *_path, char *_lo_name); + ~Stabs(); + + bool is_relocatable(){ return isRelocatable; } + long long get_textsz() { return textsz; } + Platform_t get_platform() { return platform; } + WSize_t get_class() { return wsize;} + Stab_status get_status() { return status;} + + Stab_status read_stabs(ino64_t srcInode, Module *module, Vector<ComC*> *comComs, bool readDwarf = false); + Stab_status read_archive(LoadObject *lo); + bool read_symbols(Vector<Function*> *functions); + uint64_t mapOffsetToAddress(uint64_t img_offset); + char *sym_name(uint64_t target, uint64_t instr, int flag); + Elf *openElf (bool dbg_info = false); + void read_hwcprof_info(Module *module); + void dump(); + void read_dwarf_from_dot_o(Module *mod); + + static bool is_fortran(Sp_lang_code lc) { return (lc == Sp_lang_fortran) || (lc == Sp_lang_fortran90); } + static Function *find_func(char *fname, Vector<Function*> *functions, bool fortran, bool inner_names=false); + Module *append_Module(LoadObject *lo, char *name, int lastMod = 0); + Function *append_Function(Module *module, char *fname); + Function *append_Function(Module *module, char *linkerName, uint64_t pc); + Function *map_PC_to_func(uint64_t pc, uint64_t &low_pc, Vector<Function*> *functions); + char *path; // path to the object file + char *lo_name; // User name of load object + + private: + Elf *elfDbg; // ELF with debug info + Elf *elfDis; // ELF for disasm + Stab_status status; // current stabs status + + long long textsz; // text segment size + Platform_t platform; // Sparc, Sparcv9, Intel + WSize_t wsize; // word size: 32 or 64 + bool isRelocatable; + Symbol *last_PC_to_sym; + + Vector<cpf_stabs_t> analyzerInfoMap; // stabs->section mapping + + bool check_Comm(Vector<ComC*> *comComs); + void check_Info(Vector<ComC*> *comComs); + void check_Loop(Vector<ComC*> *comComs); + void check_AnalyzerInfo(); + void append_local_funcs(Module *module, int first_ind); + Stab_status srcline_Stabs (Module *module, unsigned int StabSec, unsigned int StabStrSec, bool comdat); + Stab_status archive_Stabs (LoadObject *lo, unsigned int StabSec, unsigned int StabStrSec, bool comdat); + + // Interface with Elf Symbol Table + void check_Symtab(); + void readSymSec(unsigned int sec, Elf *elf); + void check_Relocs(); + void get_save_addr(bool need_swap_endian); + Symbol *map_PC_to_sym(uint64_t pc); + Symbol *pltSym; + Vector<Symbol*> *SymLst; // list of func symbols + Vector<Symbol*> *SymLstByName; // list of func symbols sorted by Name + Vector<Reloc*> *RelLst; // list of text relocations + Vector<Reloc*> *RelPLTLst; // list of PLT relocations + Vector<Symbol*> *LocalLst; // list of local func symbols + Vector<char*> *LocalFile; // list of local files + Vector<int> *LocalFileIdx; // start index in LocalLst + + Elf *openElf(char *fname, Stab_status &st); + Map<const char*, Symbol*> *get_elf_symbols(); + Dwarf *dwarf; + + bool st_check_symtab, st_check_relocs; + Function *createFunction(LoadObject *lo, Module *module, Symbol *sym); + void fixSymtabAlias(); + + // Interface with dwarf + Dwarf *openDwarf(); + + Vector<Module*> *stabsModules; + static char *get_type_name(int t); +}; + +#endif /* _STABS_H */ diff --git a/gprofng/src/Stats_data.cc b/gprofng/src/Stats_data.cc new file mode 100644 index 0000000..2595bc7 --- /dev/null +++ b/gprofng/src/Stats_data.cc @@ -0,0 +1,203 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +//#include <search.h> // For the tsearch stuff +#include <assert.h> +//#include <stdlib.h> +#include "Table.h" +#include "Sample.h" +#include "Stats_data.h" +#include "util.h" +#include "i18n.h" + +//XXX: The fundamental problem with this package of routines is that +//XXX: they should look a lot more like overview data. The result +//XXX: is that it is not possible to share as much code as would +//XXX: otherwise be possible. + +int +Stats_data::size () +{ + // Return the number of Stats_item values associated with "this". + if (stats_items == NULL) + return 0; + return stats_items->size (); +} + +Stats_data::Stats_item +Stats_data::fetch (int index) +{ + // Routine will return the "index"'th Stats_item associated with "this". + assert (index >= 0 && index < stats_items->size ()); + return *(stats_items->fetch (index)); +} + +Stats_data::Stats_data () +{ + // this constructor for use with sum() + packets = NULL; + stats_items = NULL; +} + +Stats_data::Stats_data (DataView *_packets) +{ + packets = _packets; + stats_items = NULL; + compute_data (); // reads all data +} + +Stats_data::~Stats_data () +{ + if (stats_items) + { + stats_items->destroy (); + delete stats_items; + } +} + +void +Stats_data::sum (Stats_data *data) +{ + int index; + Stats_item *stats_item, *data_item; + if (stats_items == NULL) + { + stats_items = new Vector<Stats_item*>; + Vec_loop (Stats_item*, data->stats_items, index, data_item) + { + stats_item = create_stats_item (data_item->value.ll, data_item->label); + stats_items->append (stats_item); + } + } + else + { + Vec_loop (Stats_item*, data->stats_items, index, data_item) + { + stats_items->fetch (index)->value.ll += data_item->value.ll; + } + } +} + +Stats_data::Stats_item * +Stats_data::create_stats_item (long long v, char *l) +{ + Stats_data::Stats_item *st_it; + st_it = new Stats_data::Stats_item; + st_it->label = l; + st_it->value.sign = false; + st_it->value.ll = v; + st_it->value.tag = VT_LLONG; + return st_it; +} + +PrUsage * +Stats_data::fetchPrUsage (long index) +{ + // Return the data values corresponding to the "index"'th sample. + PrUsage *prusage; + if (packets->getSize () > 0) + { + Sample* sample = (Sample*) packets->getObjValue (PROP_SMPLOBJ, index); + prusage = sample->get_usage (); + if (prusage != NULL) + return prusage; + } + return new PrUsage; +} + +void +Stats_data::compute_data () +{ + Stats_data::Stats_item *stats_item; + PrUsage *tots, *temp; + stats_items = new Vector<Stats_data::Stats_item*>; + + // Precomputation is needed. + long size = packets->getSize (); + tots = new PrUsage (); + for (long index = 0; index < size; index++) + { + temp = fetchPrUsage (index); + tots->pr_tstamp += temp->pr_tstamp; + tots->pr_create += temp->pr_create; + tots->pr_term += temp->pr_term; + tots->pr_rtime += temp->pr_rtime; + tots->pr_utime += temp->pr_utime; + tots->pr_stime += temp->pr_stime; + tots->pr_ttime += temp->pr_ttime; + tots->pr_tftime += temp->pr_tftime; + tots->pr_dftime += temp->pr_dftime; + tots->pr_kftime += temp->pr_kftime; + tots->pr_slptime += temp->pr_slptime; + tots->pr_ltime += temp->pr_ltime; + tots->pr_wtime += temp->pr_wtime; + tots->pr_stoptime += temp->pr_stoptime; + tots->pr_minf += temp->pr_minf; + tots->pr_majf += temp->pr_majf; + tots->pr_nswap += temp->pr_nswap; + tots->pr_inblk += temp->pr_inblk; + tots->pr_oublk += temp->pr_oublk; + tots->pr_msnd += temp->pr_msnd; + tots->pr_mrcv += temp->pr_mrcv; + tots->pr_sigs += temp->pr_sigs; + tots->pr_vctx += temp->pr_vctx; + tots->pr_ictx += temp->pr_ictx; + tots->pr_sysc += temp->pr_sysc; + tots->pr_ioch += temp->pr_ioch; + } + stats_item = create_stats_item ((long long) tots->pr_minf, + GTXT ("Minor Page Faults")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_majf, + GTXT ("Major Page Faults")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_nswap, + GTXT ("Process swaps")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_inblk, + GTXT ("Input blocks")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_oublk, + GTXT ("Output blocks")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_msnd, + GTXT ("Messages sent")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_mrcv, + GTXT ("Messages received")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_sigs, + GTXT ("Signals handled")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_vctx, + GTXT ("Voluntary context switches")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_ictx, + GTXT ("Involuntary context switches")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_sysc, + GTXT ("System calls")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_ioch, + GTXT ("Characters of I/O")); + stats_items->append (stats_item); + delete tots; +} diff --git a/gprofng/src/Stats_data.h b/gprofng/src/Stats_data.h new file mode 100644 index 0000000..2f6b6a7 --- /dev/null +++ b/gprofng/src/Stats_data.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _STATS_DATA_H +#define _STATS_DATA_H + +// A Stats_data object is used to obtain the data needed to display +// a statistics display. + +#include "vec.h" +#include "Exp_Layout.h" + +class DataView; + +class Stats_data +{ +public: + + struct Stats_item + { + char *label; // statistic label + TValue value; // statistic value + }; + + Stats_data (); + Stats_data (DataView *packets); + ~Stats_data (); + int size (); // Return the total number of items. + Stats_item fetch (int index); + void sum (Stats_data *data); + +private: + + PrUsage * fetchPrUsage (long index); + void compute_data (); // Perform any initial computation. + Stats_data::Stats_item *create_stats_item (long long, char *); + + Vector<Stats_item*> *stats_items; // Actual statistics values + DataView *packets; +}; + +#endif /* _STATS_DATA_H */ diff --git a/gprofng/src/StringBuilder.cc b/gprofng/src/StringBuilder.cc new file mode 100644 index 0000000..a9656d9 --- /dev/null +++ b/gprofng/src/StringBuilder.cc @@ -0,0 +1,585 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <values.h> +#include <stdarg.h> + +#include "gp-defs.h" +#include "StringBuilder.h" +#include "i18n.h" + +StringBuilder::StringBuilder () +{ + count = 0; + maxCapacity = 16; + value = (char *) malloc (maxCapacity); + memset (value, 0, maxCapacity); +} + +StringBuilder::StringBuilder (int capacity) +{ + count = 0; + maxCapacity = capacity; + value = (char *) malloc (maxCapacity); + memset (value, 0, maxCapacity); +} + +StringBuilder::~StringBuilder () +{ + free (value); +} + +void +StringBuilder::ensureCapacity (int minimumCapacity) +{ + if (minimumCapacity > maxCapacity) + expandCapacity (minimumCapacity); +} + +void +StringBuilder::expandCapacity (int minimumCapacity) +{ + int newCapacity = (maxCapacity + 1) * 2; + if (newCapacity < 0) + newCapacity = MAXINT; + else if (minimumCapacity > newCapacity) + newCapacity = minimumCapacity; + char *newValue = (char *) malloc (newCapacity); + maxCapacity = newCapacity; + memcpy (newValue, value, count); + memset (newValue + count, 0, maxCapacity - count); + free (value); + value = newValue; +} + +void +StringBuilder::trimToSize () +{ + if (count < maxCapacity) + { + char *newValue = (char *) malloc (count); + maxCapacity = count; + memcpy (newValue, value, count); + free (value); + value = newValue; + } +} + +void +StringBuilder::trim () +{ + while (count > 0) + { + if (value[count - 1] != ' ') + break; + count--; + } +} + +void +StringBuilder::setLength (int newLength) +{ + if (newLength < 0) + return; + if (newLength > maxCapacity) + expandCapacity (newLength); + if (count < newLength) + { + for (; count < newLength; count++) + value[count] = '\0'; + } + else + count = newLength; +} + +char +StringBuilder::charAt (int index) +{ + if (index < 0 || index >= count) + return 0; + return value[index]; +} + +void +StringBuilder::getChars (int srcBegin, int srcEnd, char dst[], int dstBegin) +{ + if (srcBegin < 0) + return; + if (srcEnd < 0 || srcEnd > count) + return; + if (srcBegin > srcEnd) + return; + memcpy (dst + dstBegin, value + srcBegin, srcEnd - srcBegin); +} + +void +StringBuilder::setCharAt (int index, char ch) +{ + if (index < 0 || index >= count) + return; + value[index] = ch; +} + +StringBuilder * +StringBuilder::append (StringBuilder *sb) +{ + if (sb == NULL) + return append (NTXT ("null")); + int len = sb->count; + int newcount = count + len; + if (newcount > maxCapacity) + expandCapacity (newcount); + sb->getChars (0, len, value, count); + count = newcount; + return this; +} + +StringBuilder * +StringBuilder::append (const char str[]) +{ + int len = (int) strlen (str); + int newCount = count + len; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + count, str, len); + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::append (const char str[], int offset, int len) +{ + int newCount = count + len; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + count, str + offset, len); + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::append (bool b) +{ + if (b) + append (NTXT ("true")); + else + append (NTXT ("false")); + return this; +} + +StringBuilder * +StringBuilder::append (char c) +{ + int newCount = count + 1; + if (newCount > maxCapacity) + { + expandCapacity (newCount); + } + value[count++] = c; + return this; +} + +StringBuilder * +StringBuilder::append (int i) +{ + char buf[16]; + snprintf (buf, sizeof (buf), NTXT ("%d"), i); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (unsigned int i) +{ + char buf[16]; + snprintf (buf, sizeof (buf), NTXT ("%u"), i); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (long lng) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%ld"), lng); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (unsigned long lng) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%lu"), lng); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (long long lng) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%lld"), lng); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (unsigned long long lng) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%llu"), lng); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (float f) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%f"), (double) f); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (double d) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%f"), d); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::_delete (int start, int end) +{ + if (start < 0) + return this; + if (end > count) + end = count; + if (start > end) + return this; + int len = end - start; + if (len > 0) + { + memcpy (value + start, value + start + len, count - end); + count -= len; + } + return this; +} + +StringBuilder * +StringBuilder::deleteCharAt (int index) +{ + if (index < 0 || index >= count) + return this; + memcpy (value + index, value + index + 1, count - index - 1); + count--; + return this; +} + +bool +StringBuilder::endsWith (const char str[]) +{ + if (str == NULL) + { + if (count == 0) + return true; + return false; + } + int len = (int) strlen (str); + if (len == 0) + return true; + int start = count - len; + if (start < 0) + return false; + int res = strncmp ((const char *) (value + start), str, len); + if (res != 0) + return false; + return true; +} + +StringBuilder * +StringBuilder::insert (int index, const char str[], int offset, int len) +{ + if (index < 0 || index > count) + return this; + if (offset < 0 || len < 0 || offset > ((int) strlen (str)) - len) + return this; + int newCount = count + len; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + index + len, value + index, count - index); + memcpy (value + index, str + offset, len); + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, const char str[]) +{ + if (offset < 0 || offset > count) + return this; + int len = (int) strlen (str); + int newCount = count + len; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + offset + len, value + offset, count - offset); + memcpy (value + offset, str, len); + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, bool b) +{ + return insert (offset, b ? NTXT ("true") : NTXT ("false")); +} + +StringBuilder * +StringBuilder::insert (int offset, char c) +{ + int newCount = count + 1; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + offset + 1, value + offset, count - offset); + value[offset] = c; + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, int i) +{ + char buf[16]; + snprintf (buf, sizeof (buf), NTXT ("%d"), i); + insert (offset, buf); + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, long l) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%ld"), l); + insert (offset, buf); + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, float f) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%f"), (double) f); + insert (offset, buf); + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, double d) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%f"), d); + insert (offset, buf); + return this; +} + +StringBuilder * +StringBuilder::reverse () +{ + int n = count - 1; + for (int j = (n - 1) >> 1; j >= 0; --j) + { + char temp = value[j]; + char temp2 = value[n - j]; + value[j] = temp2; + value[n - j] = temp; + } + return this; +} + +//String *StringBuilder::toString(); +char * +StringBuilder::toString () +{ + char *str = (char *) malloc (count + 1); + memcpy (str, value, count); + str[count] = '\0'; + return str; +} + +void +StringBuilder::toFile (FILE *fp) +{ + append ('\0'); + count--; + fprintf (fp, NTXT ("%s"), value); +} + +void +StringBuilder::toFileLn (FILE *fp) +{ + trim (); + append ('\0'); + count--; + fprintf (fp, NTXT ("%s\n"), value); +} + +StringBuilder * +StringBuilder::sprintf (const char *fmt, ...) +{ + int cnt; + setLength (0); + + va_list vp; + va_start (vp, fmt); + cnt = vsnprintf (value, maxCapacity, fmt, vp); + va_end (vp); + if (cnt < maxCapacity) + { + count = cnt; + return this; + } + + // Have to count the trailing zero + ensureCapacity (cnt + 1); + va_start (vp, fmt); + count = vsnprintf (value, maxCapacity, fmt, vp); + va_end (vp); + return this; +} + +StringBuilder * +StringBuilder::appendf (const char *fmt, ...) +{ + va_list vp; + va_start (vp, fmt); + int cnt = vsnprintf (value + count, maxCapacity - count, fmt, vp); + va_end (vp); + if (cnt + count < maxCapacity) + { + count += cnt; + return this; + } + + // Have to count the trailing zero + ensureCapacity (count + cnt + 1); + va_start (vp, fmt); + count += vsnprintf (value + count, maxCapacity - count, fmt, vp); + va_end (vp); + return this; +} + +int +StringBuilder::indexOf (const char str[]) +{ + return indexOf (str, 0); +} + +int +StringBuilder::indexOf (const char str[], int fromIndex) +{ + int len = (int) strlen (str); + if (fromIndex >= count) + return len == 0 ? count : -1; + if (fromIndex < 0) + fromIndex = 0; + if (len == 0) + return fromIndex; + + char first = str[0]; + int max = (count - len); + + for (int i = fromIndex; i <= max; i++) + { + /* Look for first character. */ + if (value[i] != first) + while (++i <= max && value[i] != first) + ; + /* Found first character, now look at the rest of v2 */ + if (i <= max) + { + int j = i + 1; + int end = j + len - 1; + for (int k = 1; j < end && value[j] == str[k]; j++, k++) + ; + if (j == end) /* Found whole string. */ + return i; + } + } + return -1; +} + +int +StringBuilder::lastIndexOf (const char str[]) +{ + return lastIndexOf (str, count); +} + +int +StringBuilder::lastIndexOf (const char str[], int fromIndex) +{ + /* + * Check arguments; return immediately where possible. For + * consistency, don't check for null str. + */ + int len = (int) strlen (str); + int rightIndex = count - len; + if (fromIndex < 0) + return -1; + if (fromIndex > rightIndex) + fromIndex = rightIndex; + /* Empty string always matches. */ + if (len == 0) + return fromIndex; + + int strLastIndex = len - 1; + char strLastChar = str[strLastIndex]; + int min = len - 1; + int i = min + fromIndex; + + while (true) + { + while (i >= min && value[i] != strLastChar) + i--; + if (i < min) + return -1; + + int j = i - 1; + int start = j - (len - 1); + int k = strLastIndex - 1; + while (j > start) + { + if (value[j--] != str[k--]) + { + i--; + break; + } + } + if (j == start) + return start + 1; + } +} + diff --git a/gprofng/src/StringBuilder.h b/gprofng/src/StringBuilder.h new file mode 100644 index 0000000..5fe1a51 --- /dev/null +++ b/gprofng/src/StringBuilder.h @@ -0,0 +1,101 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * java/lang/StringBuilder + * + * Based on JavaTM 2 Platform Standard Ed. 5.0 + */ + +#ifndef _StringBuilder_h +#define _StringBuilder_h + +class StringBuilder +{ +public: + StringBuilder (); + StringBuilder (int capacity); + virtual ~StringBuilder (); + + int + length () + { + return count; + } + + int + capacity () + { + return maxCapacity; + } + + bool endsWith (const char str[]); + void ensureCapacity (int minimumCapacity); + void expandCapacity (int minimumCapacity); + void trimToSize (); + void trim (); + void setLength (int newLength); + char charAt (int index); + void getChars (int srcBegin, int srcEnd, char dst[], int dstBegin); + void setCharAt (int index, char ch); + StringBuilder *append (StringBuilder *sb); + StringBuilder *append (const char str[]); + StringBuilder *append (const char str[], int offset, int len); + StringBuilder *append (bool b); + StringBuilder *append (char c); + StringBuilder *append (int i); + StringBuilder *append (unsigned int i); + StringBuilder *append (long lng); + StringBuilder *append (unsigned long i); + StringBuilder *append (long long lng); + StringBuilder *append (unsigned long long lng); + StringBuilder *append (float f); + StringBuilder *append (double d); + StringBuilder *_delete (int start, int end); + StringBuilder *deleteCharAt (int index); + StringBuilder *insert (int index, const char str[], int offset, int len); + StringBuilder *insert (int offset, const char str[]); + StringBuilder *insert (int offset, bool b); + StringBuilder *insert (int offset, char c); + StringBuilder *insert (int offset, int i); + StringBuilder *insert (int offset, long l); + StringBuilder *insert (int offset, float f); + StringBuilder *insert (int offset, double d); + StringBuilder *reverse (); + char *toString (); + void toFile (FILE *fp); + void toFileLn (FILE *fp); + + // Not in Java + StringBuilder *appendf (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + StringBuilder *sprintf (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + int indexOf (const char str[]); + int indexOf (const char str[], int fromIndex); + int lastIndexOf (const char str[]); + int lastIndexOf (const char str[], int fromIndex); + +private: + char *value; + int count; + int maxCapacity; +}; + +#endif /* _StringBuilder_h */ diff --git a/gprofng/src/StringMap.h b/gprofng/src/StringMap.h new file mode 100644 index 0000000..31898f4 --- /dev/null +++ b/gprofng/src/StringMap.h @@ -0,0 +1,238 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * String Map implementation. + */ + +#ifndef _DBE_STRINGMAP_H +#define _DBE_STRINGMAP_H + +#include <assert.h> +#include <vec.h> +#include <Map.h> +#include <util.h> + +template <typename Value_t> +class StringMap : public Map<const char*, Value_t> +{ +public: + + StringMap (int htable_size = 1024, int chunk_size = 16384); + ~StringMap (); + void clear (); + void put (const char *key, Value_t val); + Value_t get (const char *key); + Value_t get (const char *key, typename Map<const char*, Value_t>::Relation rel); + Value_t remove (const char*); + Vector<const char*> *keySet (); + Vector<Value_t> *values (); + +private: + + static unsigned + hash (const char *key) + { + return (unsigned) crc64 (key, strlen (key)); + } + + struct Entry + { + char *key; + Value_t val; + }; + + int CHUNK_SIZE, HTABLE_SIZE; + int entries; + int nchunks; + Entry **chunks; + Vector<Entry*> *index; + Entry **hashTable; +}; + +template <typename Value_t> +StringMap<Value_t>::StringMap (int htable_size, int chunk_size) +{ + HTABLE_SIZE = htable_size; + CHUNK_SIZE = chunk_size; + entries = 0; + nchunks = 0; + chunks = NULL; + index = new Vector<Entry*>; + hashTable = new Entry*[HTABLE_SIZE]; + for (int i = 0; i < HTABLE_SIZE; i++) + hashTable[i] = NULL; +} + +template <typename Value_t> +StringMap<Value_t>::~StringMap () +{ + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + free (entry->key); + } + for (int i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; + delete index; + delete[] hashTable; +} + +template <typename Value_t> +void +StringMap<Value_t>::clear () +{ + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + free (entry->key); + } + entries = 0; + index->reset (); + for (int i = 0; i < HTABLE_SIZE; i++) + hashTable[i] = NULL; +} + +template <typename Value_t> +void +StringMap<Value_t>::put (const char *key, Value_t val) +{ + unsigned idx = hash (key) % HTABLE_SIZE; + Entry *entry = hashTable[idx]; + if (entry && strcmp (entry->key, key) == 0) + { + entry->val = val; + return; + } + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + entry = index->fetch (md); + int cmp = strcmp (entry->key, key); + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + entry->val = val; + return; + } + } + if (entries >= nchunks * CHUNK_SIZE) + { + nchunks++; + + // Reallocate Entry chunk array + Entry **new_chunks = new Entry*[nchunks]; + for (int i = 0; i < nchunks - 1; i++) + new_chunks[i] = chunks[i]; + delete[] chunks; + chunks = new_chunks; + + // Allocate new chunk for entries. + chunks[nchunks - 1] = new Entry[CHUNK_SIZE]; + } + entry = &chunks[entries / CHUNK_SIZE][entries % CHUNK_SIZE]; + entry->key = strdup (key); + entry->val = val; + index->insert (lo, entry); + hashTable[idx] = entry; + entries++; +} + +template <typename Value_t> +Value_t +StringMap<Value_t>::get (const char *key) +{ + unsigned idx = hash (key) % HTABLE_SIZE; + Entry *entry = hashTable[idx]; + if (entry && strcmp (entry->key, key) == 0) + return entry->val; + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + entry = index->fetch (md); + int cmp = strcmp (entry->key, key); + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + hashTable[idx] = entry; + return entry->val; + } + } + return (Value_t) 0; +} + +template <typename Value_t> +Value_t +StringMap<Value_t>::get (const char *key, typename Map<const char*, + Value_t>::Relation rel) +{ + if (rel != Map<const char*, Value_t>::REL_EQ) + return (Value_t) 0; + return get (key); +} + +template <typename Value_t> +Value_t +StringMap<Value_t>::remove (const char*) +{ + // Not implemented + if (1) + assert (0); + return (Value_t) 0; +} + +template <typename Value_t> +Vector<Value_t> * +StringMap<Value_t>::values () +{ + Vector<Value_t> *vals = new Vector<Value_t>(entries); + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + vals->append (entry->val); + } + return vals; +} + +template <typename Value_t> +Vector<const char*> * +StringMap<Value_t>::keySet () +{ + Vector<const char*> *keys = new Vector<const char*>(entries); + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + keys->append (entry->key); + } + return keys; +} + +#endif diff --git a/gprofng/src/Table.cc b/gprofng/src/Table.cc new file mode 100644 index 0000000..afe44bd --- /dev/null +++ b/gprofng/src/Table.cc @@ -0,0 +1,1687 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> + +#include "IndexMap2D.h" +#include "DbeSession.h" +#include "FilterExp.h" +#include "Table.h" +#include "util.h" +#include "i18n.h" + +char * +get_prof_data_type_name (int t) +{ + switch (t) + { + case DATA_SAMPLE: return NTXT("PROFDATA_TYPE_SAMPLE"); + case DATA_GCEVENT: return NTXT("PROFDATA_TYPE_GCEVENT"); + case DATA_HEAPSZ: return NTXT("PROFDATA_TYPE_HEAPSZ"); + case DATA_CLOCK: return NTXT("PROFDATA_TYPE_CLOCK"); + case DATA_HWC: return NTXT("PROFDATA_TYPE_HWC"); + case DATA_SYNCH: return NTXT("PROFDATA_TYPE_SYNCH"); + case DATA_HEAP: return NTXT("PROFDATA_TYPE_HEAP"); + case DATA_OMP: return NTXT("PROFDATA_TYPE_OMP"); + case DATA_OMP2: return NTXT("PROFDATA_TYPE_OMP2"); + case DATA_OMP3: return NTXT("PROFDATA_TYPE_OMP3"); + case DATA_OMP4: return NTXT("PROFDATA_TYPE_OMP4"); + case DATA_OMP5: return NTXT("PROFDATA_TYPE_OMP5"); + case DATA_IOTRACE: return NTXT("PROFDATA_TYPE_IOTRACE"); + default: abort (); + return NTXT ("PROFDATA_TYPE_ERROR"); + } +} + +char * +get_prof_data_type_uname (int t) +{ + switch (t) + { + case DATA_SAMPLE: return GTXT("Process-wide Resource Utilization"); + case DATA_GCEVENT: return GTXT("Java Garbage Collection Events"); + case DATA_HEAPSZ: return GTXT("Heap Size"); + case DATA_CLOCK: return GTXT("Clock Profiling"); + case DATA_HWC: return GTXT("HW Counter Profiling"); + case DATA_SYNCH: return GTXT("Synchronization Tracing"); + case DATA_HEAP: return GTXT("Heap Tracing"); + case DATA_OMP: return GTXT("OpenMP Profiling"); + case DATA_OMP2: return GTXT("OpenMP Profiling"); + case DATA_OMP3: return GTXT("OpenMP Profiling"); + case DATA_OMP4: return GTXT("OpenMP Profiling"); + case DATA_OMP5: return GTXT("OpenMP Profiling"); + case DATA_IOTRACE: return GTXT("IO Tracing"); + default: abort (); + return NTXT ("PROFDATA_TYPE_ERROR"); + } +} + +int assert_level = 0; // set to 1 to bypass problematic asserts + +#define ASSERT_SKIP (assert_level) + +/* + * class PropDescr + */ + +PropDescr::PropDescr (int _propID, const char *_name) +{ + propID = _propID; + name = strdup (_name ? _name : NTXT ("")); + uname = NULL; + vtype = TYPE_NONE; + flags = 0; + stateNames = NULL; + stateUNames = NULL; +} + +PropDescr::~PropDescr () +{ + free (name); + free (uname); + if (stateNames) + { + stateNames->destroy (); + delete stateNames; + } + if (stateUNames) + { + stateUNames->destroy (); + delete stateUNames; + } +} + +void +PropDescr::addState (int value, const char *stname, const char *stuname) +{ + if (value < 0 || stname == NULL) + return; + if (stateNames == NULL) + stateNames = new Vector<char*>; + stateNames->store (value, strdup (stname)); + if (stateUNames == NULL) + stateUNames = new Vector<char*>; + stateUNames->store (value, strdup (stuname)); +} + +char * +PropDescr::getStateName (int value) +{ + if (stateNames && value >= 0 && value < stateNames->size ()) + return stateNames->fetch (value); + return NULL; +} + +char * +PropDescr::getStateUName (int value) +{ + if (stateUNames && value >= 0 && value < stateUNames->size ()) + return stateUNames->fetch (value); + return NULL; +} + +/* + * class FieldDescr + */ + +FieldDescr::FieldDescr (int _propID, const char *_name) +{ + propID = _propID; + name = _name ? strdup (_name) : NULL; + offset = 0; + vtype = TYPE_NONE; + format = NULL; +} + +FieldDescr::~FieldDescr () +{ + free (name); + free (format); +} + +/* + * class PacketDescriptor + */ + +PacketDescriptor::PacketDescriptor (DataDescriptor *_ddscr) +{ + ddscr = _ddscr; + fields = new Vector<FieldDescr*>; +} + +PacketDescriptor::~PacketDescriptor () +{ + fields->destroy (); + delete fields; +} + +void +PacketDescriptor::addField (FieldDescr *fldDscr) +{ + if (fldDscr == NULL) + return; + fields->append (fldDscr); +} + +/* + * class Data + */ + +/* Check compatibility between Datum and Data */ +static void +checkCompatibility (VType_type v1, VType_type v2) +{ + switch (v1) + { + case TYPE_NONE: + case TYPE_STRING: + case TYPE_DOUBLE: + case TYPE_OBJ: + case TYPE_DATE: + assert (v1 == v2); + break; + case TYPE_INT32: + case TYPE_UINT32: + assert (v2 == TYPE_INT32 || + v2 == TYPE_UINT32); + break; + case TYPE_INT64: + case TYPE_UINT64: + assert (v2 == TYPE_INT64 || + v2 == TYPE_UINT64); + break; + default: + assert (0); + } +} + +class DataINT32 : public Data +{ +public: + + DataINT32 () + { + data = new Vector<int32_t>; + } + + virtual + ~DataINT32 () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_INT32; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%d"), data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return (double) data->fetch (i); + } + + virtual void * + fetchObject (long) + { + assert (ASSERT_SKIP); + return NULL; + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->i); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, (int32_t) val); + } + + virtual void + setObjValue (long, void*) + { + assert (ASSERT_SKIP); + return; + } + + virtual int + cmpValues (long idx1, long idx2) + { + int32_t i1 = data->fetch (idx1); + int32_t i2 = data->fetch (idx2); + return i1 < i2 ? -1 : i1 > i2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + int32_t i1 = data->fetch (idx); + int32_t i2 = val->i; + return i1 < i2 ? -1 : i1 > i2 ? 1 : 0; + } + +private: + Vector<int32_t> *data; +}; + +class DataUINT32 : public Data +{ +public: + + DataUINT32 () + { + data = new Vector<uint32_t>; + } + + virtual + ~DataUINT32 () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_UINT32; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%u"), data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return (double) data->fetch (i); + } + + virtual void * + fetchObject (long) + { + assert (ASSERT_SKIP); + return NULL; + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->i); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, (uint32_t) val); + } + + virtual void + setObjValue (long, void*) + { + assert (ASSERT_SKIP); + return; + } + + virtual int + cmpValues (long idx1, long idx2) + { + uint32_t u1 = data->fetch (idx1); + uint32_t u2 = data->fetch (idx2); + return u1 < u2 ? -1 : u1 > u2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + uint32_t u1 = data->fetch (idx); + uint32_t u2 = (uint32_t) val->i; + return u1 < u2 ? -1 : u1 > u2 ? 1 : 0; + } + +private: + Vector<uint32_t> *data; +}; + +class DataINT64 : public Data +{ +public: + + DataINT64 () + { + data = new Vector<int64_t>; + } + + virtual + ~DataINT64 () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_INT64; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%lld"), (long long) data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return (double) data->fetch (i); + } + + virtual void * + fetchObject (long) + { + assert (ASSERT_SKIP); + return NULL; + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->ll); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, (int64_t) val); + } + + virtual void + setObjValue (long, void*) + { + assert (ASSERT_SKIP); + return; + } + + virtual int + cmpValues (long idx1, long idx2) + { + int64_t i1 = data->fetch (idx1); + int64_t i2 = data->fetch (idx2); + return i1 < i2 ? -1 : i1 > i2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + int64_t i1 = data->fetch (idx); + int64_t i2 = val->ll; + return i1 < i2 ? -1 : i1 > i2 ? 1 : 0; + } + +private: + Vector<int64_t> *data; +}; + +class DataUINT64 : public Data +{ +public: + + DataUINT64 () + { + data = new Vector<uint64_t>; + } + + virtual + ~DataUINT64 () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_UINT64; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%llu"), (long long) data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return (double) data->fetch (i); + } + + virtual void * + fetchObject (long) + { + assert (ASSERT_SKIP); + return NULL; + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->ll); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, val); + } + + virtual void + setObjValue (long, void*) + { + assert (ASSERT_SKIP); + return; + } + + virtual int + cmpValues (long idx1, long idx2) + { + uint64_t u1 = data->fetch (idx1); + uint64_t u2 = data->fetch (idx2); + return u1 < u2 ? -1 : u1 > u2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + uint64_t u1 = data->fetch (idx); + uint64_t u2 = (uint64_t) val->ll; + return u1 < u2 ? -1 : u1 > u2 ? 1 : 0; + } + +private: + Vector<uint64_t> *data; +}; + +class DataOBJECT : public Data +{ +public: + + DataOBJECT () + { + dtype = TYPE_OBJ; + data = new Vector<void*>; + } + + DataOBJECT (VType_type _dtype) + { + dtype = _dtype; + data = new Vector<void*>; + } + + virtual + ~DataOBJECT () + { + delete data; + } + + virtual VType_type + type () + { + return dtype; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long) + { + assert (ASSERT_SKIP); + return 0; + } + + virtual unsigned long long + fetchULong (long) + { + assert (ASSERT_SKIP); + return 0LL; + } + + virtual long long + fetchLong (long) + { + assert (ASSERT_SKIP); + return 0LL; + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%lu"), (unsigned long) data->fetch (i)); + } + + virtual double + fetchDouble (long) + { + assert (ASSERT_SKIP); + return 0.0; + } + + virtual void * + fetchObject (long i) + { + return data->fetch (i); + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->p); + } + + virtual void + setValue (long, uint64_t) + { + assert (ASSERT_SKIP); + return; + } + + virtual void + setObjValue (long idx, void *p) + { + data->store (idx, p); + } + + virtual int + cmpValues (long, long) + { + return 0; + } + + virtual int + cmpDatumValue (long, const Datum *) + { + return 0; + } + +private: + VType_type dtype; + Vector<void*> *data; +}; + +class DataSTRING : public Data +{ +public: + + DataSTRING () + { + data = new Vector<char*>; + } + + virtual + ~DataSTRING () + { + data->destroy (); + delete data; + } + + virtual VType_type + type () + { + return TYPE_STRING; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long) + { + return 0; + } + + virtual unsigned long long + fetchULong (long) + { + return 0LL; + } + + virtual long long + fetchLong (long) + { + return 0LL; + } + + virtual char * + fetchString (long i) + { + return strdup (data->fetch (i)); + } + + virtual double + fetchDouble (long) + { + return 0.0; + } + + virtual void * + fetchObject (long i) + { + return data->fetch (i); + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->l); + } + + virtual void + setValue (long, uint64_t) + { + return; + } + + virtual void + setObjValue (long idx, void *p) + { + data->store (idx, (char*) p); + } + + virtual int + cmpValues (long, long) + { + return 0; + } + + virtual int + cmpDatumValue (long, const Datum *) + { + return 0; + } + +private: + Vector<char*> *data; +}; + +class DataDOUBLE : public Data +{ +public: + + DataDOUBLE () + { + data = new Vector<double>; + } + + virtual + ~DataDOUBLE () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_DOUBLE; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%f"), data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return data->fetch (i); + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->d); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, (double) val); + } + + virtual void + setObjValue (long, void*) + { + return; + } + + virtual void * + fetchObject (long) + { + return NULL; + } + + virtual int + cmpValues (long idx1, long idx2) + { + double d1 = data->fetch (idx1); + double d2 = data->fetch (idx2); + return d1 < d2 ? -1 : d1 > d2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + double d1 = data->fetch (idx); + double d2 = val->d; + return d1 < d2 ? -1 : d1 > d2 ? 1 : 0; + } + +private: + Vector<double> *data; +}; + +Data * +Data::newData (VType_type vtype) +{ + switch (vtype) + { + case TYPE_INT32: + return new DataINT32; + case TYPE_UINT32: + return new DataUINT32; + case TYPE_INT64: + return new DataINT64; + case TYPE_UINT64: + return new DataUINT64; + case TYPE_OBJ: + return new DataOBJECT; + case TYPE_STRING: + return new DataSTRING; + case TYPE_DOUBLE: + return new DataDOUBLE; + default: + return NULL; + } +} + +/* + * class DataDescriptor + */ +DataDescriptor::DataDescriptor (int _id, const char *_name, const char *_uname, + int _flags) +{ + isMaster = true; + id = _id; + name = _name ? strdup (_name) : strdup (NTXT ("")); + uname = _uname ? strdup (_uname) : strdup (NTXT ("")); + flags = _flags; + + // master data, shared with reference copies: + master_size = 0; + master_resolveFrameInfoDone = false; + props = new Vector<PropDescr*>; + data = new Vector<Data*>; + setsTBR = new Vector<Vector<long long>*>; + + // master references point to self: + ref_size = &master_size; + ref_resolveFrameInfoDone = &master_resolveFrameInfoDone; +} + +DataDescriptor::DataDescriptor (int _id, const char *_name, const char *_uname, + DataDescriptor* dDscr) +{ + isMaster = false; + id = _id; + name = _name ? strdup (_name) : strdup (NTXT ("")); + uname = _uname ? strdup (_uname) : strdup (NTXT ("")); + flags = dDscr->flags; + + // references point to master DataDescriptor + ref_size = &dDscr->master_size; + ref_resolveFrameInfoDone = &dDscr->master_resolveFrameInfoDone; + props = dDscr->props; + data = dDscr->data; + setsTBR = dDscr->setsTBR; + + // data that should never be accessed in reference copy + master_size = -1; + master_resolveFrameInfoDone = false; +} + +DataDescriptor::~DataDescriptor () +{ + free (name); + free (uname); + if (!isMaster) + return; + props->destroy (); + delete props; + data->destroy (); + delete data; + setsTBR->destroy (); + delete setsTBR; +} + +void +DataDescriptor::reset () +{ + if (!isMaster) + return; + for (int i = 0; i < data->size (); i++) + { + Data *d = data->fetch (i); + if (d != NULL) + d->reset (); + Vector<long long> *set = setsTBR->fetch (i); + if (set != NULL) + set->reset (); + } + master_size = 0; +} + +PropDescr * +DataDescriptor::getProp (int prop_id) +{ + for (int i = 0; i < props->size (); i++) + { + PropDescr *propDscr = props->fetch (i); + if (propDscr->propID == prop_id) + return propDscr; + } + return NULL; +} + +Data * +DataDescriptor::getData (int prop_id) +{ + if (prop_id < 0 || prop_id >= data->size ()) + return NULL; + return data->fetch (prop_id); +} + +void +DataDescriptor::addProperty (PropDescr *propDscr) +{ + if (propDscr == NULL) + return; + if (propDscr->propID < 0) + return; + PropDescr *oldProp = getProp (propDscr->propID); + if (oldProp != NULL) + { + checkCompatibility (propDscr->vtype, oldProp->vtype); //YXXX depends on experiment correctness + delete propDscr; + return; + } + props->append (propDscr); + data->store (propDscr->propID, Data::newData (propDscr->vtype)); + setsTBR->store (propDscr->propID, NULL); +} + +long +DataDescriptor::addRecord () +{ + if (!isMaster) + return -1; + return master_size++; +} + +static void +checkEntity (Vector<long long> *set, long long val) +{ + // Binary search + int lo = 0; + int hi = set->size () - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + long long ent = set->fetch (md); + if (ent < val) + lo = md + 1; + else if (ent > val) + hi = md - 1; + else + return; + } + set->insert (lo, val); +} + +void +DataDescriptor::setDatumValue (int prop_id, long idx, const Datum *val) +{ + if (idx >= *ref_size) + return; + Data *d = getData (prop_id); + if (d != NULL) + { + VType_type datum_type = val->type; + VType_type data_type = d->type (); + checkCompatibility (datum_type, data_type); + d->setDatumValue (idx, val); + Vector<long long> *set = setsTBR->fetch (prop_id); + if (set != NULL)// Sets are maintained + checkEntity (set, d->fetchLong (idx)); + } +} + +void +DataDescriptor::setValue (int prop_id, long idx, uint64_t val) +{ + if (idx >= *ref_size) + return; + Data *d = getData (prop_id); + if (d != NULL) + { + d->setValue (idx, val); + Vector<long long> *set = setsTBR->fetch (prop_id); + if (set != NULL)// Sets are maintained + checkEntity (set, d->fetchLong (idx)); + } +} + +void +DataDescriptor::setObjValue (int prop_id, long idx, void *val) +{ + if (idx >= *ref_size) + return; + Data *d = getData (prop_id); + if (d != NULL) + d->setObjValue (idx, val); +} + +DataView * +DataDescriptor::createView () +{ + return new DataView (this); +} + +DataView * +DataDescriptor::createImmutableView () +{ + return new DataView (this, DataView::DV_IMMUTABLE); +} + +DataView * +DataDescriptor::createExtManagedView () +{ + return new DataView (this, DataView::DV_EXT_MANAGED); +} + +int +DataDescriptor::getIntValue (int prop_id, long idx) +{ + Data *d = getData (prop_id); + if (d == NULL || idx >= d->getSize ()) + return 0; + return d->fetchInt (idx); +} + +unsigned long long +DataDescriptor::getULongValue (int prop_id, long idx) +{ + Data *d = getData (prop_id); + if (d == NULL || idx >= d->getSize ()) + return 0L; + return d->fetchULong (idx); +} + +long long +DataDescriptor::getLongValue (int prop_id, long idx) +{ + Data *d = getData (prop_id); + if (d == NULL || idx >= d->getSize ()) + return 0L; + return d->fetchLong (idx); +} + +void * +DataDescriptor::getObjValue (int prop_id, long idx) +{ + Data *d = getData (prop_id); + if (d == NULL || idx >= d->getSize ()) + return NULL; + return d->fetchObject (idx); +} + +static int +pcmp (const void *p1, const void *p2, const void *arg) +{ + long idx1 = *(long*) p1; // index1 into Data + long idx2 = *(long*) p2; // index2 into Data + for (Data **dsorted = (Data**) arg; *dsorted != DATA_SORT_EOL; dsorted++) + { + Data *data = *dsorted; + if (data == NULL)// sort property not in this data, skip this criteria + continue; + int res = data->cmpValues (idx1, idx2); + if (res) + return res; + } + // Provide stable sort + return idx1 < idx2 ? -1 : idx1 > idx2 ? 1 : 0; +} + +Vector<long long> * +DataDescriptor::getSet (int prop_id) +{ + if (prop_id < 0 || prop_id >= setsTBR->size ()) + return NULL; + Vector<long long> *set = setsTBR->fetch (prop_id); + if (set != NULL) + return set; + + Data *d = getData (prop_id); + if (d == NULL) + return NULL; + set = new Vector<long long>; + for (long i = 0; i<*ref_size; ++i) + checkEntity (set, d->fetchLong (i)); + setsTBR->store (prop_id, set); + + return set; +} + +/* + * class DataView + */ +DataView::DataView (DataDescriptor *_ddscr) +{ + init (_ddscr, DV_NORMAL); +} + +DataView::DataView (DataDescriptor *_ddscr, DataViewType _type) +{ + init (_ddscr, _type); +} + +void +DataView::init (DataDescriptor *_ddscr, DataViewType _type) +{ + ddscr = _ddscr; + type = _type; + switch (type) + { + case DV_IMMUTABLE: + ddsize = ddscr->getSize (); + index = NULL; + break; + case DV_NORMAL: + case DV_EXT_MANAGED: + ddsize = 0; + index = new Vector<long>; + break; + } + for (int ii = 0; ii < (MAX_SORT_DIMENSIONS + 1); ii++) + sortedBy[ii] = DATA_SORT_EOL; + filter = NULL; +} + +DataView::~DataView () +{ + delete filter; + delete index; +} + +void +DataView::appendDataDescriptorId (long pkt_id /* ddscr index */) +{ + if (type != DV_EXT_MANAGED) + return; // updates allowed only on externally managed DataViews + long curr_ddsize = ddscr->getSize (); + if (pkt_id < 0 || pkt_id >= curr_ddsize) + return; // error! + index->append (pkt_id); +} + +void +DataView::setDataDescriptorValue (int prop_id, long pkt_id, uint64_t val) +{ + ddscr->setValue (prop_id, pkt_id, val); +} + +long long +DataView::getDataDescriptorValue (int prop_id, long pkt_id) +{ + return ddscr->getLongValue (prop_id, pkt_id); +} + +Vector<PropDescr*>* +DataView::getProps () +{ + return ddscr->getProps (); +}; + +PropDescr* +DataView::getProp (int prop_id) +{ + return ddscr->getProp (prop_id); +}; + +void +DataView::filter_in_chunks (fltr_dbe_ctx *dctx) +{ + Expression::Context *e_ctx = new Expression::Context (dctx->fltr->ctx->dbev, dctx->fltr->ctx->exp); + Expression *n_expr = dctx->fltr->expr->copy (); + bool noParFilter = dctx->fltr->noParFilter; + FilterExp *nFilter = new FilterExp (n_expr, e_ctx, noParFilter); + long iter = dctx->begin; + long end = dctx->end; + long orig_ddsize = dctx->orig_ddsize; + while (iter < end) + { + nFilter->put (dctx->tmpView, iter); + if (nFilter->passes ()) + dctx->idxArr[iter - orig_ddsize] = 1; + iter += 1; + } + delete nFilter; +} + +bool +DataView::checkUpdate () +{ + long newSize = ddscr->getSize (); + if (ddsize == newSize) + return false; + if (index == NULL) + return false; + if (type == DV_EXT_MANAGED) + return false; + bool updated = false; + if (filter) + { + DataView *tmpView = ddscr->createImmutableView (); + assert (tmpView->getSize () == newSize); + while (ddsize < newSize) + { + filter->put (tmpView, ddsize); + if (filter->passes ()) + index->append (ddsize); + ddsize += 1; + } + delete tmpView; + return updated; + } + while (ddsize < newSize) + { + index->append (ddsize); + updated = true; + ddsize += 1; + } + return updated; +} + +long +DataView::getSize () +{ + if (checkUpdate () && sortedBy[0] != DATA_SORT_EOL) + // note: after new filter is set, getSize() incurs cost of + // sorting even if caller isn't interested in sort + index->sort ((CompareFunc) pcmp, sortedBy); + + if (index == NULL) + return ddscr->getSize (); + return index->size (); +} + +void +DataView::setDatumValue (int prop_id, long idx, const Datum *val) +{ + ddscr->setDatumValue (prop_id, getIdByIdx (idx), val); +} + +void +DataView::setValue (int prop_id, long idx, uint64_t val) +{ + ddscr->setValue (prop_id, getIdByIdx (idx), val); +} + +void +DataView::setObjValue (int prop_id, long idx, void *val) +{ + ddscr->setObjValue (prop_id, getIdByIdx (idx), val); +} + +int +DataView::getIntValue (int prop_id, long idx) +{ + return ddscr->getIntValue (prop_id, getIdByIdx (idx)); +} + +unsigned long long +DataView::getULongValue (int prop_id, long idx) +{ + return ddscr->getULongValue (prop_id, getIdByIdx (idx)); +} + +long long +DataView::getLongValue (int prop_id, long idx) +{ + return ddscr->getLongValue (prop_id, getIdByIdx (idx)); +} + +void * +DataView::getObjValue (int prop_id, long idx) +{ + return ddscr->getObjValue (prop_id, getIdByIdx (idx)); +} + +void +DataView::sort (const int props[], int prop_count) +{ + if (index == NULL) + { + assert (ASSERT_SKIP); + return; + } + assert (prop_count >= 0 && prop_count < MAX_SORT_DIMENSIONS); + bool sort_changed = false; // see if sort has changed... + for (int ii = 0; ii <= prop_count; ii++) + { // sortedBy size is prop_count+1 + Data *data; + if (ii == prop_count) + data = DATA_SORT_EOL; // special end of array marker + else + data = ddscr->getData (props[ii]); + if (sortedBy[ii] != data) + { + sortedBy[ii] = data; + sort_changed = true; + } + } + if (!checkUpdate () && !sort_changed) + return; + index->sort ((CompareFunc) pcmp, sortedBy); +} + +void +DataView::sort (int prop0) +{ + sort (&prop0, 1); +} + +void +DataView::sort (int prop0, int prop1) +{ + int props[2] = {prop0, prop1}; + sort (props, 2); +} + +void +DataView::sort (int prop0, int prop1, int prop2) +{ + int props[3] = {prop0, prop1, prop2}; + sort (props, 3); +} + +void +DataView::setFilter (FilterExp *f) +{ + if (index == NULL) + { + assert (ASSERT_SKIP); + return; + } + delete filter; + filter = f; + index->reset (); + ddsize = 0; + checkUpdate (); +} + +long +DataView::getIdByIdx (long idx) +{ + if (index == NULL) + return idx; + return index->fetch (idx); +} + +static int +tvalcmp (long data_id, const Datum valColumns[], Data *sortedBy[]) +{ + for (int ii = 0; ii < MAX_SORT_DIMENSIONS; ii++) + { + if (sortedBy[ii] == DATA_SORT_EOL) + break; + Data *d = sortedBy[ii]; + if (d == NULL)// property doesn't exist in data; compare always matches + continue; + const Datum *tvalue = &valColumns[ii]; + int res = d->cmpDatumValue (data_id, tvalue); + if (res) + return res; + } + return 0; +} + +static void +checkSortTypes (const Datum valColumns[], Data *sortedBy[]) +{ +#ifndef NDEBUG + for (int ii = 0; ii < MAX_SORT_DIMENSIONS; ii++) + { + if (sortedBy[ii] == DATA_SORT_EOL) + break; + Data *d = sortedBy[ii]; + if (d == NULL)// property doesn't exist in data; compare always matches + continue; + VType_type datum_type = valColumns[ii].type; + VType_type data_type = d->type (); + checkCompatibility (datum_type, data_type); + } +#endif +} + +bool +DataView::idxRootDimensionsMatch (long idx, const Datum valColumns[]) +{ + // compares idx vs. valColumns[] - If all dimensions match + // (except sort leaf), then the leaf value is valid => return true. + // Otherwise, return false. + checkSortTypes (valColumns, sortedBy); + if (idx < 0 || idx >= index->size ()) // fell off end of array + return false; + long data_id = index->fetch (idx); + + // we will check all dimensions for a match except the "leaf" dimension + for (int ii = 0; ii < (MAX_SORT_DIMENSIONS - 1); ii++) + { + if (sortedBy[ii + 1] == DATA_SORT_EOL) + break; // we are at leaf dimension, don't care about it's value + if (sortedBy[ii] == DATA_SORT_EOL) + break; // end of list + Data *d = sortedBy[ii]; + if (d == NULL) // property doesn't exist in data; compare always matches + continue; + const Datum *tvalue = &valColumns[ii]; + int res = d->cmpDatumValue (data_id, tvalue); + if (res) + return false; + } + return true; +} + +long +DataView::getIdxByVals (const Datum valColumns[], Relation rel) +{ + // checks sortedBy[] columns for match; relation only used on last column + return getIdxByVals (valColumns, rel, -1, -1); +} + +long +DataView::getIdxByVals (const Datum valColumns[], Relation rel, + long minIdx, long maxIdx) +{ + // checks sortedBy[] columns for match; relation only used on last column + checkSortTypes (valColumns, sortedBy); + if (index == NULL || sortedBy[0] == DATA_SORT_EOL) + return -1; + + long lo; + if (minIdx < 0) + lo = 0; + else + lo = minIdx; + + long hi; + if (maxIdx < 0 || maxIdx >= index->size ()) + hi = index->size () - 1; + else + hi = maxIdx; + + long md = -1; + while (lo <= hi) + { + md = (lo + hi) / 2; + int cmp = tvalcmp (index->fetch (md), valColumns, sortedBy); + if (cmp < 0) + { + lo = md + 1; + continue; + } + else if (cmp > 0) + { + hi = md - 1; + continue; + } + + // cmp == 0, we have an exact match + switch (rel) + { + case REL_LT: + hi = md - 1; // continue searching + break; + case REL_GT: + lo = md + 1; // continue searching + break; + case REL_LTEQ: + case REL_GTEQ: + case REL_EQ: + // note: "md" may not be deterministic if multiple matches exist + return md; // a match => done. + } + } + + // no exact match found + switch (rel) + { + case REL_LT: + case REL_LTEQ: + md = hi; + break; + case REL_GT: + case REL_GTEQ: + md = lo; + break; + case REL_EQ: + return -1; + } + if (idxRootDimensionsMatch (md, valColumns)) + return md; + return -1; +} + +void +DataView::removeDbeViewIdx (long idx) +{ + index->remove (idx); +} + diff --git a/gprofng/src/Table.h b/gprofng/src/Table.h new file mode 100644 index 0000000..48ce06a --- /dev/null +++ b/gprofng/src/Table.h @@ -0,0 +1,618 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _TABLE_H +#define _TABLE_H + +#include "vec.h" +#include "Map2D.h" + +#include "dbe_structs.h" + +class FilterExp; +struct PropDescr; +struct FieldDescr; +class PacketDescriptor; +class DataDescriptor; +class DataView; + +// Note: order must match VTYPE_TYPE_NAMES, below + +enum VType_type +{ + TYPE_NONE, + TYPE_INT32, + TYPE_UINT32, + TYPE_INT64, + TYPE_UINT64, + TYPE_STRING, + TYPE_DOUBLE, + TYPE_OBJ, + TYPE_DATE, // Used in FieldDescr only, mapped to TYPE_UINT64 in PropDescr + TYPE_BOOL, // Used only to describe filter props + TYPE_ENUM, // Used only to describe filter props + + TYPE_LAST +}; + +#define VTYPE_TYPE_NAMES \ +{ \ + NTXT("NONE"), \ + NTXT("INT32"), \ + NTXT("UINT32"), \ + NTXT("INT64"), \ + NTXT("UINT64"), \ + NTXT("STRING"), \ + NTXT("DOUBLE"), \ + NTXT("OBJECT"), \ + NTXT("DATE"), \ + NTXT("BOOL"), \ + NTXT("ENUM") \ +} + +// Note: order must match PROFDATA_TYPE_NAMES and PROFDATA_TYPE_UNAMES, below + +enum ProfData_type +{ // a.k.a "data_id" (not the same as Pckt_type "kind") + DATA_SAMPLE, // Traditional collect "Samples" + DATA_GCEVENT, // Java Garbage Collection events + DATA_HEAPSZ, // heap size tracking based on heap tracing data + DATA_CLOCK, // clock profiling data + DATA_HWC, // hardware counter profiling data + DATA_SYNCH, // synchronization tracing data + DATA_HEAP, // heap tracing data + DATA_MPI, // MPI tracing data + DATA_RACE, // data race detection data + DATA_DLCK, // deadlock detection data + DATA_OMP, // OpenMP profiling data (fork events) + DATA_OMP2, // OpenMP profiling data (enter thread events) + DATA_OMP3, // OpenMP profiling data (enter task events) + DATA_OMP4, // OpenMP profiling data (parreg descriptions) + DATA_OMP5, // OpenMP profiling data (task descriptions) + DATA_IOTRACE, // IO tracing data + DATA_LAST +}; + +extern char *get_prof_data_type_name (int t); +extern char * +get_prof_data_type_uname (int t); + +enum Prop_type +{ + PROP_NONE, + // commonly used properties (libcollector modules, er_print) + PROP_ATSTAMP, // hrtime_t, Filter: system HRT timestamp; + // "Absolute TSTAMP" + PROP_ETSTAMP, // hrtime_t, Filter: nanoseconds from subexperiment start; + // "subExperiment TSTAMP" + PROP_TSTAMP, // hrtime_t, Packet: system HRT timestamp + // Filter: nanoseconds from founder start + PROP_THRID, // mapped to uint32_t by readPacket + PROP_LWPID, // mapped to uint32_t by readPacket + PROP_CPUID, // mapped to uint32_t by readPacket + PROP_FRINFO, // uint64_t frinfo + PROP_EVT_TIME, // hrtime_t Filter: Time delta + // If TSTAMP taken at end of event, EVT_TIME will be positive + // If TSTAMP taken at start of event, EVT_TIME will be negative + // Note: clock and hwc profile events set EVT_TIME=0 + // except Solaris Microstate events where NTICK>1: + // These will use EVT_TIME=(NTICK-1)*<tick duration> + + // DATA_SAMPLE + PROP_SAMPLE, // uint64_t sample number + PROP_SMPLOBJ, // Sample* + + // DATA_GCEVENT + PROP_GCEVENT, // uint64_t event id + PROP_GCEVENTOBJ, // GCEvent* + + // DATA_CLOCK + PROP_MSTATE, // unsigned ProfilePacket::mstate + PROP_NTICK, // unsigned ProfilePacket::value + PROP_OMPSTATE, // int ProfilePacket::ompstate + PROP_MPISTATE, // int ProfilePacket::mpistate + + // DATA_SAMPLE // see PrUsage class, see PROP_MSTATE - TBR? + PROP_UCPU, + PROP_SCPU, + PROP_TRAP, + PROP_TFLT, + PROP_DFLT, + PROP_KFLT, + PROP_ULCK, + PROP_TSLP, + PROP_WCPU, + PROP_TSTP, + + // DATA_SYNCH + PROP_SRQST, // hrtime_t SyncPacket::requested + PROP_SOBJ, // Vaddr SyncPacket::objp + + // DATA_HWC + PROP_HWCTAG, // uint32_t HWCntrPacket::tag; + PROP_HWCINT, // uint64_t HWCntrPacket::interval + PROP_VADDR, // Vaddr HWCntrPacket::dbeVA->eaddr + PROP_PADDR, // Vaddr HWCntrPacket::dbePA->eaddr + PROP_HWCDOBJ, // DataObject* HWCntrPacket::dobj + PROP_VIRTPC, // Vaddr HWCntrPacket::eventVPC + PROP_PHYSPC, // Vaddr HWCntrPacket::eventPPC + PROP_EA_PAGESIZE, // uint32_t HWCntrPacket::ea_pagesize + PROP_PC_PAGESIZE, // uint32_t HWCntrPacket::pc_pagesize + PROP_EA_LGRP, // uint32_t HWCntrPacket::ea_lgrp + PROP_PC_LGRP, // uint32_t HWCntrPacket::pc_lgrp + PROP_LWP_LGRP_HOME, // uint32_t HWCntrPacket::lwp_lgrp_home + PROP_PS_LGRP_HOME, // uint32_t HWCntrPacket::ps_lgrp_home + PROP_MEM_LAT, // uint64_t HWCntrPacket::latency + PROP_MEM_SRC, // uint64_t HWCntrPacket::data_source + + // DATA_HEAP + PROP_HTYPE, // Heap_type HeapPacket::mtype + PROP_HSIZE, // Size HeapPacket::size (bytes alloc'd by this event) + PROP_HVADDR, // Vaddr HeapPacket::vaddr + PROP_HOVADDR, // Vaddr HeapPacket::ovaddr + PROP_HLEAKED, // Size HeapPacket::leaked (net bytes leaked) + PROP_HMEM_USAGE, // Size heap memory usage + PROP_HFREED, // Size (bytes freed by this event) + PROP_HCUR_ALLOCS, // int64_t (net allocations running total. Recomputed after each filter) + PROP_HCUR_NET_ALLOC, // int64_t (net allocation for this packet. Recomputed after each filter) + PROP_HCUR_LEAKS, // Size (net leaks running total. Recomputed after each filter) + + // DATA_IOTRACE + PROP_IOTYPE, // IOTrace_type IOTracePacket::iotype + PROP_IOFD, // int32_t IOTracePacket::fd + PROP_IONBYTE, // Size_type IOTracePacket::nbyte + PROP_IORQST, // hrtime_t IOTracePacket::requested + PROP_IOOFD, // int32_t IOTracePacket::ofd + PROP_IOFSTYPE, // FileSystem_type IOTracePacket::fstype + PROP_IOFNAME, // char IOTracePacket::fname + PROP_IOVFD, // int32_t virtual file descriptor + + // DATA_MPI + PROP_MPITYPE, // MPI_type MPIPacket::mpitype + PROP_MPISCOUNT, // Size MPIPacket::scount + PROP_MPISBYTES, // Size MPIPacket::sbytes + PROP_MPIRCOUNT, // Size MPIPacket::rcount + PROP_MPIRBYTES, // Size MPIPacket::rbytes + + // DATA_OMP* + PROP_CPRID, // uint64_t (Note: not same as "PROP_CPRID" below) + PROP_PPRID, // uint64_t OMPPacket::omp_pprid + PROP_TSKID, // uint64_t (Note: not same as "PROP_CPRID" below) + PROP_PTSKID, // uint64_t OMPPacket::omp_ptskid + PROP_PRPC, // uint64_t OMPPacket::omp_prpc + + // DATA_RACE + PROP_RTYPE, // Race_type RacePacket::rtype + PROP_RID, // uint32_t RacePacket::id + PROP_RVADDR, // Vaddr RacePacket::vaddr + PROP_RCNT, // uint32_t RacePacket::count + PROP_LEAFPC, // Vaddr CommonPacket::leafpc + + // DATA_DLCK + PROP_DID, // uint32_t DeadlockPacket::id + PROP_DTYPE, // Deadlock_Lock_type DeadlockPacket::lock_type + PROP_DLTYPE, // Deadlock_type DeadlockPacket::dl_type + PROP_DVADDR, // Vaddr DeadlockPacket::lock_addr + + // Synthetic properties (queries only) + PROP_STACKID, + PROP_STACK, // void* Generic; mapped to M, U, or XSTACK + PROP_MSTACK, // void* machine stack + PROP_USTACK, // void* user_stack + PROP_XSTACK, // void* expert_stack + PROP_HSTACK, // void* hide_stack + //PROP_CPRID, // void* (Note: not same as "PROP_CPRID" above) + //PROP_TSKID, // void* (Note: not same as "PROP_TSKID" above) + PROP_JTHREAD, // JThread* CommonPacket::jthread + PROP_LEAF, // uint64_t stack leaf function + PROP_DOBJ, // "DOBJ" DataObject* + PROP_SAMPLE_MAP, // Map events to SAMPLE using sample's time range + PROP_GCEVENT_MAP, // Map events to GCEVENT using gcevent's time range + PROP_PID, // int unix getpid() + PROP_EXPID, // int Experiment->getUserExpId(), AKA process number, >=1. + PROP_EXPID_CMP, // int "Comparable PROP_EXPID". In compare mode, if this + // process has been matched to another groups' process, + // returns PROP_EXPID of the matching process with the + // lowest PROP_EXPGRID value. Otherwise returns PROP_EXPID. + PROP_EXPGRID, // int Comparison group number. >=0, 0 is Baseline. + PROP_PARREG, // "PARREG" uint64_t (see 6436500) TBR? + PROP_TSTAMP_LO, // hrtime_t Filter: Event's low TSTAMP + PROP_TSTAMP_HI, // hrtime_t Filter: Event's high TSTAMP + PROP_TSTAMP2, // hrtime_t Filter: End TSTAMP (TSTAMP<=TSTAMP2) + PROP_FREQ_MHZ, // int frequency in MHZ (for converting HWC profiling cycles to time) + PROP_NTICK_USEC, // hrtime_t Clock profiling interval, microseconds (PROP_NTICK * Experiment->ptimer_usec) + PROP_IOHEAPBYTES, // Size PROP_HSIZE or PROP_IONBYTE + PROP_STACKL, // void* Generic; mapped to M, U, or XSTACK for DbeLine + PROP_MSTACKL, // void* machine stack + PROP_USTACKL, // void* user_stack + PROP_XSTACKL, // void* expert_stack + PROP_STACKI, // void* Generic; mapped to M, U, or XSTACK for DbeInstr + PROP_MSTACKI, // void* machine stack + PROP_USTACKI, // void* user_stack + PROP_XSTACKI, // void* expert_stack + PROP_DDSCR_LNK, // long long index into DataDescriptor table for a related event + PROP_VOIDP_OBJ, // void* pointer to object containing metadata + PROP_LAST +}; + +enum Prop_flag +{ + PRFLAG_NOSHOW = 0x40 +}; + +struct PropDescr +{ + PropDescr (int propID, const char *name); + virtual ~PropDescr (); + + void addState (int value, const char *stname, const char *stuname); + char *getStateName (int value); + char *getStateUName (int value); + + int + getMaxState () + { + return stateNames ? stateNames->size () : 0; + } + + int propID; + char *name; + char *uname; + VType_type vtype; + int flags; + +private: + Vector<char*>*stateNames; + Vector<char*>*stateUNames; +}; + +struct FieldDescr +{ + FieldDescr (int propID, const char *name); + virtual ~FieldDescr (); + + int propID; + char *name; + int offset; + VType_type vtype; + char *format; +}; + +class PacketDescriptor +{ +public: + PacketDescriptor (DataDescriptor*); + virtual ~PacketDescriptor (); + + DataDescriptor * + getDataDescriptor () + { + return ddscr; + } + + Vector<FieldDescr*> * + getFields () + { + return fields; + } + + void addField (FieldDescr*); + +private: + DataDescriptor *ddscr; + Vector<FieldDescr*> *fields; +}; + +struct Datum +{ + + void + setUINT32 (uint32_t vv) + { + type = TYPE_UINT32; + i = vv; + } + + void + setUINT64 (uint64_t vv) + { + type = TYPE_UINT64; + ll = vv; + } + + void + setSTRING (char* vv) + { + type = TYPE_STRING; + l = vv; + } + + void + setDOUBLE (double vv) + { + type = TYPE_DOUBLE; + d = vv; + } + + void + setOBJ (void* vv) + { + type = TYPE_OBJ; + p = vv; + } + + VType_type type; + union + { + int i; + double d; + char *l; + void *p; + unsigned long long ll; + }; +}; + +class Data +{ +public: + static Data *newData (VType_type); + + virtual + ~Data () { } + + virtual VType_type + type () + { + return TYPE_NONE; + } + virtual void reset () = 0; + virtual long getSize () = 0; + virtual int fetchInt (long i) = 0; + virtual unsigned long long fetchULong (long i) = 0; + virtual long long fetchLong (long i) = 0; + virtual char *fetchString (long i) = 0; + virtual double fetchDouble (long i) = 0; + virtual void *fetchObject (long i) = 0; + virtual void setDatumValue (long, const Datum*) = 0; + virtual void setValue (long, uint64_t) = 0; + virtual void setObjValue (long, void*) = 0; + virtual int cmpValues (long idx1, long idx2) = 0; + virtual int cmpDatumValue (long idx, const Datum *val) = 0; +}; + +enum Data_flag +{ + DDFLAG_NOSHOW = 0x01 +}; + +class DataDescriptor +{ + /* + * An instance of this class stores the data packets for a specific + * type of profiling, for example, clock profiling. + * + * Each packet consists of values for various properties. + * For example, a timestamp is a property which is accessed with PROP_TSTAMP. + * + * Ideally, DataDescriptor contents are considered immutable after the + * data is read in. setValue() should only be used during creation. + * - The packets are in fixed order. This allows DataDescriptor <pkt_id> + * to be treated as a stable handle. + * - Sorting/filtering is handled by the DataView class + * - In the future, if we need to add the ability to append new packets, + * we might add a flag to show when the class is immutable and/or appendible + */ +public: + + DataDescriptor (int id, const char* name, const char* uname, int flags = 0); // master + DataDescriptor (int id, const char* name, const char* uname, DataDescriptor*); // reference copy + ~DataDescriptor (); + + // packets' descriptions + int + getId () + { + return id; + } + + char * + getName () + { + return name; + } + + char * + getUName () + { + return uname; + } + + Vector<PropDescr*> * + getProps () + { + return props; // packet properties + } + PropDescr *getProp (int prop_id); // packet property + + long + getSize () + { + return *ref_size; // number of packets + } + + long + getFlags () + { + return flags; + } + + // class to provide sorting and filtering + DataView *createView (); + DataView *createImmutableView (); + DataView *createExtManagedView (); + + // packet property values (<pkt_id> is stable packet handle) + int getIntValue (int prop_id, long pkt_id); + unsigned long long getULongValue (int prop_id, long pkt_id); + long long getLongValue (int prop_id, long pkt_id); + void *getObjValue (int prop_id, long pkt_id); + Vector<long long> *getSet (int prop_id); // list of sorted, unique values + + // table creation/reset + void addProperty (PropDescr*); // add property to all packets + long addRecord (); // add packet + Data *getData (int prop_id); // get all packets + void setDatumValue (int prop_id, long pkt_id, const Datum *val); + void setValue (int prop_id, long pkt_id, uint64_t val); + void setObjValue (int prop_id, long pkt_id, void *val); + void reset (); // remove all packets (ym: TBR?) + + void + setResolveFrInfoDone () + { + *ref_resolveFrameInfoDone = true; + } + + bool + isResolveFrInfoDone () + { + return *ref_resolveFrameInfoDone; + } + + +private: + bool isMaster; + int flags; // see Data_flag enum + int id; + char *name; + char *uname; + + // the following should only be accessed if parent==NULL + long master_size; + bool master_resolveFrameInfoDone; + + // the following point to the master DataDescriptor's fields + long *ref_size; + bool *ref_resolveFrameInfoDone; + Vector<PropDescr*> *props; + Vector<Data*> *data; + Vector<Vector<long long>*> *setsTBR; // Sets of unique values +}; + +typedef struct +{ + long begin; + long end; + long orig_ddsize; + DataView *tmpView; + long *idxArr; + FilterExp *fltr; +} fltr_dbe_ctx; + +class DataView +{ + /* + * Provides sorting and filtering of DataDescriptor packets + */ +public: + + enum Relation + { + REL_LT, + REL_LTEQ, + REL_EQ, + REL_GTEQ, + REL_GT + }; + + enum DataViewType + { + DV_NORMAL, // filterable, sortable + DV_IMMUTABLE, // reflects exact data in DataDescriptor + DV_EXT_MANAGED // sortable. index[] entries managed externally. + }; + + DataView (DataDescriptor*); + DataView (DataDescriptor*, DataViewType); + virtual ~DataView (); + + Vector<PropDescr*> *getProps (); + PropDescr *getProp (int prop_id); + long getSize (); // number of post-filter packets + + // packet property values accessed by sort index (not DataDescriptor pkt_id) + int getIntValue (int prop_id, long idx); + unsigned long long getULongValue (int prop_id, long idx); + long long getLongValue (int prop_id, long idx); + void *getObjValue (int prop_id, long idx); + long getIdByIdx (long idx); // returns DataDescriptor pkt_id + + // define sort/filter + void sort (const int props[], int prop_count); + void sort (int prop); + void sort (int prop1, int prop2); + void sort (int prop1, int prop2, int prop3); + void setFilter (FilterExp*); + + // search packets + // - sort must already be defined + // - requires the user to provide all properties used in current sort. + // - For a match, the all but the last sort property (the "leaf") + // must match exactly. + long getIdxByVals (const Datum valColumns[], Relation rel); + long getIdxByVals (const Datum valColumns[], Relation rel, + long minIdx, long maxIdx); //limit idx search range + bool idxRootDimensionsMatch (long idx, const Datum valColumns[]); + // packet at idx matches all non-leaf values in valColumns + + // use during table creation, updates underlying DataDescriptor + void setDatumValue (int prop_id, long idx, const Datum *val); + void setValue (int prop_id, long idx, uint64_t val); + void setObjValue (int prop_id, long idx, void *val); + + DataDescriptor * + getDataDescriptor () + { + return ddscr; + } + + void removeDbeViewIdx (long idx); + + // for use with DV_EXT_MANAGED DataViews: + void appendDataDescriptorId (long pkt_id); + void setDataDescriptorValue (int prop_id, long pkt_id, uint64_t val); + long long getDataDescriptorValue (int prop_id, long pkt_id); + +private: + bool checkUpdate (); + void init (DataDescriptor*, DataViewType); + + static void filter_in_chunks (fltr_dbe_ctx *dctx); + DataDescriptor *ddscr; + long ddsize; + Vector<long> *index; // sorted vector of data_id (index into dDscr) +#define MAX_SORT_DIMENSIONS 10 +#define DATA_SORT_EOL ((Data *) -1) /* marks end of sortedBy[] array */ + Data *sortedBy[MAX_SORT_DIMENSIONS + 1]; // columns for sort + FilterExp *filter; + DataViewType type; +}; + +#endif /* _TABLE_H */ diff --git a/gprofng/src/UserLabel.cc b/gprofng/src/UserLabel.cc new file mode 100644 index 0000000..bdb9922 --- /dev/null +++ b/gprofng/src/UserLabel.cc @@ -0,0 +1,177 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <time.h> + +#include "DbeSession.h" +#include "Expression.h" +#include "StringBuilder.h" +#include "util.h" +#include "UserLabel.h" +#include "debug.h" + +int UserLabel::last_id = 0; + +UserLabel::UserLabel (char *_name) +{ + name = dbe_strdup (_name); + comment = str_expr = all_times = hostname = NULL; + start_f = stop_f = false; + expr = NULL; + start_tv.tv_sec = 0; + start_tv.tv_usec = 0; + atime = timeStart = timeStop = start_sec = start_hrtime = 0; + relative = REL_TIME; + id = ++last_id; +} + +UserLabel::~UserLabel () +{ + free (name); + free (comment); + free (all_times); + free (hostname); + free (str_expr); + delete expr; +} + +void +UserLabel::gen_expr () +{ + if (!start_f && !stop_f) + return; + StringBuilder sb; + sb.append ('('); + if (str_expr) + { + sb.append (str_expr); + sb.append (NTXT (" || (")); + } + if (start_f) + { + sb.append (NTXT ("TSTAMP")); + sb.append (NTXT (">=")); + sb.append (timeStart); + if (stop_f) + { + sb.append (NTXT (" && ")); + } + } + if (stop_f) + { + sb.append (NTXT ("TSTAMP")); + sb.append ('<'); + sb.append (timeStop); + } + sb.append (')'); + if (str_expr) + { + sb.append (')'); + delete str_expr; + } + str_expr = sb.toString (); + start_f = stop_f = false; +} + +void +UserLabel::register_user_label (int groupId) +{ + gen_expr (); + if (str_expr) + { + char *old_str = str_expr; + str_expr = dbe_sprintf (NTXT ("(EXPGRID==%d && %s)"), groupId, old_str); + delete old_str; + UserLabel *ulbl = dbeSession->findUserLabel (name); + if (ulbl) + { + old_str = ulbl->str_expr; + ulbl->str_expr = dbe_sprintf (NTXT ("(%s || %s)"), old_str, str_expr); + delete old_str; + if (comment) + { + if (ulbl->comment) + { + old_str = ulbl->comment; + ulbl->comment = dbe_sprintf (NTXT ("%s; %s"), old_str, comment); + delete old_str; + } + else + ulbl->comment = dbe_strdup (comment); + } + delete ulbl->expr; + ulbl->expr = dbeSession->ql_parse (ulbl->str_expr); + } + else + { + expr = dbeSession->ql_parse (str_expr); + dbeSession->append (this); + } + } +} + +char * +UserLabel::dump () +{ + StringBuilder sb; + sb.append (name); + if (str_expr) + { + sb.append (NTXT (" str_expr='")); + sb.append (str_expr); + sb.append ('\''); + } + if (all_times) + { + sb.append (NTXT (" atime=")); + sb.append ((unsigned int) (atime / NANOSEC)); + sb.append ('.'); + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("%09llu"), (unsigned long long) (atime % NANOSEC)); + sb.append (buf); + sb.append (NTXT (" all_times='")); + sb.append (all_times); + sb.append ('\''); + } + if (comment) + { + sb.append (NTXT (" comment='")); + sb.append (comment); + sb.append ('\''); + } + return sb.toString (); +} + +void +UserLabel::dump (const char *msg, Vector<UserLabel*> *labels) +{ + if (!DUMP_USER_LABELS) + return; + if (msg) + fprintf (stderr, NTXT ("%s\n"), msg); + for (int i = 0, sz = labels ? labels->size () : 0; i < sz; i++) + { + UserLabel *lbl = labels->fetch (i); + char *s = lbl->dump (); + fprintf (stderr, NTXT ("%2d %s\n"), i, s); + delete s; + } +} diff --git a/gprofng/src/UserLabel.h b/gprofng/src/UserLabel.h new file mode 100644 index 0000000..0cb37e4 --- /dev/null +++ b/gprofng/src/UserLabel.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _USER_LABEL_H +#define _USER_LABEL_H + +#include <time.h> +#include "vec.h" + +class Expression; +class StringBuilder; + +class UserLabel +{ +public: + + enum + { + REL_TIME = 0, + ABS_TIME = 1, + CUR_TIME = 2 + }; + + UserLabel (char *_name); + ~UserLabel (); + void register_user_label (int groupId); + void gen_expr (); + char *dump (); + static void dump (const char *msg, Vector<UserLabel*> *labels); + + char *name, *comment, *str_expr, *all_times, *hostname; + bool start_f, stop_f; + Expression *expr; + timeval start_tv; + long long atime, timeStart, timeStop, start_sec, start_hrtime; + int id, relative; + +private: + void gen_time_expr (StringBuilder *sb, long long hrtime, char *op); + + static int last_id; +}; + +#endif diff --git a/gprofng/src/checks.cc b/gprofng/src/checks.cc new file mode 100644 index 0000000..105821e --- /dev/null +++ b/gprofng/src/checks.cc @@ -0,0 +1,516 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> +#include <unistd.h> +#include <sys/utsname.h> +#include <sys/param.h> + +#include "gp-defs.h" +#include "Elf.h" +#include "collctrl.h" +#include "i18n.h" +#include "util.h" +#include "collect.h" + +void +collect::check_target (int argc, char **argv) +{ + char *next; + char *last = 0; + char *a; + char *ccret; + char **lasts = &last; + int tindex = targ_index; + int ret; + char *basename; + is_64 = false; + + /* now check the executable */ + nargs = argc - targ_index; + Exec_status rv = check_executable (argv[targ_index]); + switch (rv) + { + case EXEC_OK: + njargs = cc->get_java_arg_cnt (); + arglist = (char **) calloc (nargs + 5 + njargs, sizeof (char *)); + jargs = cc->get_java_args (); + + // store the first argument -- target name + ret = 0; + arglist[ret++] = argv[tindex++]; + if (cc->get_java_mode () == 1) + { + // add any user-specified -J (Java) arguments + int length = (int) strlen (argv[targ_index]); + int is_java = 0; + if ((length >= 6) && strcmp (&argv[targ_index][length - 5], NTXT ("/java")) == 0) + is_java = 1; + else if ((length == 4) && strcmp (&argv[targ_index][0], NTXT ("java")) == 0) + is_java = 1; + if (njargs != 0 && is_java) + { + next = strtok_r (jargs, NTXT (" \t"), lasts); + arglist[ret++] = next; + for (;;) + { + next = strtok_r (NULL, NTXT (" \t"), lasts); + if (next == NULL) + break; + arglist[ret++] = next; + } + } + } + + // copy the rest of the arguments + for (int i = 1; i < nargs; i++) + arglist[ret++] = argv[tindex++]; + nargs = ret; + break; + case EXEC_IS_JAR: + // Preface the user-supplied argument list with + // the path to the java, the collector invocation, + // any -J java arguments provided, and "-jar". + ccret = cc->set_java_mode (NTXT ("on")); + if (ccret != NULL) + { + writeStr (2, ccret); + exit (1); + } + njargs = cc->get_java_arg_cnt (); + arglist = (char **) calloc (nargs + 5 + njargs, sizeof (char *)); + jargs = cc->get_java_args (); + + a = find_java (); + if (a == NULL) + exit (1); // message was written + ret = 0; + arglist[ret++] = a; + // add any user-specified Java arguments + if (njargs != 0) + { + next = strtok_r (jargs, NTXT (" \t"), lasts); + arglist[ret++] = next; + for (;;) + { + next = strtok_r (NULL, NTXT (" \t"), lasts); + if (next == NULL) + break; + arglist[ret++] = next; + } + } + arglist[ret++] = NTXT ("-jar"); + for (int i = 0; i < nargs; i++) + arglist[ret++] = argv[tindex++]; + nargs = ret; + break; + case EXEC_IS_CLASSCLASS: + // remove the .class from the name + ret = (int) strlen (argv[targ_index]); + argv[targ_index][ret - 6] = 0; + + // now fall through to the EXEC_IS_CLASS case + case EXEC_IS_CLASS: + // Preface the user-supplied argument list with + // the path to the java, the collector invocation, + // and any -J java arguments provided. + ccret = cc->set_java_mode (NTXT ("on")); + if (ccret != NULL) + { + writeStr (2, ccret); + exit (1); + } + jargs = cc->get_java_args (); + njargs = cc->get_java_arg_cnt (); + arglist = (char **) calloc (nargs + 4 + njargs, sizeof (char *)); + + a = find_java (); + if (a == NULL) + exit (1); // message was written + ret = 0; + arglist[ret++] = a; + // add any user-specified Java arguments + if (njargs != 0) + { + next = strtok_r (jargs, NTXT (" \t"), lasts); + arglist[ret++] = next; + for (;;) + { + next = strtok_r (NULL, NTXT (" \t"), lasts); + if (next == NULL) + break; + arglist[ret++] = next; + } + } + + // copy the remaining arguments to the new list + for (int i = 0; i < nargs; i++) + arglist[ret++] = argv[tindex++]; + nargs = ret; + break; + case EXEC_ELF_NOSHARE: + case EXEC_OPEN_FAIL: + case EXEC_ELF_LIB: + case EXEC_ELF_HEADER: + case EXEC_ELF_ARCH: + case EXEC_ISDIR: + case EXEC_NOT_EXEC: + case EXEC_NOT_FOUND: + default: + /* something wrong; write a message */ + char *errstr = status_str (rv, argv[targ_index]); + if (errstr) + { + dbe_write (2, "%s", errstr); + free (errstr); + } + exit (1); + } + cc->set_target (arglist[0]); + + /* check the experiment */ + char *ccwarn; + ccret = cc->check_expt (&ccwarn); + if (ccwarn != NULL) + { + writeStr (2, ccwarn); + free (ccwarn); + } + if (ccret != NULL) + { + writeStr (2, ccret); + exit (1); + } + /* check if java, to see if -j flag was given */ + if ((basename = strrchr (arglist[0], '/')) == NULL) + basename = arglist[0]; + else + basename++; + if (strcmp (basename, NTXT ("java")) == 0) + { + /* the target's name is java; was java flag set? */ + if ((jseen_global == 0) && (cc->get_java_mode () == 0)) + { + char *cret = cc->set_java_mode (NTXT ("on")); + if (cret != NULL) + { + writeStr (2, cret); + exit (1); + } + } + } +} + +collect::Exec_status +collect::check_executable (char *target_name) +{ + char target_path[MAXPATHLEN]; + struct stat64 statbuf; + if (target_name == NULL) // not set, but assume caller knows what it's doing + return EXEC_OK; + if (getenv ("GPROFNG_SKIP_VALIDATION")) // don't check target + return EXEC_OK; + + // see if target exists and is not a directory + if ((dbe_stat (target_name, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFDIR)) + { + // target is found, check for access as executable + if (access (target_name, X_OK) != 0) + { + // not an executable, check for jar or class file + int i = (int) strlen (target_name); + if ((i >= 5) && strcmp (&target_name[i - 4], NTXT (".jar")) == 0) + { + // could be a jar file + // XXXX -- need better check for real jar file + cc->set_java_mode ("on"); + return EXEC_IS_JAR; + } + if ((i >= 7) && strcmp (&target_name[i - 6], NTXT (".class")) == 0) + { + // could be a class file + // XXXX -- need better check for real class file + cc->set_java_mode (NTXT ("on")); + return EXEC_IS_CLASSCLASS; + } + // not a jar or class file, return not an executable + return EXEC_NOT_EXEC; + } + else // found, and it is executable. set the path to it + snprintf (target_path, sizeof (target_path), NTXT ("%s"), target_name); + } + else + { + // not found, look on path + char *exe_name = get_realpath (target_name); + if (access (exe_name, X_OK) == 0) + { + // target can't be located + // one last attempt: append .class to name, and see if we can find it + snprintf (target_path, sizeof (target_path), NTXT ("%s.class"), target_name); + if (dbe_stat (target_path, &statbuf) == 0) + { + // the file exists + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + { + // this is a directory; that won't do. + return EXEC_ISDIR; + } + // say it's a class file + cc->set_java_mode (NTXT ("on")); + return EXEC_IS_CLASS; + } + return EXEC_NOT_FOUND; + } + snprintf (target_path, sizeof (target_path), NTXT ("%s"), exe_name); + delete exe_name; + } + + // target_path is now the purported executable + // check for ELF library out of date + if (Elf::elf_version (EV_CURRENT) == EV_NONE) + return EXEC_ELF_LIB; + Elf *elf = Elf::elf_begin (target_path); + if (elf == NULL) + return EXEC_OK; + // do not by pass checking architectural match + collect::Exec_status exec_stat = check_executable_arch (elf); + if (exec_stat != EXEC_OK) + { + delete elf; + return exec_stat; + } + delete elf; + return EXEC_OK; +} + +collect::Exec_status +collect::check_executable_arch (Elf *elf) +{ + Elf_Internal_Ehdr *ehdrp = elf->elf_getehdr (); + if (ehdrp == NULL) + return EXEC_ELF_HEADER; + unsigned short machine = ehdrp->e_machine; + + switch (machine) + { +#if ARCH(SPARC) + case EM_SPARC: + case EM_SPARC32PLUS: + break; + case EM_SPARCV9: + is_64 = true; + break; +#elif ARCH(Intel) + case EM_X86_64: + { + is_64 = true; + // now figure out if the platform can run it + struct utsname unbuf; + int r = uname (&unbuf); + if (r == 0 && unbuf.machine && strstr (unbuf.machine, "_64") == NULL) + // machine can not run 64 bits, but this code is 64-bit + return EXEC_ELF_ARCH; + } + break; + case EM_386: + break; +#elif ARCH(Aarch64) + case EM_AARCH64: + is_64 = true; + break; +#endif + default: + return EXEC_ELF_ARCH; + } + + // now check if target was built with shared libraries + int dynamic = 0; + for (unsigned cnt = 0; cnt < ehdrp->e_phnum; cnt++) + { + Elf_Internal_Phdr *phdrp = elf->get_phdr (cnt); + if (phdrp && phdrp->p_type == PT_DYNAMIC) + { + dynamic = 1; + break; + } + } + if (dynamic == 0) + { + // target is not a dynamic executable or shared object; + // can't record data + return EXEC_ELF_NOSHARE; + } + return EXEC_OK; +} + +char * +collect::status_str (Exec_status rv, char *target_name) +{ + switch (rv) + { + case EXEC_OK: + case EXEC_IS_JAR: + case EXEC_IS_CLASS: + case EXEC_IS_CLASSCLASS: + // supported flavors -- no error message + return NULL; + case EXEC_ELF_NOSHARE: + return dbe_sprintf (GTXT ("Target executable `%s' must be built with shared libraries\n"), target_name); + case EXEC_OPEN_FAIL: + return dbe_sprintf (GTXT ("Can't open target executable `%s'\n"), target_name); + case EXEC_ELF_LIB: + return strdup (GTXT ("Internal error: Not a working version of ELF library\n")); + case EXEC_ELF_HEADER: + return dbe_sprintf (GTXT ("Target `%s' is not a valid ELF executable\n"), target_name); + case EXEC_ELF_ARCH: + return dbe_sprintf (GTXT ("Target architecture of executable `%s' is not supported on this machine\n"), target_name); + case EXEC_ISDIR: + return dbe_sprintf (GTXT ("Target `%s' is a directory, not an executable\n"), target_name); + case EXEC_NOT_EXEC: + return dbe_sprintf (GTXT ("Target `%s' is not executable\n"), target_name); + case EXEC_NOT_FOUND: + return dbe_sprintf (GTXT ("Target `%s' not found\n"), target_name); + } + return NULL; +} + +char * +collect::find_java (void) +{ + char buf[MAXPATHLEN]; + char *var = NULL; + Exec_status rv = EXEC_OK; + + // first see if the user entered a -j argument + var = cc->get_java_path (); + if (var != NULL) + { + snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var); + java_how = NTXT ("-j"); + rv = check_executable (buf); + } + // then try JDK_HOME + if (java_how == NULL) + { + var = getenv (NTXT ("JDK_HOME")); + if ((var != NULL) && (strlen (var) > 0)) + { + snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var); + java_how = NTXT ("JDK_HOME"); + rv = check_executable (buf); + } + } + // then try JAVA_PATH + if (java_how == NULL) + { + var = getenv (NTXT ("JAVA_PATH")); + if ((var != NULL) && (strlen (var) > 0)) + { + snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var); + java_how = NTXT ("JAVA_PATH"); + rv = check_executable (buf); + } + } + // try the user's path + if (java_how == NULL) + { + snprintf (buf, sizeof (buf), NTXT ("java")); + rv = check_executable (buf); + if (rv == EXEC_OK) + java_how = NTXT ("PATH"); + } + // finally, just try /usr/java -- system default + if (java_how == NULL) + { + snprintf (buf, sizeof (buf), NTXT ("/usr/java/bin/java")); + rv = check_executable (buf); + java_how = NTXT ("/usr/java/bin/java"); + } + + // we now have a nominal path to java, and how we chose it + // and we have rv set to the check_executable return + switch (rv) + { + case EXEC_OK: + java_path = strdup (buf); + if (verbose == 1) + dbe_write (2, GTXT ("Path to `%s' (set from %s) used for Java profiling\n"), + java_path, java_how); + return ( strdup (buf)); + default: + dbe_write (2, GTXT ("Path to `%s' (set from %s) does not point to a JVM executable\n"), + buf, java_how); + break; + } + return NULL; +} + +void +collect::validate_config (int how) +{ + if (getenv (NTXT ("GPROFNG_SKIP_VALIDATION")) != NULL) + return; + char *cmd = dbe_sprintf (NTXT ("%s/perftools_validate"), run_dir); + if (access (cmd, X_OK) != 0) + { + if (how) + dbe_write (2, GTXT ("WARNING: Unable to validate system: `%s' could not be executed\n"), cmd); + return; + } + char *quiet = how == 0 ? NTXT ("") : NTXT ("-q"); // check collection, verbosely + char *buf; + if (cc->get_java_default () == 0 && java_path) + buf = dbe_sprintf (NTXT ("%s -c -j %s -H \"%s\" %s"), cmd, java_path, java_how, quiet); + else // not java mode -- don't check the java version + buf = dbe_sprintf (NTXT ("%s -c %s"), cmd, quiet); + free (cmd); + + /* now run the command */ + int ret = system (buf); + int status = WEXITSTATUS (ret); + if ((status & 0x1) != 0) + dbe_write (2, GTXT ("WARNING: Data collection may fail: system is not properly configured or is unsupported.\n")); + if ((status & 0x2) != 0) + dbe_write (2, GTXT ("WARNING: Java data collection may fail: J2SE[tm] version is unsupported.\n")); + free (buf); +} + +void +collect::validate_java (const char *jvm, const char *jhow, int q) +{ + char *cmd = dbe_sprintf (NTXT ("%s/perftools_ckjava"), run_dir); + if (access (cmd, X_OK) != 0) + { + dbe_write (2, GTXT ("WARNING: Unable to validate Java: `%s' could not be executed\n"), cmd); + return; + } + char *buf = dbe_sprintf (NTXT ("%s -j %s -H \"%s\" %s"), cmd, jvm, jhow, + (q == 1 ? "-q" : "")); + free (cmd); + + /* now run the command */ + int ret = system (buf); + int status = WEXITSTATUS (ret); + if (status != 0) + dbe_write (2, GTXT ("WARNING: Java data collection may fail: J2SE[tm] version is unsupported.\n")); + free (buf); +} diff --git a/gprofng/src/collctrl.cc b/gprofng/src/collctrl.cc new file mode 100644 index 0000000..48c65ff --- /dev/null +++ b/gprofng/src/collctrl.cc @@ -0,0 +1,3149 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <unistd.h> +#include <strings.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/statvfs.h> +#include <sys/param.h> +#include <signal.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> +#include <dirent.h> +#include <libgen.h> +#include <assert.h> +#include <regex.h> /* regcomp() */ + +#include "util.h" +#include "libiberty.h" +#include "collctrl.h" +#include "hwcdrv.h" +//#include "hwcfuncs.h" + +#define SP_GROUP_HEADER "#analyzer experiment group" +#define DD_MAXPATHLEN (MAXPATHLEN * 4) /* large, to build up data descriptor */ + +/* If the system doesn't provide strsignal, we get it defined in + libiberty but no declaration is supplied. */ +#if !defined (HAVE_STRSIGNAL) && !defined (strsignal) +extern const char *strsignal (int); +#endif + +// _SC_CPUID_MAX is not available on 2.6/2.7 +#ifndef _SC_CPUID_MAX +#define _SC_CPUID_MAX 517 +#endif + +const char *get_fstype (char *); + +Coll_Ctrl::Coll_Ctrl (int _interactive, bool _defHWC, bool _kernelHWC) +{ + char hostname[MAXPATHLEN]; + long ncpumax; + interactive = _interactive; + defHWC = _defHWC; + kernelHWC = _kernelHWC; + + /* set this host's parameters */ + gethostname (hostname, 1023); + node_name = strdup (hostname); + char *p = strchr (node_name, (int) '.'); + if (p != NULL) + *p = 0; + default_stem = strdup ("test"); + + /* get CPU count and processor clock rate */ + ncpumax = sysconf (_SC_CPUID_MAX); + if (ncpumax == -1) + { + ncpus = sysconf (_SC_NPROCESSORS_CONF); + /* add 2048 to count, since on some systems CPUID does not start at zero */ + ncpumax = ncpus + 2048; + } + ncpus = 0; + cpu_clk_freq = 0; + + // On Linux, read /proc/cpuinfo to get CPU count and clock rate + // Note that parsing is different on SPARC and x86 +#if defined(sparc) + FILE *procf = fopen ("/proc/cpuinfo", "r"); + if (procf != NULL) + { + char temp[1024]; + while (fgets (temp, (int) sizeof (temp), procf) != NULL) + { + if (strncmp (temp, "Cpu", 3) == 0 && temp[3] != '\0' + && strncmp ((strchr (temp + 1, 'C')) ? strchr (temp + 1, 'C') + : (temp + 4), "ClkTck", 6) == 0) + { + ncpus++; + char *val = strchr (temp, ':'); + if (val) + { + unsigned long long freq; + sscanf (val + 2, "%llx", &freq); + cpu_clk_freq = (unsigned int) (((double) freq) / 1000000.0 + 0.5); + } + else + cpu_clk_freq = 0; + } + } + fclose (procf); + } + +#elif defined(__aarch64__) + asm volatile("mrs %0, cntfrq_el0" : "=r" (cpu_clk_freq)); + dbe_write (2, GTXT ("CPU clock frequency: %d\n"), cpu_clk_freq); + +#else + FILE *procf = fopen ("/proc/cpuinfo", "r"); + if (procf != NULL) + { + char temp[1024]; + while (fgets (temp, (int) sizeof (temp), procf) != NULL) + { + // x86 Linux + if (strncmp (temp, "processor", 9) == 0) + ncpus++; + else if (strncmp (temp, "cpu MHz", 7) == 0) + { + char *val = strchr (temp, ':'); + cpu_clk_freq = val ? atoi (val + 1) : 0; + } + } + fclose (procf); + } +#endif + + /* check resolution of system clock */ + sys_resolution = sysconf (_SC_CLK_TCK); + if (sys_resolution == 0) + sys_period = 10000; + else + sys_period = MICROSEC / (int) sys_resolution; + + /* determine memory page size and number of pages */ + npages = sysconf (_SC_PHYS_PAGES); + page_size = sysconf (_SC_PAGE_SIZE); + + /* set default clock parameters */ + hwcprof_enabled_cnt = 0; // must be set before calling determine_profile_params(); + determine_profile_params (); // inits clk_params which is used by clock profiling AND HWCs + cpc_cpuver = CPUVER_UNDEFINED; + + /* set default control values */ + debug_mode = 0; +#if defined(GPROFNG_JAVA_PROFILING) + java_mode = 1; +#else + java_mode = 0; +#endif + java_default = 1; + java_path = NULL; + java_args = NULL; + njava_args = 0; + follow_mode = FOLLOW_ON; + follow_default = 1; + follow_spec_usr = NULL; + follow_spec_cmp = NULL; + prof_idle = 1; + archive_mode = strdup ("on"); + pauseresume_sig = 0; + sample_sig = 0; + uinterrupt = 0; + attach_pid = 0; + time_run = 0; + start_delay = 0; + + /* clear the string pointers */ + uexpt_name = NULL; + expt_name = NULL; + expt_dir = NULL; + base_name = NULL; + udir_name = NULL; + store_dir = NULL; + prev_store_dir = strdup (""); + store_ptr = NULL; + expt_group = NULL; + target_name = NULL; + data_desc = NULL; + lockname = NULL; + hwc_string = NULL; + project_home = NULL; + lockfd = -1; + + /* set default data collection values */ + enabled = 0; + opened = 0; + clkprof_enabled = 1; + clkprof_default = 1; + for (unsigned ii = 0; ii < MAX_PICS; ii++) + { + memset (&hwctr[ii], 0, sizeof (Hwcentry)); + hwctr[ii].reg_num = -1; + } + hwcprof_default = 0; + if (defHWC == true) + { + setup_hwc (); + hwcprof_default = 1; + } + else // disable the default, and reset the counters + hwcprof_enabled_cnt = 0; + synctrace_enabled = 0; + synctrace_thresh = -1; + synctrace_scope = 0; + heaptrace_enabled = 0; + heaptrace_checkenabled = 0; + iotrace_enabled = 0; + count_enabled = 0; + Iflag = 0; + Nflag = 0; + sample_period = 1; + sample_default = 1; + size_limit = 0; + nofswarn = 0; + expno = 1; + + // ensure that the default name is updated + // but don't print any message + (void) preprocess_names (); + (void) update_expt_name (false, false); +} + +/* Copy constructor */ +Coll_Ctrl::Coll_Ctrl (Coll_Ctrl * cc) +{ + uinterrupt = 0; + interactive = cc->interactive; + defHWC = cc->defHWC; + kernelHWC = cc->kernelHWC; + node_name = strdup (cc->node_name); + default_stem = strdup (cc->default_stem); + ncpus = cc->ncpus; + cpu_clk_freq = cc->cpu_clk_freq; + npages = cc->npages; + page_size = cc->page_size; + cpc_cpuver = cc->cpc_cpuver; + debug_mode = cc->debug_mode; + java_mode = cc->java_mode; + java_default = cc->java_default; + java_path = NULL; + java_args = NULL; + njava_args = 0; + follow_mode = cc->follow_mode; + follow_default = cc->follow_default; + if (cc->follow_spec_usr) + { + follow_spec_usr = strdup (cc->follow_spec_usr); + follow_spec_cmp = strdup (cc->follow_spec_cmp); + } + else + { + follow_spec_usr = NULL; + follow_spec_cmp = NULL; + } + archive_mode = strdup (cc->archive_mode); + pauseresume_sig = cc->pauseresume_sig; + sample_sig = cc->sample_sig; + time_run = cc->time_run; + start_delay = cc->start_delay; + clk_params = cc->clk_params; + clkprof_enabled = cc->clkprof_enabled; + clkprof_default = cc->clkprof_default; + clkprof_timer = cc->clkprof_timer; + clkprof_timer_target = cc->clkprof_timer_target; + + // copy HW counter information + hwcprof_default = cc->hwcprof_default; + hwcprof_enabled_cnt = cc->hwcprof_enabled_cnt; + if (cc->hwc_string != NULL) + hwc_string = strdup (cc->hwc_string); + else + hwc_string = NULL; + for (int i = 0; i < hwcprof_enabled_cnt; i++) + hwcentry_dup (&hwctr[i], &(cc->hwctr[i])); + project_home = cc->project_home ? strdup (cc->project_home) : NULL; + synctrace_enabled = cc->synctrace_enabled; + synctrace_thresh = cc->synctrace_thresh; + synctrace_scope = cc->synctrace_scope; + heaptrace_enabled = cc->heaptrace_enabled; + heaptrace_checkenabled = cc->heaptrace_checkenabled; + iotrace_enabled = cc->iotrace_enabled; + count_enabled = cc->count_enabled; + Iflag = cc->Iflag; + Nflag = cc->Nflag; + sample_period = cc->sample_period; + sample_default = cc->sample_default; + size_limit = cc->size_limit; + nofswarn = cc->nofswarn; + + // these will get reset during preprocess_names() + expt_name = NULL; + expt_dir = NULL; + store_dir = NULL; + base_name = NULL; + expno = 1; + + // these represent user settings + expt_group = NULL; + if (cc->expt_group != NULL) + expt_group = strdup (cc->expt_group); + uexpt_name = NULL; + if (cc->uexpt_name != NULL) + uexpt_name = strdup (cc->uexpt_name); + udir_name = NULL; + if (cc->udir_name != NULL) + udir_name = strdup (cc->udir_name); + + /* clear the string pointers */ + prev_store_dir = strdup (""); + store_ptr = NULL; + target_name = NULL; + data_desc = NULL; + lockname = NULL; + lockfd = -1; + + /* set default data collection values */ + enabled = cc->enabled; + opened = 0; + nofswarn = cc->nofswarn; + sys_resolution = cc->sys_resolution; + sys_period = cc->sys_period; + + // ensure that the default name is updated + (void) preprocess_names (); + (void) update_expt_name (false, false); + build_data_desc (); +} + +Coll_Ctrl::~Coll_Ctrl () +{ + free (node_name); + free (expt_name); + free (expt_dir); + free (base_name); + free (udir_name); + free (store_dir); + free (store_ptr); + free (expt_group); + free (target_name); + free (data_desc); + free (lockname); + free (hwc_string); + free (project_home); + free (java_path); + hwcprof_enabled_cnt = 0; +} + +/* set up the experiment */ +char * +Coll_Ctrl::setup_experiment () +{ + char *ret; + if (enabled == 0) + return NULL; + build_data_desc (); + + /* create the experiment directory */ + ret = create_exp_dir (); + if (ret != NULL) + return ret; + + /* if an experiment-group, join it */ + ret = join_group (); + if (ret != NULL) + { + remove_exp_dir (); + return ret; + } + /* all is OK, return 0 */ + opened = 1; + return NULL; +} + +void +Coll_Ctrl::interrupt () +{ + uinterrupt = 1; +} + +char * +Coll_Ctrl::enable_expt () +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (cpu_clk_freq == 0) + return strdup (GTXT ("Can not determine CPU clock frequency.\n")); + if (sys_resolution == 0) + return strdup (GTXT ("System clock profile resolution can not be determined.\n")); + enabled = 1; + return NULL; +} + +/* close the experiment */ +void +Coll_Ctrl::close_expt () +{ + opened = 0; + (void) update_expt_name (false, false); +} + +/* close and delete the experiment */ +void +Coll_Ctrl::delete_expt () +{ + if (opened == 0) + return; + remove_exp_dir (); + + /* The order of removing the directory and closing + * the experiment may seem unnatural, but it's not. + * We do need to update names when we close the experiment + * (actually Coll_Ctrl object) and we can't remove anything + * after that. + */ + close_expt (); +} + +// Check the experiment settings for consistency. Returns NULL if OK, +// or an error message if there are invalid combinations of settings +char * +Coll_Ctrl::check_consistency () +{ + /* check for Java arguments, but not Java profiling */ + if (java_args != NULL && java_mode == 0) + return strdup (GTXT ("Java arguments can not be set if Java profiling is not enabled.\n")); + + /* if count data, no other data is allowed */ + if (count_enabled != 0 + && ((clkprof_default != 1 && clkprof_enabled != 0) + || hwcprof_enabled_cnt != 0 || synctrace_enabled != 0 + || heaptrace_enabled != 0 || iotrace_enabled != 0)) + return strdup (GTXT ("Count data cannot be collected along with any other data.\n")); + + /* if count data, various other options are not allowed */ + if (count_enabled != 0 + && ((java_mode != 0 && java_default != 1) + || java_args != NULL || debug_mode != 0 + || (follow_mode != 0 && follow_default != 1) + || pauseresume_sig != 0 || sample_sig != 0 + || (sample_default != 1 && sample_period != 0) || time_run != 0)) + return strdup (GTXT ("Count data cannot be collected with any of -F -S -y -l -j -J -x -t .\n")); + /* if not count data, I and N options are not allowed */ + if (count_enabled == 0 && (Iflag != 0 || Nflag != 0)) + return strdup (GTXT ("-I or -N can only be specified with count data.\n")); + return NULL; +} + +char * +Coll_Ctrl::check_expt (char **warn) +{ + char *ret; + *warn = NULL; + ret = check_consistency (); + if (ret != NULL) /* something is wrong, return the error */ + return ret; + /* check for heaptrace and java -- warn that it covers native allocations only */ + if (heaptrace_enabled == 1 && java_mode == 1 && java_default == 0) + *warn = strdup (GTXT ("Note: Heap profiling will only trace native allocations, not Java allocations.\n")); + + /* if no profiling data selected, warn the user */ + if (clkprof_enabled == 0 && hwcprof_enabled_cnt == 0 && synctrace_enabled == 0 + && heaptrace_enabled == 0 && iotrace_enabled == 0 && count_enabled == 0) + *warn = strdup (GTXT ("Warning: No function level data requested; only statistics will be collected.\n\n")); + build_data_desc (); + + /* verify that the directory exists */ + struct stat statbuf; + if (stat (store_dir, &statbuf) != 0) + return dbe_sprintf (GTXT ("Store directory %s is not accessible: %s\n"), + store_dir, strerror (errno)); + if (access (store_dir, W_OK) != 0) + return dbe_sprintf (GTXT ("Store directory %s is not writeable: %s\n"), + store_dir, strerror (errno)); + + /* if an experiment-group, verify that it can be written */ + ret = check_group (); + if (ret != NULL) + return ret; + return NULL; +} + +char * +Coll_Ctrl::show (int i) +{ + char UEbuf[4096]; + UEbuf[0] = 0; + if (i == 0) + { + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Collection parameters:\n")); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT (" experiment enabled\n")); + } + if (target_name != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\ttarget = %s\n"), target_name); + if (uexpt_name != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tuser_expt_name = %s\n"), uexpt_name); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texpt_name = %s\n"), + ((expt_name != NULL) ? expt_name : NTXT ("<NULL>"))); + if (udir_name != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdir_name = %s\n"), udir_name); + if (expt_group != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texpt_group = %s\n"), expt_group); + if (debug_mode == 1) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdebug_mode enabled\n")); + if (clkprof_enabled != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tclock profiling enabled, %.3f millisec.\n"), + (double) (clkprof_timer) / 1000.); + if (synctrace_enabled != 0) + { + if (synctrace_thresh < 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsynchronization tracing enabled, threshold: calibrate; ")); + else if (synctrace_thresh == 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsynchronization tracing enabled, threshold: all; ")); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsynchronization tracing enabled, threshold: %d micros.; "), synctrace_thresh); + switch (synctrace_scope) + { + case SYNCSCOPE_NATIVE: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Native-APIs\n")); + break; + case SYNCSCOPE_JAVA: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Java-APIs\n")); + break; + case SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Native- and Java-APIs\n")); + break; + default: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("ERR -- unexpected synctrace_scope %d\n"), synctrace_scope); + break; + } + } + if (hwcprof_enabled_cnt != 0) + { + char ctrbuf[MAXPATHLEN]; + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\thardware counter profiling%s enabled:\n"), + (hwcprof_default == 1 ? GTXT (" (default)") : "")); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t %u. %s\n"), ii + 1, + hwc_hwcentry_specd_string (ctrbuf, MAXPATHLEN, &hwctr[ii])); + } + if (heaptrace_enabled != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\theap tracing enabled, %s\n"), + (heaptrace_checkenabled == 0 ? GTXT ("no checking") : + (heaptrace_checkenabled == 1 ? GTXT ("over/underrun checking") : + GTXT ("over/underrun checking and pattern storing")))); + if (iotrace_enabled != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tI/O tracing enabled\n")); + switch (count_enabled) + { + case 0: + break; + case 1: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tcount data enabled\n")); + break; + case -1: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tstatic count data will be generated (for a.out only)\n")); + break; + } + switch (follow_mode) + { + case FOLLOW_ON: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdescendant processes will be followed\n")); + break; + case FOLLOW_ALL: + if (follow_spec_usr && follow_spec_cmp) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texperiments will be recorded for descendant processes that match pattern '%s'\n"), + follow_spec_usr); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdescendant processes will all be followed\n")); + break; + case FOLLOW_NONE: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdescendant processes will not be followed\n")); + break; + default: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tfollowing descendant processes: <UNKNOWN>\n")); + break; + } + if (java_mode == 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tjava profiling disabled\n")); + if (pauseresume_sig != 0) + { + const char *buf = strsignal (pauseresume_sig); + if (buf != NULL) + { + if (pauseresume_pause == 1) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tpause-resume (delayed initialization) signal %s (%d) -- paused\n"), buf, pauseresume_sig); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tpause-resume (delayed initialization) signal %s (%d)\n"), buf, pauseresume_sig); + } + else + { + if (pauseresume_pause == 1) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tpause-resume (delayed initialization) signal %d -- paused\n"), pauseresume_sig); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tpause-resume (delayed initialization) signal %d\n"), pauseresume_sig); + } + } + if (sample_sig != 0) + { + const char *buf = strsignal (sample_sig); + if (buf != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsample signal %s (%d)\n"), buf, sample_sig); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsample signal %d\n"), sample_sig); + } + if (time_run != 0 || start_delay != 0) + { + if (start_delay != 0) + { + if (time_run != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdata-collection duration, %d-%d secs.\n"), start_delay, time_run); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdata-collection duration, %d- secs.\n"), start_delay); + } + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdata-collection duration, %d secs.\n"), time_run); + } + if (sample_period != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tperiodic sampling, %d secs.\n"), sample_period); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tno periodic sampling\n")); + if (size_limit != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texperiment size limit %d MB.\n"), size_limit); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tno experiment size limit set\n")); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texperiment archiving: -a %s\n"), archive_mode); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdata descriptor: \"%s\"\n"), + ((data_desc != NULL) ? data_desc : NTXT ("<NULL>"))); +#if 0 + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t expt_dir: %s\n"), + ((expt_dir != NULL) ? expt_dir : NTXT ("<NULL>"))); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t base_name: %s\n"), + ((base_name != NULL) ? base_name : NTXT ("<NULL>"))); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t store_dir: %s\n"), + ((store_dir != NULL) ? store_dir : NTXT ("<NULL>"))); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t store_ptr: %s\n"), + ((store_ptr != NULL) ? store_ptr : NTXT ("<NULL>"))); +#endif + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t\thost: `%s', ncpus = %d, clock frequency %d MHz.\n"), + ((node_name != NULL) ? node_name : NTXT ("<NULL>")), + (int) ncpus, (int) cpu_clk_freq); + if (npages > 0) + { + long long memsize = ((long long) npages * (long long) page_size) / (1024 * 1024); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t\tmemory: %ld pages @ %ld bytes = %lld MB.\n"), + npages, page_size, memsize); + } + return strdup (UEbuf); +} + +#define MAX_COLLECT_ARGS 100 + +char ** +Coll_Ctrl::get_collect_args () +{ + char buf[DD_MAXPATHLEN]; + char **p; + char **argv = (char **) calloc (MAX_COLLECT_ARGS, sizeof (char *)); + if (argv == NULL) // poor way of dealing with calloc failure + abort (); + p = argv; + *p++ = strdup ("collect"); + if (debug_mode == 1) + *p++ = strdup ("-x"); + if (clkprof_enabled != 0) + { + *p++ = strdup ("-p"); + snprintf (buf, sizeof (buf), "%du", clkprof_timer); + *p++ = strdup (buf); + } + if (hwcprof_enabled_cnt > 0) + { + *buf = 0; + *p++ = strdup ("-h"); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + { + char*rateString = hwc_rate_string (&hwctr[ii], 1); //"1" is for temporary goldfile compatibility. TBR YXXX!! + snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), + "%s%s,%s%s", ii ? "," : "", hwctr[ii].name, + rateString ? rateString : "", + (ii + 1 < hwcprof_enabled_cnt) ? "," : ""); + free (rateString); + } + if (strlen (buf) + 1 >= sizeof (buf)) + abort (); + *p++ = strdup (buf); + } + if (heaptrace_enabled != 0) + { + *p++ = strdup ("-H"); + *p++ = strdup ("on"); + } + if (iotrace_enabled != 0) + { + *p++ = strdup ("-i"); + *p++ = strdup ("on"); + } + if (synctrace_enabled != 0) + { + *p++ = strdup ("-s"); + if (synctrace_thresh < 0) + *p++ = strdup ("calibrate"); + else if (synctrace_thresh < 0) + *p++ = strdup ("all"); + else + *p++ = dbe_sprintf ("%d", synctrace_thresh); + *p++ = dbe_sprintf (",%d", synctrace_scope); + } + if (follow_mode != 0) + { + *p++ = strdup ("-F"); + char * fs = get_follow_usr_spec (); + if (fs) + *p++ = strdup (fs); + else + { + switch (get_follow_mode ()) + { + case FOLLOW_ON: + *p++ = strdup ("on"); + break; + case FOLLOW_ALL: + *p++ = strdup ("all"); + break; + case FOLLOW_NONE: + default: + *p++ = strdup ("off"); + break; + } + } + } + *p++ = strdup ("-a"); + *p++ = strdup (get_archive_mode ()); + if (java_mode != 0) + { + *p++ = strdup ("-j"); + *p++ = strdup ("on"); + } + if (pauseresume_sig != 0) + { + *p++ = strdup ("-y"); + *p++ = dbe_sprintf ("%d%s", pauseresume_sig, + (pauseresume_pause == 0 ? ",r" : "")); + } + if (sample_sig != 0) + { + *p++ = strdup ("-l"); + *p++ = dbe_sprintf ("%d", sample_sig); + } + if (sample_period != 0) + { + *p++ = strdup ("-S"); + *p++ = dbe_sprintf ("%d", sample_period); + } + if (size_limit != 0) + { + *p++ = strdup ("-L"); + *p++ = dbe_sprintf ("%d", size_limit); + } + if (expt_group != NULL) + { + *p++ = strdup ("-g"); + *p++ = strdup (expt_group); + } + if (udir_name != 0) + { + *p++ = strdup ("-d"); + *p++ = strdup (udir_name); + } + if (expt_name != 0) + { + *p++ = strdup ("-o"); + *p++ = strdup (expt_name); + } + if (p - argv >= MAX_COLLECT_ARGS) // argument list too small -- fatal error + abort (); + return argv; +} + +char * +Coll_Ctrl::show_expt () +{ + if (enabled == 0) + return NULL; + char UEbuf[4096]; + UEbuf[0] = 0; + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Creating experiment directory %s (Process ID: %ld) ...\n"), + ((store_ptr != NULL) ? store_ptr : NTXT ("<NULL>")), (long) getpid ()); + char *caller = getenv ("SP_COLLECTOR_FROM_GUI"); // Collector from GUI + if (caller != NULL) // Print non-localized message + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + NTXT ("\nCreating experiment directory %s (Process ID: %ld) ...\n"), + ((store_ptr != NULL) ? store_ptr : NTXT ("<NULL>")), (long) getpid ()); +#if 0 + char *fstype = get_fstype (store_dir); + if ((fstype != NULL) && (nofswarn == 0)) + { + // only warn if clock or hwc profiling is turned on + if (clkprof_enabled || hwcprof_enabled_cnt != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("this experiment is being recorded to a file system \nof type \"%s\", which may distort the measured performance."), + fstype); + } +#endif + return strdup (UEbuf); +} + +void +Coll_Ctrl::set_clk_params (int min, int res, int max, int hi, int norm, int lo) +{ + clk_params.min = min; + clk_params.res = res; + clk_params.max = max; + clk_params.hival = hi; + clk_params.normval = norm; + clk_params.lowval = lo; + set_clkprof_timer_target (clk_params.normval); // note: requires clk_params to be initialized! +} + +char * +Coll_Ctrl::reset_clkprof (int val) +{ + if (val != clkprof_timer) + { + // profiler has had to reset to a different value; warn user + char *msg = dbe_sprintf ( + GTXT ("Warning: Clock profiling timer reset from %.3f millisec. to %.3f millisec. as required by profiling driver\n\n"), + (double) (clkprof_timer) / 1000., (double) (val) / 1000.); + adjust_clkprof_timer (val); + return msg; + } + return NULL; +} + +char * +Coll_Ctrl::set_clkprof (const char *string, char** warn) +{ + int ticks; + int nclkprof_timer; + int prevclkprof_enabled; + int prevclkprof_default; + *warn = NULL; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + /* if the first character is a +, warn user that it is no longer supported */ + if (string[0] == '+') + return strdup (GTXT ("Warning: clock-based memoryspace and dataspace profiling is no longer supported\n")); + if (strcmp (string, "off") == 0) + { + clkprof_enabled = 0; + clkprof_default = 0; + return NULL; + } + else if (string == NULL || strcmp (string, "on") == 0) + nclkprof_timer = clk_params.normval; + else if (strcmp (string, "lo") == 0 || strcmp (string, "low") == 0) + nclkprof_timer = clk_params.lowval; + else if (strcmp (string, "hi") == 0 || strcmp (string, "high") == 0 + || strcmp (string, "h") == 0) + nclkprof_timer = clk_params.hival; + else + { + /* the remaining string should be a number > 0 */ + char *endchar = NULL; + double dval = strtod (string, &endchar); + if (*endchar == 'm' || *endchar == 0) /* user specified milliseconds */ + dval = dval * 1000.; + else if (*endchar == 'u') /* user specified microseconds */ + dval = dval; + else + return dbe_sprintf (GTXT ("Unrecognized clock-profiling interval `%s'\n"), string); + nclkprof_timer = (int) (dval + 0.5); + } + // we now have the proposed value; ensure it's within limits + if (nclkprof_timer <= 0) + return dbe_sprintf (GTXT ("Unrecognized clock-profiling interval `%s'\n"), string); + + // Check consistency with experiment + prevclkprof_enabled = clkprof_enabled; + prevclkprof_default = clkprof_default; + clkprof_enabled = 1; + clkprof_default = 0; + char *ret = check_consistency (); + if (ret != NULL) + { + clkprof_default = prevclkprof_default; + clkprof_enabled = prevclkprof_enabled; + return ret; + } + int ref_nclkprof_timer = nclkprof_timer; + + // check for minimum value + if (nclkprof_timer < clk_params.min) + { + /* value too small, use minimum value, with warning */ + *warn = dbe_sprintf ( + GTXT ("Warning: Clock profiling at %.3f millisec. interval is not supported on this system; minimum %.3f millisec. used\n"), + (double) (nclkprof_timer) / 1000., (double) (clk_params.min) / 1000.); + nclkprof_timer = clk_params.min; + } + + // check for maximum value + if (nclkprof_timer > clk_params.max) + { + *warn = dbe_sprintf ( + GTXT ("Clock profiling at %.3f millisec. interval is not supported on this system; maximum %.3f millisec. used\n"), + (double) (nclkprof_timer) / 1000., (double) (clk_params.max) / 1000.); + nclkprof_timer = clk_params.max; + } + + /* see if setting is a multiple of the period */ + if (nclkprof_timer > clk_params.res) + { + ticks = ((nclkprof_timer / clk_params.res) * clk_params.res); + if (ticks != nclkprof_timer) + { + /* no, we need to reset to a multiple */ + *warn = dbe_sprintf ( + GTXT ("Clock profile interval rounded from %.3f to %.3f (system resolution = %.3f) millisec."), + (double) (nclkprof_timer) / 1000., (double) (ticks) / 1000., + (double) (clk_params.res) / 1000.); + nclkprof_timer = ticks; + } + } + + // limit reference "target" rate. Target rate is also used for HWCS. + if (ref_nclkprof_timer > PROFINT_MAX) + ref_nclkprof_timer = PROFINT_MAX; + if (ref_nclkprof_timer < PROFINT_MIN) + ref_nclkprof_timer = PROFINT_MIN; + set_clkprof_timer_target (ref_nclkprof_timer); + adjust_clkprof_timer (nclkprof_timer); + return NULL; +} + +char * +Coll_Ctrl::set_synctrace (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + char *comma_p = NULL; + if (string == NULL) + { + /* no argument provided, use default: calibrate and native */ + synctrace_enabled = 1; + synctrace_thresh = -1; + synctrace_scope = SYNCSCOPE_NATIVE; + char *ret = check_consistency (); + if (ret != NULL) + { + synctrace_enabled = 0; + return ret; + } + return NULL; + } + char *val = strdup (string); + /* see if there's a comma in the string */ + char *next = strchr (val, (int) ','); + if (next != NULL) + { + /* remember where the comma was */ + comma_p = next; + + /* set the scope based on the characters following the comma */ + synctrace_scope = 0; + next++; + while (*next != 0) + { + if (*next == 'n') + synctrace_scope |= SYNCSCOPE_NATIVE; + else if (*next == 'j') + synctrace_scope |= SYNCSCOPE_JAVA; + else + return dbe_sprintf (GTXT ("Unrecognized synchronization tracing threshold `%s'\n"), string); + next++; + } + if (synctrace_scope == 0) + synctrace_scope = SYNCSCOPE_NATIVE; + /* clear the comma for the threshold determination */ + *comma_p = 0; + } + else /* no ",<scope>" -- default to native and Java */ + synctrace_scope = SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA; + if (!strlen (val) || !strcmp (val, "calibrate") || !strcmp (val, "on")) + { + /* use default: calibrate and native */ + synctrace_enabled = 1; + synctrace_thresh = -1; + free (val); + char *ret = check_consistency (); + if (ret != NULL) + { + synctrace_enabled = 0; + return ret; + } + return NULL; + } + if (strcmp (val, "off") == 0) + { + synctrace_enabled = 0; + free (val); + return NULL; + } + if (strcmp (val, "all") == 0) + { + /* set to record all events */ + synctrace_thresh = 0; + synctrace_enabled = 1; + char *ret = check_consistency (); + free (val); + if (ret != NULL) + { + synctrace_enabled = 0; + return ret; + } + return NULL; + } + /* the remaining string should be a number >= 0 */ + char *endchar = NULL; + int tval = (int) strtol (val, &endchar, 0); + free (val); + if (*endchar != 0 || tval < 0) + { + /* invalid setting */ + /* restore the comma, if it was zeroed out */ + if (comma_p != NULL) + *comma_p = ','; + return dbe_sprintf (GTXT ("Unrecognized synchronization tracing threshold `%s'\n"), string); + } + synctrace_thresh = tval; + synctrace_enabled = 1; + return NULL; +} + +char * +Coll_Ctrl::set_heaptrace (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0) + { + heaptrace_enabled = 1; + char *ret = check_consistency (); + if (ret != NULL) + { + heaptrace_enabled = 0; + return ret; + } + return NULL; + } + if (strcmp (string, "off") == 0) + { + heaptrace_enabled = 0; + return NULL; + } +#if 0 + if (strcmp (string, "check") == 0) + { + /* set to check for over/underruns */ + heaptrace_checkenabled = 1; + heaptrace_enabled = 1; + return NULL; + } + if (strcmp (string, "clear") == 0) + { + /* set to check for over/underruns, and store patterns */ + heaptrace_checkenabled = 2; + heaptrace_enabled = 1; + return NULL; + } +#endif + return dbe_sprintf (GTXT ("Unrecognized heap tracing parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_iotrace (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0) + { + iotrace_enabled = 1; + char *ret = check_consistency (); + if (ret != NULL) + { + iotrace_enabled = 0; + return ret; + } + return NULL; + } + if (strcmp (string, "off") == 0) + { + iotrace_enabled = 0; + return NULL; + } + return dbe_sprintf (GTXT ("Unrecognized I/O tracing parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_count (const char *string) +{ + int ret = -1; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "off") == 0) + { + count_enabled = 0; + ret = 0; + } + if (strcmp (string, "on") == 0) + { + count_enabled = 1; + char *cret = check_consistency (); + if (cret != NULL) + { + count_enabled = 0; + return cret; + } + ret = 0; + } + if (strcmp (string, "static") == 0) + { + count_enabled = -1; + char *cret = check_consistency (); + if (cret != NULL) + { + count_enabled = 0; + return cret; + } + ret = 0; + } + if (ret == 0) + { + if (count_enabled != 0) + { + /* ensure that sample period is 0, if set by default */ + if (sample_default == 1) + sample_period = 0; + /* ensure that clock profiling is off, if set by default */ + if (clkprof_default == 1) + { + clkprof_default = 0; + clkprof_enabled = 0; + } + if (hwcprof_default == 1) + hwcprof_default = 0; + } + return NULL; + } + return dbe_sprintf (GTXT ("Unrecognized count parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_time_run (const char *valarg) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (valarg == NULL) /* invalid setting */ + return strdup (GTXT ("time parameter can not be NULL\n")); + /* the string should be a number >= 0 */ + int prev_start_delay = start_delay; + int prev_time_run = time_run; + const char *endchar = valarg; + char *newchar = NULL; + int val = 0; + if (*endchar != '-') + { + val = (int) strtol (endchar, &newchar, 0); + endchar = newchar; + if (val < 0) + return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg); + if (*endchar == 'm') + { + val = val * 60; /* convert to seconds */ + endchar++; + } + else if (*endchar == 's') /* no conversion needed */ + endchar++; + if (*endchar == 0) + { + time_run = val; + return NULL; + } + else if (*endchar != '-') + return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg); + } + /* a second number is provided */ + start_delay = val; + endchar++; + val = (int) strtol (endchar, &newchar, 0); + endchar = newchar; + if (val < 0) + { + start_delay = prev_start_delay; + return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg); + } + if (*endchar == 'm') + { + val = val * 60; /* convert to seconds */ + endchar++; + } + else if (*endchar == 's') /* no conversion needed */ + endchar++; + if (*endchar != 0) + { + start_delay = prev_start_delay; + return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg); + } + time_run = val; + if (time_run != 0 && start_delay >= time_run) + { + start_delay = prev_start_delay; + return dbe_sprintf (GTXT ("Invalid time parameter `%s': start time must be earlier than end time\n"), valarg); + } + char *ret = check_consistency (); + if (ret != NULL) + { + start_delay = prev_start_delay; + time_run = prev_time_run; + return ret; + } + return NULL; +} + +char * +Coll_Ctrl::set_attach_pid (char *valarg) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (valarg == NULL) + return strdup (GTXT ("Specified PID can not be NULL\n")); + + /* the string should be a number corresponding to an active process' pid */ + char *endchar = NULL; + int val = (int) strtol (valarg, &endchar, 0); + if (*endchar != 0 || val < 0) + return dbe_sprintf (GTXT ("Invalid process pid `%s'\n"), valarg); + int prev_attach_pid = attach_pid; + attach_pid = val; + char *ret = check_consistency (); + if (ret != NULL) + { + attach_pid = prev_attach_pid; + return ret; + } + return NULL; +} + +void +Coll_Ctrl::free_hwc_fields (Hwcentry * tmpctr) +{ + if (tmpctr->name != NULL) + free (tmpctr->name); + if (tmpctr->int_name != NULL) + free (tmpctr->int_name); + memset (tmpctr, 0, sizeof (Hwcentry)); + tmpctr->reg_num = -1; +} + +void +Coll_Ctrl::hwcentry_dup (Hwcentry *hnew, Hwcentry *_hwc) +{ + *hnew = *_hwc; + if (_hwc->name != NULL) + hnew->name = strdup (_hwc->name); + else + hnew->name = NULL; + if (_hwc->int_name != NULL) + hnew->int_name = strdup (_hwc->int_name); + else + hnew->int_name = NULL; + if (_hwc->metric != NULL) + hnew->metric = strdup (_hwc->metric); + else + hnew->metric = NULL; + if (_hwc->short_desc != NULL) + hnew->short_desc = strdup (_hwc->short_desc); + else + hnew->short_desc = NULL; + if (_hwc->reg_list != NULL) + { + hnew->reg_list = (regno_t*) malloc (sizeof (regno_t*) * MAX_PICS); + // poor way of dealing with malloc failure + if (hnew->reg_list) + { + for (int i = 0; i < MAX_PICS; i++) + { + hnew->reg_list[i] = _hwc->reg_list[i]; + if (hnew->reg_list[i] == REGNO_ANY) + break; + } + } + } +} + +// Routine to initialize the HWC tables, set up the default experiment, etc. +void +Coll_Ctrl::setup_hwc () +{ + static bool is_hwc_setup = false; + if (is_hwc_setup == true) + return; + // try to set the default counters + is_hwc_setup = true; + set_hwcdefault (); +} + +hrtime_t +Coll_Ctrl::clkprof_timer_2_hwcentry_min_time (int target_clkprof_usec) +{ + hrtime_t hwc_nanosec; + if (target_clkprof_usec == clk_params.normval) + hwc_nanosec = HWCTIME_ON; + else if (target_clkprof_usec == clk_params.lowval) + hwc_nanosec = HWCTIME_LO; + else if (target_clkprof_usec == clk_params.hival) + hwc_nanosec = HWCTIME_HI; + else + hwc_nanosec = 1000LL * target_clkprof_usec; // nanoseconds + return hwc_nanosec; +} + +void +Coll_Ctrl::set_clkprof_timer_target (int microseconds) +{ + clkprof_timer = microseconds; + clkprof_timer_target = microseconds; + hrtime_t hwc_min_time_nanosec = clkprof_timer_2_hwcentry_min_time (microseconds); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + { + hwctr[ii].min_time_default = hwc_min_time_nanosec; + hwc_update_val (&hwctr[ii]); + } +} + +void +Coll_Ctrl::adjust_clkprof_timer (int use) +{ + clkprof_timer = use; +} + +/* set HWC counter set from a string */ +char * /* return an error string */ +Coll_Ctrl::set_hwcstring (const char *string, char **warnmsg) +{ + *warnmsg = NULL; + if (string == NULL || strcmp (string, "off") == 0) + { + hwcprof_enabled_cnt = 0; + return NULL; + } + setup_hwc (); + int old_cnt = hwcprof_enabled_cnt; + int old_hwcprof_default = hwcprof_default; + + /* reset any previous count to zero */ + hwcprof_enabled_cnt = 0; + char *ret = add_hwcstring (string, warnmsg); + if (ret != NULL) + { + // restore previous setting + hwcprof_enabled_cnt = old_cnt; + hwcprof_default = old_hwcprof_default; + } + return ret; +} + +/* add additional HWC counters to counter set from string */ +char * /* return an error string */ +Coll_Ctrl::add_hwcstring (const char *string, char **warnmsg) +{ + *warnmsg = NULL; + if (string == NULL || strcmp (string, "off") == 0) + { + hwcprof_enabled_cnt = 0; + return NULL; + } + setup_hwc (); + int rc = 0; + int old_cnt = hwcprof_enabled_cnt; + int prev_cnt = hwcprof_enabled_cnt; + // int old_hwcprof_default = hwcprof_default; + char UEbuf[MAXPATHLEN * 5]; + int UEsz; + Hwcentry tmpctr[MAX_PICS]; + Hwcentry * ctrtable[MAX_PICS]; + char *emsg; + char *wmsg; + UEbuf[0] = 0; + UEsz = sizeof (UEbuf); + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (hwcprof_default == 0) + { + /* Copy the counters already defined */ + for (int ii = 0; ii < prev_cnt; ii++) + tmpctr[ii] = hwctr[ii]; + } + else /* the previously-defined counters were defaulted; don't copy them */ + prev_cnt = 0; + + /* look up the CPU version */ + cpc_cpuver = hwc_get_cpc_cpuver (); + if (string && *string) + { + /* lookup counters */ + /* set up a pointer array */ + for (unsigned ii = 0; ii < MAX_PICS; ii++) + ctrtable[ii] = &tmpctr[ii]; + hrtime_t global_min_time = clkprof_timer_2_hwcentry_min_time (clkprof_timer_target); + rc = hwc_lookup (kernelHWC, global_min_time, string, &ctrtable[prev_cnt], MAX_PICS - prev_cnt, &emsg, &wmsg); + if (wmsg != NULL) + *warnmsg = wmsg; + if (rc < 0) + return emsg; + /* set count for sum of old and new counters */ + rc = rc + prev_cnt; + } + + /* even though the actual hwctr[] array is not updated, we can check consistency */ + char *ret = check_consistency (); + if (ret != NULL) + { + hwcprof_enabled_cnt = old_cnt; + return ret; + } + + /* finally, validate the full counter set */ + emsg = hwc_validate_ctrs (kernelHWC, ctrtable, rc); + if (emsg != NULL) + { + hwcprof_enabled_cnt = old_cnt; + return emsg; + } + + /* success, update real counters and the string for them */ + /* turn off the default */ + hwcprof_default = 0; + hwcprof_enabled_cnt = rc; + free (hwc_string); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + { + /* shallow copy of new counters */ + hwctr[ii] = tmpctr[ii]; + char *rateString = hwc_rate_string (&hwctr[ii], 0); + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + NTXT (",%s,%s"), hwctr[ii].name, + rateString ? rateString : ""); + free (rateString); + } + /* now duplicate that string, skipping the leading comma */ + hwc_string = strdup (&UEbuf[1]); + return NULL; +} + +/* add default HWC counters to counter set with resolution (on, hi, or lo) */ +/* Note that the resultion will also be used to set the clock-profiling default */ +char * /* return an error string */ +Coll_Ctrl::add_default_hwcstring (const char *resolution, char **warnmsg, bool add, bool forKernel) +{ + setup_hwc (); + *warnmsg = NULL; + char *def_string = hwc_get_default_cntrs2 (forKernel, 1); + if (def_string == NULL) + { + /* no string defined, format and return an error message */ + char cpuname[128]; + hwc_get_cpuname (cpuname, sizeof (cpuname)); + return dbe_sprintf (GTXT ("No default HW counter set is defined for %s\n"), cpuname); + } + int len = strlen (def_string); + if (len == 0) + { + /* string zero-length, meaning default counters can't be used */ + char cpuname[128]; + hwc_get_cpuname (cpuname, sizeof (cpuname)); + return dbe_sprintf (GTXT ("HW counter set for %s cannot be loaded on this system\n"), cpuname); + } + /* allocate return string */ + int retsize = 2 * len + 10; + char *ret = (char *) malloc (retsize); + if (ret == NULL) + return strdup (GTXT ("internal error formating HW counter set; malloc failed\n")); + *ret = 0; + char *retp = ret; + char *stringp = def_string; + int first = 1; + char *hwc_defaultx = strdup (def_string); + + /* now massage the string in order to insert resolution for each counter */ + for (;;) + { + /* find the next comma */ + char * next; + char *nextp; + if (first == 1) + nextp = stringp; + else + nextp = stringp + 1; + first = 0; + if ((next = strchr (nextp, (int) ',')) != NULL) + { + if (next == nextp) + { + /* next counter is zero-length -- invalid string */ + char cpuname[128]; + hwc_get_cpuname (cpuname, sizeof (cpuname)); + free (ret); + ret = dbe_sprintf (GTXT ("HW counter set for %s, \"%s\", format error\n"), cpuname, hwc_defaultx); + free (hwc_defaultx); + return ret; + } + /* another field found */ + *next = 0; + char nextc = *(next + 1); + if ((nextc == 0) || (nextc == ',')) + { + /* either ,, between fields, or string ends in comma */ + /* append the string */ + strncat (retp, stringp, (retsize - strlen (retp) - 1)); + strncat (retp, ",", (retsize - strlen (retp) - 1)); + strncat (retp, resolution, (retsize - strlen (retp) - 1)); + if (nextc == 0) /* string ended in comma; we're done */ + break; + } + else + { + /* string had only one comma between counter names; that's not valid */ + char cpuname[128]; + hwc_get_cpuname (cpuname, sizeof (cpuname)); + free (ret); + ret = dbe_sprintf (GTXT ("HW counter set for %s, \"%s\", format error\n"), cpuname, hwc_defaultx); + free (hwc_defaultx); + return ret; + } + /* string had ,, between fields; move to next field */ + stringp = next + 1; + if (* (stringp + 1) == 0) /* name ended in ,, -- we're done */ + break; + continue; + } + else + { + /* no comma found, add the last counter and the comma and resolution */ + strncat (retp, stringp, (retsize - strlen (retp) - 1)); + strncat (retp, ",", (retsize - strlen (retp) - 1)); + strncat (retp, resolution, (retsize - strlen (retp) - 1)); + break; + } + } + + /* we have now formatted the new string, with resolution inserted */ + char *ccret; + if (add == true) + ccret = add_hwcstring (ret, warnmsg); + else + ccret = set_hwcstring (ret, warnmsg); + free (hwc_defaultx); + free (ret); + + /* now set the clock-profiling timer, if on by default */ + if (clkprof_default == 1) + { + if (strcmp (resolution, NTXT ("on")) == 0) + set_clkprof_timer_target (clk_params.normval); + else if (strcmp (resolution, NTXT ("lo")) == 0) + set_clkprof_timer_target (clk_params.lowval); + else if (strcmp (resolution, NTXT ("hi")) == 0) + set_clkprof_timer_target (clk_params.hival); + } + return ccret; +} + +void +Coll_Ctrl::set_hwcdefault () +{ + char *string = hwc_get_default_cntrs2 (kernelHWC, 1); + if (string != NULL) + { + if (strlen (string) == 0) + hwcprof_default = 0; + else + { + char * warnmsg = NULL; + char *ccret = add_hwcstring (string, &warnmsg); + if (ccret != NULL) + { +#if 0 + /* set string to zero-length so that it won't be used again */ + hwc_set_default_cntrs (kernelHWC, NTXT ("")); +#endif + hwcprof_default = 0; + } + else + hwcprof_default = 1; + } + free (string); + } + else + hwcprof_default = 0; +} + +void +Coll_Ctrl::disable_hwc () +{ + hwcprof_enabled_cnt = 0; + hwcprof_default = 0; + free (hwc_string); + hwc_string = NULL; +} + +char * +Coll_Ctrl::set_sample_period (const char *string) +{ + int val; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strcmp (string, "on") == 0) + val = 1; + else if (strcmp (string, "off") == 0) + val = 0; + else + { + /* string should be a number > 0 */ + char *endchar = NULL; + val = (int) strtol (string, &endchar, 0); + if (*endchar != 0 || val <= 0) + return dbe_sprintf (GTXT ("Unrecognized sample period `%s'\n"), string); + } + /* set that value */ + int prev_sample_period = sample_period; + sample_period = val; + char *ret = check_consistency (); + if (ret != NULL) + { + sample_period = prev_sample_period; + return ret; + } + sample_default = 0; + return NULL; +} + +char * +Coll_Ctrl::set_size_limit (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 + || strcmp (string, "unlimited") == 0 || strcmp (string, "none") == 0) + { + size_limit = 0; + return NULL; + } + /* string should be a number >0; 0 is an error */ + char *endchar = NULL; + int val = (int) strtol (string, &endchar, 0); + if (*endchar != 0 || val <= 0) + return dbe_sprintf (GTXT ("Unrecognized size limit `%s'\n"), string); + size_limit = val; + return 0; +} + +void +Coll_Ctrl::build_data_desc () +{ + char spec[DD_MAXPATHLEN]; + spec[0] = 0; + + // Put sample sig before clock profiling. Dbx uses PROF + // for that purpose and we want it to be processed first. + if (project_home) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "P:%s;", project_home); + if (sample_sig != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "g:%d;", sample_sig); + if (pauseresume_sig != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "d:%d%s;", pauseresume_sig, + (pauseresume_pause == 1 ? "p" : "")); + if (clkprof_enabled == 1) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "p:%d;", clkprof_timer); + if (synctrace_enabled == 1) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "s:%d,%d;", synctrace_thresh, synctrace_scope); + if (heaptrace_enabled == 1) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "H:%d;", heaptrace_checkenabled); + if (iotrace_enabled == 1) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "i:;"); + if (hwcprof_enabled_cnt > 0) + { + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "h:%s", + (hwcprof_default == true) ? "*" : ""); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + { + /* min_time is a "new" field. + * + * To help process_data_descriptor() in hwcfuncs.c parse + * the HWC portion of this string -- specifically, to + * recognize min_time when it's present and skip over + * when it's not -- we prepend 'm' to the min_time value. + * + * When we no longer worry about, say, an old dbx + * writing this string and a new libcollector looking for + * the min_time field, the 'm' character can be + * removed and process_data_descriptor() simplified. + */ + hrtime_t min_time = hwctr[ii].min_time; + if (min_time == HWCTIME_TBD) + // user did not specify any value for overflow rate + min_time = hwctr[ii].min_time_default; + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), + "%s%s:%s:%d:%d:m%lld:%d:%d:0x%x", ii ? "," : "", + strcmp (hwctr[ii].name, hwctr[ii].int_name) ? hwctr[ii].name : "", + hwctr[ii].int_name, hwctr[ii].reg_num, hwctr[ii].val, + min_time, ii, /*tag*/ hwctr[ii].timecvt, hwctr[ii].memop); + } + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), ";"); + } + if ((time_run != 0) || (start_delay != 0)) + { + if (start_delay != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "t:%d:%d;", start_delay, time_run); + else + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "t:%d;", time_run); + } + if (sample_period != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "S:%d;", + sample_period); + if (size_limit != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "L:%d;", + size_limit); + if (java_mode != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "j:%d;", (int) java_mode); + if (follow_mode != FOLLOW_NONE) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "F:%d;", (int) follow_mode); + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "a:%s;", archive_mode); + if (strlen (spec) + 1 >= sizeof (spec)) + abort (); + free (data_desc); + data_desc = strdup (spec); +} + +char * +Coll_Ctrl::check_group () +{ + char group_file[MAXPATHLEN]; + if (expt_group == NULL) + return NULL; + // Is the group an relative path, with a store directory set? + if ((expt_group[0] == '/') || ((udir_name == NULL) || (udir_name[0] == '0'))) + snprintf (group_file, sizeof (group_file), "%s", expt_group); + else // relative path, store directory; make group_file in that directory + snprintf (group_file, sizeof (group_file), "%s/%s", udir_name, expt_group); + // See if we can write the group file + int ret = access (group_file, W_OK); + if (ret != 0) + { + if (errno == ENOENT) + { + char *stmp = group_file; + char *dir = dirname (stmp); + ret = access (dir, W_OK); + if (ret != 0) // group file does not exist; + return dbe_sprintf (GTXT ("Directory (%s) for group file %s is not writeable: %s\n"), + dir, group_file, strerror (errno)); + } + else + return dbe_sprintf (GTXT ("Group file %s is not writeable: %s\n"), + group_file, strerror (errno)); + } + return NULL; +} + +char * +Coll_Ctrl::join_group () +{ + int tries = 0; + int groupfd; + FILE *file; + char group_file[MAXPATHLEN]; + struct stat statbuf; + struct flock flockbuf; + flockbuf.l_type = F_WRLCK; + flockbuf.l_whence = SEEK_SET; + flockbuf.l_start = 0; + flockbuf.l_len = 0; + if (expt_group == NULL) + return NULL; + // Is the group an relative path, with a store directory set? + if (expt_group[0] == '/' || udir_name == NULL || udir_name[0] == '0') + snprintf (group_file, sizeof (group_file), "%s", expt_group); + else // relative path, store directory; make group_file in that directory + snprintf (group_file, sizeof (group_file), "%s/%s", udir_name, expt_group); + for (;;) + { + tries++; + // try to open the group file + while ((groupfd = open (group_file, O_RDWR)) >= 0) + { + if (uinterrupt == 1) + { + close (groupfd); + return strdup (GTXT ("user interrupt\n")); + } + // it's opened, now lock it + if (fcntl (groupfd, F_SETLK, &flockbuf) != -1) + { + // we got the lock; check the file size + if (fstat (groupfd, &statbuf) != 0) + { + // can't stat the file -- give up + close (groupfd); + return dbe_sprintf (GTXT ("Can't fstat group file %s\n"), group_file); + } + if (statbuf.st_size == 0) + { + // size is zero: we got the lock just as someone + // else created the group file + // close the file and release the lock; try again + close (groupfd); + continue; + } + else + { + // size is non-zero, add our record + file = fdopen (groupfd, "a"); + if (file == NULL) + { + close (groupfd); + return dbe_sprintf (GTXT ("Can't access group file %s\n"), group_file); + } + if (fprintf (file, "%s\n", store_ptr) <= 0) + { + fclose (file); + return dbe_sprintf (GTXT ("Can't update group file %s\n"), group_file); + } + // close the file, releasing our lock + fclose (file); + return NULL; + } + } + else + { + // can't get the lock, close the file and try again + close (groupfd); + if (uinterrupt == 1) + return strdup (GTXT ("user interrupt\n")); + if (tries == 11900) + return dbe_sprintf (GTXT ("Timed out: waiting for group file %s\n"), group_file); +#if 0 + if (tries % 500 == 0) + USR_WARN (GTXT ("Waiting for group file %s . . ."), group_file); +#endif + usleep (10000U); + continue; + } + } + // If the error was not that the file did not exist, report it + if (errno != ENOENT) + return dbe_sprintf (GTXT ("Can't open group file %s: %s\n"), + group_file, strerror (errno)); + // the file did not exist, try to create it + groupfd = open (group_file, O_CREAT | O_EXCL | O_RDWR, 0666); + if (groupfd < 0) + { + // we could not create the file + if (errno == EEXIST) + continue; + return dbe_sprintf (GTXT ("Can't create group file %s: %s\n"), + group_file, strerror (errno)); + } + // we created the group file, now lock it, waiting for the lock + while (fcntl (groupfd, F_SETLKW, &flockbuf) == -1) + { + // we created the file, but couldn't lock it + if (errno != EINTR) + return dbe_sprintf (GTXT ("Unable to lock group file %s\n"), group_file); + } + // we created and locked the file, write to it + file = fdopen (groupfd, "a"); + if (file == NULL) + { + close (groupfd); + return dbe_sprintf (GTXT ("Can't access group file %s\n"), group_file); + } + // write the header line + if (fprintf (file, "%s\n", SP_GROUP_HEADER) <= 0) + { + fclose (file); + return dbe_sprintf (GTXT ("Can't initialize group file %s\n"), group_file); + } + if (fprintf (file, "%s\n", store_ptr) <= 0) + { + fclose (file); + return dbe_sprintf (GTXT ("Can't update group file %s\n"), group_file); + } + // finally, close the file, releasing the lock + fclose (file); + return NULL; + } + // never reached +} + +char * +Coll_Ctrl::set_directory (char *dir, char **warn) +{ + struct stat statbuf; + *warn = NULL; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (stat (dir, &statbuf) != 0) + return dbe_sprintf (GTXT ("Can't set directory `%s': %s\n"), + dir, strerror (errno)); + if (!S_ISDIR (statbuf.st_mode)) + return dbe_sprintf (GTXT ("Can't set directory `%s': %s\n"), + dir, strerror (ENOTDIR)); + free (udir_name); + udir_name = strdup (dir); + + // Process new setting + *warn = preprocess_names (); + if ((uexpt_name != NULL) || (interactive != 0)) + { + char *ret = update_expt_name (true, true); + if (ret != NULL) + { + if (*warn != NULL) + { + char *msg = dbe_sprintf ("%s%s", *warn, ret); + free (*warn); + free (ret); + *warn = msg; + } + else + *warn = ret; + } + } + else + (void) update_expt_name (false, false); + return NULL; // All is OK +} + +int +Coll_Ctrl::set_target (char* targetname) +{ + free (target_name); + target_name = NULL; + if (targetname != NULL) + target_name = strdup (targetname); + return 0; +} + +void +Coll_Ctrl::set_default_stem (const char* stem) +{ + default_stem = strdup (stem); + preprocess_names (); + (void) update_expt_name (false, false); // no warnings +} + +char * +Coll_Ctrl::set_expt (const char *ename, char **warn, bool overwriteExp) +{ + *warn = NULL; + if (ename == NULL) + { + free (uexpt_name); + uexpt_name = NULL; + return NULL; + } + char *exptname = canonical_path(strdup(ename)); + size_t i = strlen (exptname); + if (i < 4 || strcmp (&exptname[i - 3], ".er") != 0) + { + free (exptname); + return dbe_sprintf (GTXT ("Experiment name `%s' must end in `.er'\n"), + ename); + } + // Name is OK + free (uexpt_name); + uexpt_name = exptname; + preprocess_names (); + char *err = update_expt_name (true, true, overwriteExp); + if (err != NULL) + return err; + if (overwriteExp) + { + char *nm = dbe_sprintf ("%s/%s", store_dir, base_name); + struct stat statbuf; + char *cmd = dbe_sprintf ("/bin/rm -rf %s >/dev/null 2>&1", nm); + system (cmd); + free (cmd); + if (stat (nm, &statbuf) == 0) + return dbe_sprintf (GTXT ("Cannot remove experiment `%s'\n"), nm); + if (errno != ENOENT) + return dbe_sprintf (GTXT ("Cannot remove experiment `%s'\n"), nm); + free (nm); + } + *warn = update_expt_name (true, false); + return NULL; +} + +char * +Coll_Ctrl::set_group (char *groupname) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (expt_group != NULL) + { + free (expt_group); + expt_group = NULL; + } + if (groupname == NULL) + { + // reset the name + preprocess_names (); + (void) update_expt_name (true, false); + return NULL; + } + int i = (int) strlen (groupname); + if (i < 5 || strcmp (&groupname[i - 4], ".erg") != 0) + return dbe_sprintf (GTXT ("Experiment group name `%s'must end in `.erg'\n"), groupname); + expt_group = strdup (groupname); + preprocess_names (); + (void) update_expt_name (true, false); + return NULL; +} + +char * +Coll_Ctrl::set_java_mode (const char *string) +{ + struct stat statbuf; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0) + { +#if defined(GPROFNG_JAVA_PROFILING) + int prev_java_mode = java_mode; + int prev_java_default = java_default; + java_mode = 1; + java_default = 0; + char *ret = check_consistency (); + if (ret != NULL) + { + java_mode = prev_java_mode; + java_default = prev_java_default; + return ret; + } + return NULL; +#else + return strdup (GTXT ("gprofng was built without support for profiling Java applications\n")); +#endif + } + if (strcmp (string, "off") == 0) + { + int prev_java_mode = java_mode; + int prev_java_default = java_default; + java_mode = 0; + java_default = 0; + char *ret = check_consistency (); + if (ret != NULL) + { + java_mode = prev_java_mode; + java_default = prev_java_default; + return ret; + } + free (java_path); + java_path = NULL; + return NULL; + } + /* any other value should be a path to Java installation directory */ + if (stat (string, &statbuf) == 0) + { + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + { + // it's a directory -- set the Java path to it + int prev_java_mode = java_mode; + int prev_java_default = java_default; + java_mode = 1; + java_default = 0; + char *ret = check_consistency (); + if (ret != NULL) + { + java_mode = prev_java_mode; + java_default = prev_java_default; + return ret; + } + return set_java_path (string); + } + } + return dbe_sprintf (GTXT ("Java-profiling parameter is neither \"on\", nor \"off\", nor is it a directory: `%s'\n"), string); +} + +char * +Coll_Ctrl::set_java_path (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + free (java_path); + java_path = strdup (string); + return NULL; +} + +char * +Coll_Ctrl::set_java_args (char *string) +{ + char *next; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + char *prev_java_args = java_args; + if (string == NULL || strlen (string) == 0) + java_args = strdup (""); + else + java_args = strdup (string); + // now count the number of Java arguments + for (next = java_args; *next; next++) + { + if (*next == ' ' || *next == '\t') + continue; + njava_args++; + for (++next; *next; next++) + if (*next == ' ' || *next == '\t') + break; + if (!*next) + break; + } + if (njava_args == 0) + java_args = NULL; + char *ret = check_consistency (); + if (ret != NULL) + { + java_args = prev_java_args; + return ret; + } + free (prev_java_args); + return NULL; +} + +char * +Coll_Ctrl::set_follow_mode (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + free (follow_spec_usr); + free (follow_spec_cmp); + follow_spec_usr = NULL; + follow_spec_cmp = NULL; + if (string == NULL || strlen (string) == 0 || strcmp (string, "all") == 0 + || strcmp (string, "on") == 0) + { + follow_mode = FOLLOW_ON; + follow_default = 0; + return NULL; + } + if (strcmp (string, "off") == 0) + { + follow_mode = FOLLOW_NONE; + follow_default = 0; + return NULL; + } + + /* compile regular expression if string starts with "=" */ + if (string[0] == '=' && string[1] != 0) + { + // user has specified a string matching specification + regex_t regex_desc; + int ercode; + const char *userspec = &string[1]; + size_t newstrlen = strlen (userspec) + 3; + char * str = (char *) malloc (newstrlen); + if (str) + { + snprintf (str, newstrlen, "^%s$", userspec); + assert (strlen (str) == newstrlen - 1); + ercode = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + } + else + ercode = 1; + if (!ercode) + { + follow_spec_usr = strdup (string); + /* Ideally, follow_spec_cmp = [serialized regex_desc], */ + /* so that libcollector wouldn't have to recompile it. */ + /* For now, just copy the regular expression into follow_spec_cmp */ + follow_spec_cmp = str; + follow_mode = FOLLOW_ALL; + follow_default = 0; + return NULL; + } + // syntax error in parsing string +#if 0 + char errbuf[256]; + regerror (ercode, ®ex_desc, errbuf, sizeof (errbuf)); + fprintf (stderr, "Coll_Ctrl::set_follow_mode: regerror()=%s\n", errbuf); +#endif + free (str); + } + return dbe_sprintf (GTXT ("Unrecognized follow-mode parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_prof_idle (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0) + { + prof_idle = 1; + return NULL; + } + if (strcmp (string, "off") == 0) + { + prof_idle = 0; + return NULL; + } + return dbe_sprintf (GTXT ("Unrecognized profiling idle cpus parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_archive_mode (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0) + string = "on"; + if (strcasecmp (string, "on") == 0 || strcasecmp (string, "off") == 0 + || strcasecmp (string, "ldobjects") == 0 + || strcasecmp (string, "usedldobjects") == 0 + || strcasecmp (string, "src") == 0 || strcasecmp (string, "usedsrc") == 0 + || strcasecmp (string, "all") == 0) + { + free (archive_mode); + archive_mode = strdup (string); + return NULL; + } + return dbe_sprintf (GTXT ("Unrecognized archive-mode parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_sample_signal (int value) +{ + const char *buf; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (value == 0) + { + sample_sig = 0; + return NULL; + } + if (value == pauseresume_sig) + return report_signal_conflict (value); + if ((buf = strsignal (value)) != NULL) + sample_sig = value; + else + return dbe_sprintf (GTXT ("Invalid sample signal %d\n"), value); + return NULL; +} + +/* find a signal by name */ +int +Coll_Ctrl::find_sig (const char *string) +{ + int val; + char *signame_alloc = NULL; + const char *signame; + val = -1; + if (strcmp (string, "off") == 0) + return 0; + // see if the name begins with SIG + if (strncmp (string, "SIG", 3) != 0) + { + // no: add it + signame_alloc = (char *) malloc (strlen (string) + 3 + 1); + if (signame_alloc == NULL) + return -1; + strcpy (signame_alloc, "SIG"); + strcpy (&signame_alloc[3], string); + signame = signame_alloc; + } + else + signame = string; + + /* see if the string is a number */ + char *endchar = NULL; + val = (int) strtol (signame, &endchar, 0); + if (*endchar != 0) + val = strtosigno (signame); + free (signame_alloc); + if (val == SIGKILL) + return -1; + return val; +} + +char * +Coll_Ctrl::set_pauseresume_signal (int value, int resume) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (value == 0) + { + pauseresume_sig = 0; + return NULL; + } + if (value == sample_sig) + return report_signal_conflict (value); + if (strsignal (value) != NULL) + { + pauseresume_sig = value; + pauseresume_pause = resume; + } + else + return dbe_sprintf (GTXT ("Invalid pause-resume (delayed initialization) signal %d\n"), value); + return NULL; +} + +char * +Coll_Ctrl::report_signal_conflict (int value) +{ + const char *xbuf = strsignal (value); + if (xbuf != NULL) + return dbe_sprintf (GTXT ("Signal %s (%d) can not be used for both sample and pause-resume (delayed initialization)\n"), + xbuf, value); + return dbe_sprintf (GTXT ("Signal %d can not be used for both sample and pause-resume (delayed initialization)\n"), + value); +} + +char * +Coll_Ctrl::set_debug_mode (int value) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + debug_mode = value; + return NULL; +} + +char * +Coll_Ctrl::create_exp_dir () +{ + int max = 4095; // 0xFFF - can be increased if it seems too low + for (int i = 0; i < max; i++) + { + if (mkdir (store_ptr, + S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) + { + int err = errno; + if (err == EACCES) + return dbe_sprintf (GTXT ("Store directory %s is not writeable: %s\n"), + store_dir, strerror (err)); + if (i + 1 >= max) // no more attempts + return dbe_sprintf (GTXT ("Unable to create directory `%s' -- %s\n%s: %d\n"), + store_ptr, strerror (err), + GTXT ("collect: Internal error: loop count achieved"), + max); + char *ermsg = update_expt_name (false, false, true); + if (ermsg != NULL) + { + char *msg = dbe_sprintf (GTXT ("Unable to create directory `%s' -- %s\n"), + store_ptr, ermsg); + free (ermsg); + return msg; + } + continue; + } + return NULL; // All is OK + } + return dbe_sprintf (GTXT ("Unable to create directory `%s'\n"), store_ptr); +} + +char * +Coll_Ctrl::get_exp_name (const char *stembase) +{ + expno = 1; + return dbe_sprintf ("%s.%d.er", stembase, expno); +} + +char * +Coll_Ctrl::preprocess_names () +{ + char buf[MAXPATHLEN]; + char msgbuf[MAXPATHLEN]; + char *ret = NULL; + + /* convert the experiment name and directory into store name/dir */ + /* free the old strings */ + if (store_dir != NULL) + { + free (store_dir); + store_dir = NULL; + } + if (expt_dir != NULL) + { + free (expt_dir); + expt_dir = NULL; + } + if (base_name != NULL) + { + free (base_name); + base_name = NULL; + } + if (expt_name != NULL) + { + free (expt_name); + expt_name = NULL; + } + expno = 1; + if (uexpt_name != NULL) + expt_name = strdup (uexpt_name); + else + { + // no user name -- pick a default + char *c; + char *stem; + char *stembase; + if (expt_group == NULL) + { + stem = strdup (default_stem); + stembase = stem; + } + else + { + stem = strdup (expt_group); + stem[strlen (stem) - 4] = 0; + stembase = stem; + // now remove any leading directory + for (int i = 0;; i++) + { + if (stem[i] == 0) + break; + if (stem[i] == '/') + stembase = &stem[i + 1]; + } + if (strlen (stembase) == 0) + { + free (stem); + stem = strdup (default_stem); + stembase = stem; + } + } + c = get_exp_name (stembase); + expt_name = c; + free (stem); + } + snprintf (buf, sizeof (buf), NTXT ("%s"), expt_name); + if (buf[0] == '/') + { + // it's a full path name + if (udir_name != NULL) + { + snprintf (msgbuf, sizeof (msgbuf), + GTXT ("Warning: Experiment name is an absolute path; directory name %s ignored.\n"), + udir_name); + ret = strdup (msgbuf); + } + } + + // now extract the directory and basename + int lastslash = 0; + for (int i = 0;; i++) + { + if (buf[i] == 0) + break; + if (buf[i] == '/') + lastslash = i; + } + expt_dir = strdup (buf); + if (lastslash != 0) + base_name = strdup (&buf[lastslash + 1]); + else + base_name = strdup (buf); + expt_dir[lastslash] = 0; + if (expt_dir[0] == '/') + store_dir = strdup (expt_dir); + else if ((udir_name == NULL) || (udir_name[0] == 0)) + { + if (expt_dir[0] == 0) + store_dir = strdup ("."); + else + store_dir = strdup (expt_dir); + } + else + { + /* udir_name is a non-empty string */ + if (expt_dir[0] == 0) + store_dir = strdup (udir_name); + else + { + snprintf (buf, sizeof (buf), "%s/%s", udir_name, expt_dir); + store_dir = strdup (buf); + } + } + free (store_ptr); + if (strcmp (store_dir, ".") == 0) + store_ptr = strdup (base_name); + else + { + snprintf (buf, sizeof (buf), "%s/%s", store_dir, base_name); + store_ptr = strdup (buf); + } + + // determine the file system type + if (strcmp (store_dir, prev_store_dir) != 0) + { + free (prev_store_dir); + prev_store_dir = strdup (store_dir); + const char *fstype = get_fstype (store_dir); + if (interactive && enabled && (fstype != NULL) && (nofswarn == 0)) + { + snprintf (msgbuf, sizeof (msgbuf), + GTXT ("%sExperiment directory is set to a file system of type \"%s\",\n which may distort the measured performance;\n it is preferable to record to a local disk.\n"), + (ret == NULL ? "" : ret), fstype); + free (ret); + ret = strdup (msgbuf); + } + } + return ret; +} + +char * +Coll_Ctrl::update_expt_name (bool chgmsg, bool chkonly, bool newname) +{ + char *ret = NULL; + struct stat statbuf; + // make sure the name ends in .er + // set count to the length of the name + int count = (int) strlen (base_name); + + // this should have been checked already, so we can abort + if (count < 4 || strcmp (&base_name[count - 3], ".er") != 0) + abort (); + int pcount = count - 4; + if (!newname) + { // check if old name can be used + char fullname[MAXPATHLEN]; + snprintf (fullname, sizeof (fullname), "%s/%s", store_dir, base_name); + if (stat (fullname, &statbuf) != 0) + if (errno == ENOENT) // name does not exist, we can use it + return NULL; + } + else if (chkonly) + return NULL; + + // current name will not work, update the name + DIR *dir; + struct dirent *dir_entry; + + // see if there's a numeric field in front of the .er of the name + int digits = 0; + while (isdigit ((int) (base_name[pcount])) != 0) + { + pcount--; + if (pcount == 0) // name is of the form 12345.er; don't update it + return dbe_sprintf (GTXT ("name %s is in use and cannot be updated\n"), + base_name); + digits++; + } + if (digits == 0) // name is of form xyz.er (or xyz..er); don't update it + return dbe_sprintf (GTXT ("name %s is in use and cannot be updated\n"), + base_name); + if (base_name[pcount] != '.') // name is of form xyz123.er; don't update it + return dbe_sprintf (GTXT ("name %s is in use and cannot be updated\n"), + base_name); + if (chkonly) + return NULL; + + // save the name for a changed message + char *oldbase = strdup (base_name); + + // the name is of the from prefix.nnn.er; extract the value of nnn + int version = atoi (&base_name[pcount + 1]); + if (newname) // do not try to use old name + version++; + int max_version = version - 1; + + // terminate the base_name string after that . yielding "prefix." + base_name[pcount + 1] = 0; + if ((dir = opendir (store_dir)) == NULL) + { + // ignore error -- we'll hit it again later + free (oldbase); + return NULL; + } + + // find the maximum version in the directory + // count is the number of characters before the number + // + while ((dir_entry = readdir (dir)) != NULL) + { + count = (int) strlen (dir_entry->d_name); + if ((count < 4) || (strcmp (&dir_entry->d_name[count - 3], ".er") != 0)) + continue; + // check that the name is of the form prefix.nnn.er; if not, skip it + if (strncmp (base_name, dir_entry->d_name, pcount + 1) == 0) + { + // the "prefix." part matches, terminate the entry name before the .er + dir_entry->d_name[count - 3] = 0; + char *lastchar; + int dversion = (int) strtol (&dir_entry->d_name[pcount + 1], &lastchar, 10); + + // if it did not end where the .er was, skip it + if (*lastchar != 0) + continue; + if (dversion > max_version) + max_version = dversion; + } + } + + // we now have the maximum version determined + char newbase[MAXPATHLEN]; + base_name[pcount + 1] = 0; + version = max_version + 1; + snprintf (newbase, sizeof (newbase), "%s%d.er", base_name, version); + if ((strcmp (oldbase, newbase) != 0) && chgmsg) + { + ret = dbe_sprintf (GTXT ("name %s is in use; changed to %s\n"), + oldbase, newbase); + free (oldbase); + } + else + free (oldbase); + free (base_name); + base_name = strdup (newbase); + + // now, reset expt_name to reflect new setting + free (expt_name); + if (expt_dir[0] == 0) + expt_name = strdup (base_name); + else + expt_name = dbe_sprintf ("%s/%s", expt_dir, base_name); + free (store_ptr); + if (strcmp (store_dir, ".") == 0) + store_ptr = strdup (base_name); + else + store_ptr = dbe_sprintf ("%s/%s", store_dir, base_name); + closedir (dir); + return ret; +} + +void +Coll_Ctrl::remove_exp_dir () +{ + if (store_ptr == NULL) + return; + rmdir (store_ptr); + free (store_ptr); + store_ptr = NULL; + return; +} + +void +Coll_Ctrl::determine_profile_params () +{ + struct itimerval itimer; + struct itimerval otimer; + int period; + long nperiod; + struct sigaction act; + struct sigaction old_handler; + memset (&act, 0, sizeof (struct sigaction)); + period = 997; + + // set SIGPROF handler to SIG_IGN + sigemptyset (&act.sa_mask); + act.sa_handler = SIG_IGN; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGPROF, &act, &old_handler) == -1) + { + /* couldn't set signal */ + fprintf (stderr, GTXT ("Can't set SIGPROF: %s\n"), strerror (errno)); + exit (1); + } + + // set the timer to arbitrary resolution + itimer.it_interval.tv_sec = period / MICROSEC; + itimer.it_interval.tv_usec = period % MICROSEC; + itimer.it_value = itimer.it_interval; + setitimer (ITIMER_REALPROF, &itimer, &otimer); + + // now reset the timer to turn it off + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = 0; + if (setitimer (ITIMER_REALPROF, &itimer, &otimer) == -1) // call failed + nperiod = -1; + else + nperiod = otimer.it_interval.tv_sec * MICROSEC + otimer.it_interval.tv_usec; + + // check the returned value: is the what we asked for? + if (period == nperiod) // arbitrary precision is OK + set_clk_params (PROFINT_MIN, 1, PROFINT_MAX, PROFINT_HIGH, PROFINT_NORM, PROFINT_LOW); + else if (nperiod < 10000) // hi resolution allowed, but not arbitrary precision + set_clk_params ((int) nperiod, 1000, PROFINT_MAX, 1000, 10000, 100000); + else // low resolution only allowed + set_clk_params (10000, 10000, PROFINT_MAX, 1000, 10000, 100000); + + // If old handler was default, ignore it; otherwise restore it + if (old_handler.sa_handler != SIG_DFL) + { + act.sa_handler = old_handler.sa_handler; + if (sigaction (SIGPROF, &act, &old_handler) == -1) + { + /* couldn't reset signal */ + fprintf (stderr, GTXT ("Can't reset SIGPROF: %s\n"), strerror (errno)); + exit (1); + } + } +} + +const char * +get_fstype (char *) +{ + /* On Linux, statvfs() doesn't return any information that seems to indicate + the filetype. The structure statvfs does not have any field/flag that + gives this information. Comparing the fields from + /usr/include/bits/statvfs.h: + unsigned long int f_fsid; + int __f_unused; + ^^^^ On Solaris, this is where f_basetype is + unsigned long int f_flag; + unsigned long int f_namemax; + XXX Need to revisit this XXX + */ + return NULL; // no NFS warning on Linux for now +} + +//========== Special functions to communicate with the Collector GUI ==========// + +/* Interface strings GUI <-> CLI */ +const char *ipc_str_exp_limit = "exp_limit"; +const char *ipc_str_time_limit = "time_limit"; +const char *ipc_str_arch_exp = "arch_exp"; +const char *ipc_str_descendant = "descendant"; +const char *ipc_str_clkprof = "clkprof"; +const char *ipc_str_hwcprof = "hwcprof"; +const char *ipc_str_hwc2_prof = "hwc2_prof"; +const char *ipc_str_javaprof = "javaprof"; +const char *ipc_str_sample = "sample"; +const char *ipc_str_sample_sig = "sample_sig"; +const char *ipc_str_pause_resume_sig = "pause_resume_sig"; +const char *ipc_str_synctrace = "synctrace"; +const char *ipc_str_heaptrace = "heaptrace"; +const char *ipc_str_iotrace = "iotrace"; +const char *ipc_str_count = "count"; +const char *ipc_str_prof_idle = "prof_idle"; // -x option +// Standard answers +const char *ipc_str_empty = ""; +const char *ipc_str_on = "on"; +const char *ipc_str_off = "off"; +const char *ipc_str_src = "src"; +const char *ipc_str_usedsrc = "usedsrc"; +const char *ipc_str_usedldobjects = "usedldobjects"; +const char *ipc_str_unlimited = "unlimited"; +const char *ipc_str_unknown_control = "Unknown control"; +const char *ipc_str_internal_error = "Internal error"; + +/** + * Finds signal name + * @param signal + * @return NULL or signal name (pointer to allocated memory) + */ +char * +Coll_Ctrl::find_signal_name (int signal) +{ + char *str_signal = NULL; + const char *buf = strsignal (signal); + if (buf != NULL) + str_signal = strdup (buf); + return str_signal; +} + +/** + * Gets control's value + * @param control + * @return value + */ +char * +Coll_Ctrl::get (char * control) +{ + int len = strlen (control); + if (!strncmp (control, ipc_str_exp_limit, len)) + { + if ((size_limit > 0)) + return dbe_sprintf ("%d", size_limit); + return strdup (ipc_str_unlimited); + } + if (!strncmp (control, ipc_str_time_limit, len)) + { + if ((time_run != 0) || (start_delay != 0)) + { + if (start_delay != 0) + { + if (time_run != 0) + return dbe_sprintf ("%ds-%ds", start_delay, start_delay + time_run); + return dbe_sprintf ("%ds-0s", start_delay); + } + return dbe_sprintf ("0s-%ds", time_run); + } + return strdup (ipc_str_unlimited); + } + if (strncmp (control, ipc_str_arch_exp, len) == 0) + return strdup (get_archive_mode ()); + if (!strncmp (control, ipc_str_descendant, len)) + { + switch (get_follow_mode ()) + { + case FOLLOW_ON: + return strdup (ipc_str_on); + case FOLLOW_ALL: + return strdup (ipc_str_on); + case FOLLOW_NONE: + default: + return strdup (ipc_str_off); + } + } + if (!strncmp (control, ipc_str_prof_idle, len)) + { + if (prof_idle == 0) + return strdup (ipc_str_off); + return strdup (ipc_str_on); + } + if (!strncmp (control, ipc_str_clkprof, len)) + { + if (clkprof_default == 1 && clkprof_enabled == 1) // Default value + return strdup (ipc_str_empty); + if (clkprof_enabled == 0) + return strdup (ipc_str_off); + if ((clkprof_timer > 0)) + return dbe_sprintf ("%d", clkprof_timer / 1000); + return strdup (ipc_str_internal_error); + } + if (!strncmp (control, ipc_str_hwcprof, len)) + { + if (hwcprof_enabled_cnt == 0) + return strdup (ipc_str_off); + if (hwc_string != NULL) + return dbe_sprintf ("on\n%s", hwc_string); + return strdup (ipc_str_on); // XXX need more details? + } + if (!strncmp (control, ipc_str_javaprof, len)) + { + if ((java_mode == 0)) + return strdup (ipc_str_off); + return strdup (ipc_str_on); + } + if (!strncmp (control, ipc_str_sample, len)) + { + if (sample_default == 1 && sample_period == 1) // Default value + return strdup (ipc_str_empty); + if (sample_period == 0) + return strdup (ipc_str_off); + if (sample_period > 0) + return dbe_sprintf ("%d", sample_period); + return strdup (ipc_str_internal_error); + } + if (!strncmp (control, ipc_str_sample_sig, len)) + { + if ((sample_sig == 0)) + return strdup (ipc_str_off); + char *str_signal = find_signal_name (sample_sig); + if (str_signal != NULL) + return str_signal; + return dbe_sprintf (GTXT ("Invalid sample signal %d\n"), sample_sig); + } + if (!strncmp (control, ipc_str_pause_resume_sig, len)) + { + if (pauseresume_sig == 0) + return strdup (ipc_str_off); + char *str_signal = find_signal_name (pauseresume_sig); + if (str_signal != NULL) + return str_signal; + return dbe_sprintf (GTXT ("Invalid pause/resume signal %d\n"), pauseresume_sig); + } + if (!strncmp (control, ipc_str_synctrace, len)) + { + if (synctrace_enabled == 0) + return strdup (ipc_str_off); + if (synctrace_thresh < 0) + return strdup ("on\nthreshold: calibrate"); + if (synctrace_thresh == 0) + return strdup ("on\nthreshold: all"); + return dbe_sprintf ("on\nthreshold: %d", synctrace_thresh); + } + if (!strncmp (control, ipc_str_heaptrace, len)) + { + if ((heaptrace_enabled == 0)) + return strdup (ipc_str_off); + return strdup (ipc_str_on); + } + if (!strncmp (control, ipc_str_iotrace, len)) + { + if ((iotrace_enabled == 0)) + return strdup (ipc_str_off); + return strdup (ipc_str_on); + } + if (!strncmp (control, ipc_str_count, len)) + { + if ((count_enabled == 0)) + return strdup (ipc_str_off); + if ((count_enabled < 0)) + return strdup ("on\nstatic"); + return strdup (ipc_str_on); + } + return strdup (ipc_str_unknown_control); +} + +/** + * Resets control's value (restores the default value) + * @param control + * @param value + * @return error or warning or NULL (done) + */ +char * +Coll_Ctrl::set (char * control, const char * value) +{ + char * ret; + char * warn = NULL; + int len = strlen (control); + if (!strncmp (control, ipc_str_exp_limit, len)) + return set_size_limit (value); + if (!strncmp (control, ipc_str_time_limit, len)) + return set_time_run (value); + if (!strncmp (control, ipc_str_arch_exp, len)) + return set_archive_mode (value); + if (!strncmp (control, ipc_str_descendant, len)) + return set_follow_mode (value); + if (!strncmp (control, ipc_str_prof_idle, len)) + return set_prof_idle (value); + if (!strncmp (control, ipc_str_clkprof, len)) + { + ret = set_clkprof (value, &warn); + if (ret == NULL) + { + if (warn != NULL) + return warn; // Warning + return NULL; // Done + } + return ret; // Error + } + if (!strncmp (control, ipc_str_hwcprof, len)) + { + ret = set_hwcstring (value, &warn); + if (ret == NULL) + { + if (warn != NULL) + return warn; // Warning + return NULL; // Done + } + return ret; // Error + } + if (!strncmp (control, ipc_str_hwc2_prof, len)) + { + ret = set_hwcstring (value, &warn); + if (ret == NULL) + { + if (warn != NULL) + return warn; // Warning + return NULL; // Done + } + return ret; // Error + } + if (!strncmp (control, ipc_str_javaprof, len)) + return set_java_mode (value); + if (!strncmp (control, ipc_str_sample, len)) + return set_sample_period (value); + if (!strncmp (control, ipc_str_sample_sig, len)) + return set_sample_signal (find_sig (value)); + if (!strncmp (control, ipc_str_pause_resume_sig, len)) + { + char *str_signal = strdup (value); + char *str_state = strchr (str_signal, (int) '\n'); + if (str_state != NULL) + { + *str_state = 0; + str_state++; + } + int signal = atoi (str_signal); + int state = 0; + if (str_state != NULL) + state = atoi (str_state); + free (str_signal); + return set_pauseresume_signal (signal, state); + } + if (!strncmp (control, ipc_str_synctrace, len)) + return set_synctrace (value); + if (!strncmp (control, ipc_str_heaptrace, len)) + return set_heaptrace (value); + if (!strncmp (control, ipc_str_iotrace, len)) + return set_iotrace (value); + if (!strncmp (control, ipc_str_count, len)) + return set_count (value); + return strdup (ipc_str_unknown_control); +} + +/** + * Resets control's value (restores the default value) + * @param control + * @return error or NULL (done) + */ +char * +Coll_Ctrl::unset (char * control) +{ + int len = strlen (control); + if (!strncmp (control, ipc_str_exp_limit, len)) + size_limit = 0; + if (!strncmp (control, ipc_str_time_limit, len)) + { + time_run = 0; + start_delay = 0; + } + if (!strncmp (control, ipc_str_arch_exp, len)) + { + archive_mode = strdup ("on"); + return NULL; + } + if (!strncmp (control, ipc_str_descendant, len)) + { + follow_mode = FOLLOW_NONE; + return NULL; + } + if (!strncmp (control, ipc_str_prof_idle, len)) + { + prof_idle = 1; + return NULL; + } + if (!strncmp (control, ipc_str_clkprof, len)) + { + clkprof_default = 1; + clkprof_enabled = 1; + return NULL; + } + if (!strncmp (control, ipc_str_hwcprof, len)) + { + setup_hwc (); + set_hwcdefault (); + return NULL; + } + if (!strncmp (control, ipc_str_javaprof, len)) + { + java_mode = 0; + java_default = 0; + free (java_path); + java_path = NULL; + free (java_args); + java_args = NULL; + } + if (!strncmp (control, ipc_str_sample, len)) + { + sample_period = 1; + sample_default = 1; + return NULL; + } + if (!strncmp (control, ipc_str_sample_sig, len)) + { + sample_sig = 0; + return NULL; + } + if (!strncmp (control, ipc_str_pause_resume_sig, len)) + { + pauseresume_sig = 0; + return NULL; + } + if (!strncmp (control, ipc_str_synctrace, len)) + { + synctrace_enabled = 0; + synctrace_thresh = -1; + return NULL; + } + if (!strncmp (control, ipc_str_heaptrace, len)) + { + heaptrace_enabled = 0; + return NULL; + } + if (!strncmp (control, ipc_str_iotrace, len)) + { + iotrace_enabled = 0; + return NULL; + } + if (!strncmp (control, ipc_str_count, len)) + { + count_enabled = 0; + Iflag = 0; + Nflag = 0; + return NULL; + } + return strdup (ipc_str_unknown_control); +} + +void +Coll_Ctrl::set_project_home (char *s) +{ + if (s) + project_home = strdup (s); +} diff --git a/gprofng/src/collctrl.h b/gprofng/src/collctrl.h new file mode 100644 index 0000000..6555df7 --- /dev/null +++ b/gprofng/src/collctrl.h @@ -0,0 +1,405 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* This file describes the data structures used to control + * data collection; it is used by various commands in the MPMT + * tree, and is also shared with dbx. Care should be taken + * to ensure that both the mpmt and dbx builds continue. + + * To remove any APIs or change any enum cases: + * + * 1. Make the changes in mpmt, and preserve the old APIs + * as scaffolding and any old enum values as #defines. + * + * 2. Add the new APIs and new cases to dbx, remove the + * old ones. + * + * 3. Remove the old API scaffolding and enum values here + * + */ + +#ifndef _COLLCTRL_H +#define _COLLCTRL_H + +#include "hwcentry.h" +#include "cc_libcollector.h" + +/*---------------------------------------------------------------------------*/ + +/* class */ + +typedef struct { + int min; + int res; + int max; + int hival; + int normval; + int lowval; +} clk_params_t; + +#define PROFINT_HIGH 997 +#define PROFINT_NORM 10007 +#define PROFINT_LOW 100003 + +#define PROFINT_MIN 500 +#define PROFINT_MAX 1000000 + +class Coll_Ctrl { +public: + + /* _interactive is 1 for dbx, 0 for collect */ + Coll_Ctrl(int _interactive = 0, bool _defHWC = false, bool _kernelHWC = false); + ~Coll_Ctrl(); + + Coll_Ctrl(Coll_Ctrl *cc); /* constructor for duplicate */ + char *check_expt(char **); /* check the experiment directory */ + char *setup_experiment(); /* set up the experiment directory, etc. */ + void close_expt(); + void interrupt(); /* the user interrupts experiment */ + void delete_expt(); + + /* enable/disable the experiment */ + char *enable_expt(); + void disable_expt() { enabled = 0; }; + int isenabled() { return enabled; }; + + /* check for active experiment */ + int isopened() { return opened; }; + + /* set the parameters for clock-profiling */ + void set_clk_params(int min, int res, int max, int hi, int norm, int lo); + char *set_clkprof(const char *valptr, char **warn); + char *reset_clkprof(int val); /* called if profiler must reset value */ + int get_sys_period() { return clk_params.min; }; + int get_clk_min() { return clk_params.min; }; + int get_clk_max() { return clk_params.max; }; + int get_clk_res() { return clk_params.res; }; + int get_clkprof_mode() { return clkprof_enabled; }; + int get_clkprof_timer() { return clkprof_timer; }; + + /* set the parameters for synchronization tracing */ + char *set_synctrace(const char *valptr); + int get_synctrace_mode() { return synctrace_enabled; }; + int get_synctrace_thresh() { return synctrace_thresh; }; + int get_synctrace_scope() { return synctrace_scope; }; + + /* set the parameters for heap tracing */ + char *set_heaptrace(const char *); + int get_heaptrace_mode() { return heaptrace_enabled; }; + int get_heaptrace_checkmode() { return heaptrace_checkenabled; }; + + /* set the parameters for I/O tracing */ + char *set_iotrace(const char *); + int get_iotrace_mode() { return iotrace_enabled; }; + + /* set the parameters for HW counting */ + void setup_hwc(); + char *set_hwcstring(const char *str, char **warn); + char *add_hwcstring(const char *str, char **warn); + char *add_default_hwcstring(const char *str, char **warn, bool add, bool forKernel = false); + void set_hwcdefault(); + void disable_hwc(); + int get_hwc_cnt() { return hwcprof_enabled_cnt; }; + int get_hwc_mode() { return hwcprof_enabled_cnt ? 1 : 0; }; + char *get_hwc_string() { return hwc_string; }; + + Hwcentry * + get_hwc_entry (int n) + { + if (n < 0 || n >= hwcprof_enabled_cnt) + return 0; + return &hwctr[n]; + }; + + void hwcentry_dup (Hwcentry *, Hwcentry *); + char *get_hwc_counter (int n) { return get_hwc_entry (n)->name; }; + + /* set the parameters for count data */ + char *set_count (const char *); + int get_count () { return count_enabled; }; + void set_Iflag () { Iflag = 1; }; + void set_Nflag () { Nflag = 1; }; + + /* set time interval for attach with dbx timed collection */ + /* also used for er_kernel */ + char *set_time_run (const char *); + int get_time_run (void) { return time_run; }; + int get_start_delay (void) { return start_delay; }; + + /* set pid for attach with dbx to collect data */ + char *set_attach_pid (char *); + int get_attach_pid (void) { return attach_pid; }; + + /* set java mode, "on" = yes; "off" = no; anthing else implies + * yes, and is the path to the java to use + * java_mode is returned as zero for off, one for on + * java_default is returned as zero for explicitly set, one for defaulted on + */ + char *set_java_mode (const char *); + int get_java_mode () { return java_mode; }; + int get_java_default () { return java_default; }; + + /* setting Java path explicitly */ + char *set_java_path (const char *); + char *get_java_path () { return java_path; }; + + /* set additional arguments for Java invocation */ + char *set_java_args (char *); + char *get_java_args () { return java_args; }; + int get_java_arg_cnt () { return njava_args; }; + + /* set load-object archive mode, 0 = no; other = yes */ + char *set_archive_mode (const char *); + char *get_archive_mode () { return archive_mode; }; + + /* set follow-descendants mode, 0 = no; other = yes */ + char *set_follow_mode (const char *); + Follow_type get_follow_mode () { return follow_mode; }; + int get_follow_default () { return follow_default; }; + char *get_follow_usr_spec () { return follow_spec_usr; }; + char *get_follow_cmp_spec () { return follow_spec_cmp; }; + + /* set profile idle cpus mode, 1 = no; 0 = yes */ + char *set_prof_idle (const char *); + int get_prof_idle () { return prof_idle; }; + + /* set debug more, 1 = yes; other = no */ + /* if set, target will be set to halt at exit from exec */ + char *set_debug_mode (int); + int get_debug_mode () { return debug_mode; }; + + /* find a signal from a string */ + int find_sig (const char *); + /* find a signal name from a signal value */ + char *find_signal_name (int signal); + + /* set the pauseresume (delayed initialization) signal */ + char *set_pauseresume_signal (int, int); + int get_pauseresume_signal () { return pauseresume_sig; }; + int get_pauseresume_pause () { return pauseresume_pause; }; + + /* set the sample signal */ + char *set_sample_signal (int); + int get_sample_signal () { return sample_sig; }; + + /* set the periodic sampling */ + char *set_sample_period (const char *); + int get_sample_period (void) { return sample_period; }; + + /* set experiment size limit */ + char *set_size_limit (const char *); + int get_size_limit (void) { return size_limit; }; + + /* naming methods */ + /* set the target executable name */ + int set_target (char *); + char *get_target () { return target_name; }; + + /* set the experiment name */ + void set_default_stem (const char *); + char *set_expt (const char *, char **, bool); + char *get_expt () { return expt_name; }; + + /* set the experiment directory */ + char *set_directory (char *, char **); + + char *get_directory () { return udir_name ? udir_name : store_dir; }; + + /* return the real experiment ptr file name */ + char *get_experiment () { return store_ptr; }; + char *update_expt_name (bool verbose = true, bool ckonly = false, bool newname = false); + + /* remove the experiment */ + void remove_exp_dir (); + + /* return the data descriptor */ + char * + get_data_desc () + { + return data_desc; + }; + + /* set the experiment group */ + char *set_group (char *); + char *get_group () { return expt_group; }; + + /* return the experiment settings as a string */ + char *show (int); /* full show */ + char *show_expt (); /* short form */ + + /* return an argv array to compose a "collect" command from settings */ + char **get_collect_args (); + + /* determine characteristics of system */ + char *get_node_name () { return node_name; }; + long get_ncpus () { return ncpus; }; + int get_cpu_clk_freq () { return cpu_clk_freq; }; + int get_cpc_cpuver () { return cpc_cpuver; }; + + /* disable warning about non-local filesystems */ + void set_nofswarn () { nofswarn = 1; }; + + //========== Special functions to communicate with the Collector GUI ==========// + char *get (char *); /* get control's value */ + char *set (char *, const char *); /* set control's value */ + char *unset (char *); /* reset control's value to its default */ + void set_project_home (char *); + +private: + int interactive; /* 1 - dbx, 0 - collect */ + bool defHWC; /* true if default HWC experiment should be run */ + bool kernelHWC; /* T if default HWC counters are for kernel profiling */ + int opened; /* T if an experiment is opened */ + int enabled; /* T if an experiment is enabled */ + volatile int uinterrupt; /* set if interrupt from user */ + + /* experiment/machine characteristics */ + char *node_name; /* name of machine on which experiment is run */ + long ncpus; /* number of online CPUs */ + int cpu_clk_freq; /* chip clock (MHz.), as reported from processor_info */ + int cpc_cpuver; /* chip version, as reported from libcpc */ + long sys_resolution; /* system clock resolution */ + int sys_period; /* profiling clock resolution on the system */ + int sample_period; /* period for sampling, seconds */ + int sample_default; /* if period for sampling set by default */ + int size_limit; /* experiment size limit, MB */ + long npages; /* number of pages configured */ + long page_size; /* size of system page */ + clk_params_t clk_params; + + /* user specification of name */ + /* user may specify both uexpt_name and udir_name + * if uexpt_name is absolute path, udir_name is ignored, with warning + * otherwise, udir_name is prepended to uexpt_name + * + * if uexpt_name is of the form: <dir>/zzzz.nnn.er, where nnn is numeric, + * nnn will be reset to one greater than the the highest experiment + * with a name of the same form. + */ + char *default_stem; /* default stem for experiment name */ + char *uexpt_name; /* suggested experiment name */ + char *expt_name; /* experiment name, after defaulting */ + char *expt_dir; /* directory part of suggested experiment name */ + char *base_name; /* basename of suggested experiment name */ + char *udir_name; /* user name of directory for data */ + + char *store_dir; /* directory to contain experiment dir. */ + char *prev_store_dir; /* previously set store directory */ + char *store_ptr; /* experiment pointer file */ + char *expt_group; /* name of experiment group, if any */ + char *project_home; /* argv[0] */ + + char *target_name; /* target executable name */ + char *data_desc; /* string describing the data to be collected */ + char *lockname; /* name of directory lock file */ + int lockfd; /* fd of open lock file */ + + int nofswarn; /* if 1, don't warn of filesystem */ + int expno; /* number in <stem>.<expno>.er */ + + /* T if an target is to be left for debugger attach */ + int debug_mode; + + /* clock-profiling controls */ + /* T if clock-based profiling */ + int clkprof_enabled; + + /* T if on by default, rather than explicit setting */ + int clkprof_default; + + /* value for timer, microseconds. */ + int clkprof_timer; // adjusted clock profiling interval + int clkprof_timer_target; // desired clock profiling interval + + /* HW counter-profiling controls */ + /* >0 if HW counter-based profiling */ + int hwcprof_default; + int hwcprof_enabled_cnt; + char *hwc_string; + Hwcentry hwctr[MAX_PICS]; + + int synctrace_enabled; /* T if synchronization tracing */ + /* sync trace threshold value, microsec. */ + /* if 0, record all */ + /* if <0, calibrate */ + int synctrace_thresh; + + /* sync trace scope -- a bit mask */ + /* definitions in data_pckts.h */ + int synctrace_scope; + + int heaptrace_enabled; /* T if heap tracing */ + /* if 0 no checking; + * if 1, check for over- and under-write + * if 2, also set patterns in malloc'd and free'd areas + */ + int heaptrace_checkenabled; + int iotrace_enabled; /* T if I/O tracing */ + + /* count controls */ + /* 1 if counting is enabled; -1 if count static is enabled */ + int count_enabled; + int Iflag; /* specify bit output directory -- only with count_enabled */ + int Nflag; /* specify bit library to ignore -- only with count_enabled */ + + /* pid, if -P <pid> is invoked for attach with dbx */ + int attach_pid; + + /* time_run -- non-zero if timed execution request (dbx/er_kernel) */ + int time_run; + int start_delay; + + /* T if Java profiling is requested */ + int java_mode; + int java_default; + char *java_path; + char *java_args; + int njava_args; + + /* whether/how following-descendants is requested */ + Follow_type follow_mode; + int follow_default; + char *follow_spec_usr; // user's selective follow spec + char *follow_spec_cmp; // compiled selective follow spec + int prof_idle; // whether profiling idle cpus is requested + char *archive_mode; // how load-objects archiving is requested + + // signals to control pause-resume (delayed initialization) and samples + int pauseresume_sig; // for pause-resume signal -- delayed initialization + int pauseresume_pause; // 1 if pauseresume and start paused; 0 if not + int sample_sig; // to trigger sample + char *report_signal_conflict (int); + char *check_consistency (); // check the experiment settings for consistency + void determine_profile_params (); + char *preprocess_names (); + char *get_exp_name (const char *); + char *create_exp_dir (); + void build_data_desc (); + char *check_group (); + char *join_group (); + void free_hwc_fields (Hwcentry *tmpctr); + + // propagates clkprof_timer change to Hwcentry hwctr[] + void set_clkprof_timer_target (int microseconds); + void adjust_clkprof_timer (int microseconds); + hrtime_t clkprof_timer_2_hwcentry_min_time (int clkprof_microseconds); +}; + +#endif /* !_COLLCTRL_H */ diff --git a/gprofng/src/collect.h b/gprofng/src/collect.h new file mode 100644 index 0000000..c500dee --- /dev/null +++ b/gprofng/src/collect.h @@ -0,0 +1,156 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _COLLECT_H +#define _COLLECT_H + +#include <Application.h> + +extern "C" +{ + typedef void (*SignalHandler)(int); +} + +class Coll_Ctrl; +class Elf; + +#define MAXLABELS 10 /* maximum number of -C arguments */ +#define STDEBUFSIZE 24000 + +enum { MAX_LD_PRELOAD_TYPES = 3 }; + +struct Process +{ + Process (pid_t _pid) : pid (_pid) { } + pid_t pid; +}; + +struct DtraceTool +{ + DtraceTool (char*); + ~DtraceTool (); + + char *name; // Tool name as specified by -D flag + char *params; // Tool parameters + char *dfile; // Extracted d-script + char *mfile; // Extracted metadata file + char *ofile; // Output file + pid_t pid; +}; + +// collect object +class collect : Application +{ +public: + collect (int argc, char *argv[], char **envp); + virtual ~collect (); + void start (int argc, char *argv[]); + void writeStr (int f, const char *buf); + + // the collector control class + Coll_Ctrl *cc; + +private: + enum Exec_status + { + EXEC_OK = 0, // found as runnable executable + EXEC_ELF_NOSHARE, // found, but built unshared + EXEC_IS_JAR, // found as a .jar file + EXEC_IS_CLASS, // found as a .class file but name missing .class + EXEC_IS_CLASSCLASS, // found as a .class file with explicit .class + EXEC_OPEN_FAIL, // could not be opened + EXEC_ELF_LIB, // internal error: bad elf library + EXEC_ELF_HEADER, // executable, with bad ELF header + EXEC_ELF_ARCH, // executable, but unrunnable architecture + EXEC_ISDIR, // a directory, not a file + EXEC_NOT_EXEC, // a file, but not executable + EXEC_NOT_FOUND // a directory, not a file + }; + + // override methods in base class + void usage (); + void short_usage (); + void show_hwc_usage (); + int check_args (int argc, char *argv[]); + void check_target (int, char **); + Exec_status check_executable (char *); + Exec_status check_executable_arch (Elf *); + char *status_str (Exec_status, char *); + int do_flag (const char *); + char *find_java (void); + char *java_path; + char *java_how; + int putenv_libcollector (); + int putenv_libcollector_ld_audits (); + int putenv_libcollector_ld_preloads (); + int putenv_libcollector_ld_misc (); + void add_ld_preload (const char *lib); + int putenv_ld_preloads (); + int putenv_memso (); + int env_strip (char *env, const char *str); + int putenv_purged_ld_preloads (const char *var); + int putenv_append (const char *var, const char *val); + void get_count_data (); + void prepare_dbx (); + int traceme (const char *file, char *const argv[]); + int checkflagterm (const char *); + void dupflagseen (char); + void dupflagseen (const char *); + void validate_config (int); + void validate_java (const char *, const char *, int); + int set_output (); + void reset_output (); + + /* Logging warning messages */ + char **collect_warnings; + int collect_warnings_idx; + void warn_open (); + void warn_close (); + void warn_write (const char *format, ...); + void warn_comment (const char *kind, int num, char *s = NULL, int len = 0); + char *warnfilename; + FILE *warn_file; + + /* MPI experiment handling */ + void setup_MPI_expt (); /* the founder experiment */ + void write_MPI_founder_log (); + void close_MPI_founder_log (int, int); + void spawn_MPI_job (); /* run the MPI job */ + void copy_collect_args (char ***); /* copy collect args for an MPI target */ + int disabled; + int jseen_global; /* if -j flag was seen */ + int verbose; + bool mem_so_me; /* if T, preload mem.so, not libcollector */ + int origargc; + char **arglist; + char **origargv; + char **origenvp; + int targ_index; // index of name of target in origargv + bool is_64; + int nargs; + int njargs; + char *jargs; + int nlabels; + char *label[MAXLABELS]; + char *sp_preload_list[MAX_LD_PRELOAD_TYPES + 1]; // +1 for NULL termination + char *sp_libpath_list[MAX_LD_PRELOAD_TYPES + 1]; // +1 for NULL termination +}; + +#endif /* ! _COLLECT_H */ diff --git a/gprofng/src/collector_module.h b/gprofng/src/collector_module.h new file mode 100644 index 0000000..512af7a --- /dev/null +++ b/gprofng/src/collector_module.h @@ -0,0 +1,223 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _COLLECTOR_MODULE_H +#define _COLLECTOR_MODULE_H + +#include <sys/types.h> +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> +#include <dirent.h> + +#include "gp-defs.h" + +struct stat; +struct tm; + +#define COLLECTOR_MODULE_ERR ((CollectorModule)-1) + +/* ------- libc interface ----------------- */ +/* the fields in this structure are in alphabetical order. + * If you add any, please put it in the right place */ +typedef struct CollectorUtilFuncs +{ + int (*access)(); + int (*atoi)(const char *nptr); + void *(*calloc)(size_t nelem, size_t elsize); + int (*clearenv)(void); + int (*close)(int); + int (*closedir)(); + int (*execv)(const char *path, char *const argv[]); + void (*exit)(int status); + int (*fclose)(FILE *stream); + int (*fcntl)(int fd, int cmd, ...); + char *(*fgets)(char *s, int n, FILE *stream); + FILE *(*fopen)(const char *filename, const char *mode); + pid_t (*vfork)(); + int (*fprintf)(FILE *stream, const char *format, ...); + void (*free)(void *ptr); + int (*fstat)(int fd, struct stat *buf); + int (*getcpuid)(); + char *(*getcwd)(char *buf, size_t size); + char *(*getenv)(const char *name); + struct tm *(*gmtime_r)(const time_t *clock, struct tm *res); + int (*ioctl)(int d, int request, ...); + off_t (*lseek)(int fd, off_t offset, int whence); + void *(*malloc)(size_t size); + void *(*memset)(void *s1, int c, size_t n); + int (*mkdir)(); + time_t (*mktime)(struct tm *timeptr); + void *(*mmap)(void *, size_t, int, int, int, off_t); + void *(*mmap64)(); + int (*munmap)(); + int (*open)(const char *, int, ...); + int (*open_bare)(const char *, int, ...); + DIR *(*opendir)(); + int (*pclose)(FILE *stream); + FILE *(*popen)(const char *command, const char *mode); + int (*putenv)(char *string); + ssize_t (*pwrite)(); + ssize_t (*pwrite64)(); + ssize_t (*read)(); + int (*setenv)(const char *name, const char *value, int overwrite); + int (*sigfillset)(sigset_t *set); + int (*sigprocmask)(int how, const sigset_t *set, sigset_t *oldset); + int (*snprintf)(char *str, size_t size, const char *format, ...); + int (*stack_getbounds)(); + char *(*strchr)(const char *name, int c); + int (*strcmp)(const char *s1, const char *s2); + int (*strcpy)(const char *s1, const char *s2); + char *(*libc_strdup)(const char *s1); // Don't use "strdup" because it is a macro in gcc + char *(*strerror)(int errnum); + int (*strerror_r)(int errnum, char *strerrbuf, size_t buflen); + size_t (*strlcat)(char *dest, const char *src, size_t dstsize); + size_t (*strlcpy)(char *dest, const char *src, size_t dstsize); + size_t (*strlen)(const char *string); + int (*strncmp)(const char *s1, const char *s2, size_t n); + size_t (*strncpy)(char *dst, const char *src, size_t dstsize); + size_t (*strspn)(const char *s1, const char *s2); + char *(*strrchr)(const char *name, int c); + char *(*strstr)(const char *s1, const char *s2); + long int (*strtol)(const char *nptr, char **endptr, int base); + long long int (*strtoll)(const char *nptr, char **endptr, int base); + unsigned long int (*strtoul)(const char *nptr, char **endptr, int base); + unsigned long long int (*strtoull)(const char *nptr, char **endptr, int base); + int (*symlink)(const char *s1, const char *s2); + int (*syscall)(int number, ...); + long (*sysconf)(int name); + long (*sysinfo)(int command, char *buf, long count); + time_t (*time)(time_t *tloc); + int (*unsetenv)(const char *name); + int (*vsnprintf)(char *str, size_t size, const char *format, va_list ap); + pid_t (*waitpid)(pid_t pid, int *stat_loc, int options); + ssize_t (*write)(); + double (*atof)(); + void *n_a; +} CollectorUtilFuncs; + +extern CollectorUtilFuncs __collector_util_funcs; +extern int __collector_dlsym_guard; + +#define CALL_UTIL(x) __collector_util_funcs.x + +/* The following constants define the meaning of the "void *arg" + * argument of getFrameInfo(). + */ +/* arg is a pointer to ucontext_t, walk the stack described by it */ +#define FRINFO_FROM_UC 1 +/* walk the current stack starting from the frame containing arg */ +#define FRINFO_FROM_STACK 2 +/* walk the current stack starting from the caller of the frame containing arg */ +#define FRINFO_FROM_STACK_ARG 3 +/* arg is a pc, process a stack containing just that pc */ +#define FRINFO_FROM_PC 4 +/* arg is of type CM_Array describing a stack image */ +#define FRINFO_FROM_ARRAY 5 +#define FRINFO_NO_OMP_INFO 0x80000000 +#define FRINFO_NO_WALK 0x40000000 + +typedef struct CM_Array +{ + unsigned int length; /* in bytes, not including length */ + void *bytes; +} CM_Array; + +// Interface with libcollector.so: +typedef enum +{ + SP_ORIGIN_FORK = -1, + SP_ORIGIN_LIBCOL_INIT = 0, + SP_ORIGIN_DBX_ATTACH = 1, + SP_ORIGIN_GENEXP = 2, + SP_ORIGIN_KERNEL = 3, + SP_ORIGIN_DTRACE = 4, + SP_ORIGIN_COLLECT = 5 +} sp_origin_t; + +struct Heap; +struct Common_packet; +struct CM_Packet; +struct ModuleInterface; + +typedef long long HiResTime; +typedef int CollectorModule; +typedef unsigned long long FrameInfo; +typedef struct CollectorInterface +{ + /* General services */ + CollectorModule (*registerModule)(struct ModuleInterface*); + const char *(*getParams)(); + const char *(*getExpDir)(); + int (*writeLog)(char *format, ...); + FrameInfo (*getFrameInfo)(CollectorModule modl, HiResTime ts, int mode, void *arg); + FrameInfo (*getUID)(CM_Array *arg); + FrameInfo (*getUID2)(CM_Array *arg, FrameInfo uid); + int (*getStackTrace)(void *buf, int size, void *bptr, void *eptr, void *arg); + int (*writeMetaData)(CollectorModule modl, char *format, ...); + + /* writeDataRecord ensures that the header is filled in, and then calls writeDataPacket */ + int (*writeDataRecord)(CollectorModule modl, struct Common_packet *pckt); + int (*writeDataPacket)(CollectorModule modl, struct CM_Packet *pckt); + void (*write_sample)(char *name); + void (*get_progspec)(char *retstr, int tmp_sz, char *namestr, int name_sz); + int (*open_experiment)(const char *exp, const char *params, sp_origin_t origin); + HiResTime (*getHiResTime)(); + + /* Dynamic memory allocation service */ + struct Heap *(*newHeap)(); + void (*deleteHeap)(struct Heap *heap); + void *(*allocCSize)(struct Heap *heap, unsigned sz, int log); + void (*freeCSize)(struct Heap *heap, void *ptr, unsigned sz); + void *(*allocVSize)(struct Heap *heap, unsigned sz); + void *(*reallocVSize)(struct Heap *heap, void *ptr, unsigned newsz); + + /* Thread specific data service */ + unsigned (*createKey)(size_t sz, void (*init)(void*), void (*fini)(void*)); + void *(*getKey)(unsigned key); + + /* Debugging services */ + void (*writeDebugInfo)(int, int, char *, ...) __attribute__ ((format (printf, 3, 4))); +} CollectorInterface; + +typedef struct ModuleInterface +{ + char *description; + int (*initInterface)(CollectorInterface*); + int (*openExperiment)(const char *); + int (*startDataCollection)(); + int (*stopDataCollection)(); + int (*closeExperiment)(); + int (*detachExperiment)(); /* called from fork-child before openExperiment() */ +} ModuleInterface; + +typedef CollectorModule (*RegModuleFunc)(ModuleInterface*); +typedef void (*ModuleInitFunc)(CollectorInterface*); + +#ifdef __cplusplus +extern "C" +{ +#endif + CollectorModule __collector_register_module (ModuleInterface *modint); +#ifdef __cplusplus +} +#endif + +#endif /* _COLLECTOR_MODULE_H */ diff --git a/gprofng/src/comp_com.c b/gprofng/src/comp_com.c new file mode 100644 index 0000000..486bd1a --- /dev/null +++ b/gprofng/src/comp_com.c @@ -0,0 +1,3481 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <locale.h> +#include <values.h> +#include <assert.h> + +#include "comp_com.h" + +/* + * To add a new message _FORMAT_ please perform the following tasks: + * 1) Insert it into the list below, with the matching comment. + * The table is sorted by parameter type. In increasing order + * they are: String, Procedure, Variable, Loop, Region, Integer. + * 2) Insert the corresponding information into the following + * procedures in this file: ccm_num_params(), ccm_paramlist_index(), + * ccm_param_primtype(), and ccm_param_hightype(). + * 3) If you are also creating a new high-type or primitive-type, + * extend the corresponding enum, update this comment and make sure + * to update any code in the analyzer, iropt, cg or ube that depends + * on knowing the limited set of types. + */ + +typedef enum ccm_fmt { + CCMFMT_NONE, /* none */ + CCMFMT_S1, /* s1 */ + CCMFMT_S1S2, /* s1, s2 */ + CCMFMT_S1L2, /* s1, l2 */ + CCMFMT_S1L2VV3, /* s1, l2, v3, v4, ... */ + CCMFMT_S1R2VV3, /* s1, r2, v3, v4, ... */ + CCMFMT_S1X2, /* s1, x2 */ + CCMFMT_P1, /* p1 */ + CCMFMT_P1S2, /* p1, s2 */ + CCMFMT_P1S2P3, /* p1, s2, p3 */ + CCMFMT_P1S2P3I4, /* p1, s2, p3, i4 */ + CCMFMT_P1S2I3, /* p1, s2, i3 */ + CCMFMT_P1P2, /* p1, p2 */ + CCMFMT_P1L2, /* p1, l2 */ + CCMFMT_P1I2, /* p1, i2 */ + CCMFMT_P1I2L3, /* p1, i2, l3 */ + CCMFMT_P1I2LL3, /* p1, i2, l3, l4 ... */ + CCMFMT_P1I2I3, /* p1, i2, i3 */ + CCMFMT_PP1, /* p1, p2, ... */ + CCMFMT_V1, /* v1 */ + CCMFMT_V1V2, /* v1, v2 */ + CCMFMT_V1L2, /* v1, l2 */ + CCMFMT_VV1, /* v1, v2, ... */ + CCMFMT_L1, /* l1 */ + CCMFMT_L1S2, /* l1, s2 */ + CCMFMT_L1S2L3, /* l1, s2, l3 */ + CCMFMT_L1P2, /* l1, p2 */ + CCMFMT_L1P2I3, /* l1, p2, i3 */ + CCMFMT_L1PP2, /* l1, p2, p3, ... */ + CCMFMT_L1VV2, /* l1, v2, v3, ... */ + CCMFMT_L1L2, /* l1, l2 */ + CCMFMT_L1L2L3, /* l1, l2, l3 */ + CCMFMT_LL1, /* l1, l2, ... */ + CCMFMT_L1R2, /* l1, r2 */ + CCMFMT_L1I2, /* l1, i2 */ + CCMFMT_L1I2L3, /* l1, i2, l3 */ + CCMFMT_L1I2LL3, /* l1, i2, l3, l4, ... */ + CCMFMT_L1I2I3L4, /* l1, i2, i3, l4 */ + CCMFMT_L1I2I3I4I5, /* l1, i2, ..., i5 */ + CCMFMT_L1I2I3I4I5I6I7, /* l1, i2, ..., i7 */ + CCMFMT_L1I2I3I4I5I6I7I8I9, /* l1, i2, ..., i9 */ + CCMFMT_L1II2, /* l1, i2, i3, ... */ + CCMFMT_R1, /* r1 */ + CCMFMT_R1VV2, /* r1, v2, v3, ... */ + CCMFMT_I1, /* i1 */ + CCMFMT_I1P2I3, /* i1, p2, i3 */ + CCMFMT_I1V2, /* i1, v2 */ + CCMFMT_I1V2V3, /* i1, v2, v3 */ + CCMFMT_I1L2, /* i1, l2 */ + CCMFMT_I1LL2, /* i1, l2, l3, ... */ + CCMFMT_I1I2I3I4, /* i1, i2, i3, i4 */ + CCMFMT_I1I2I3I4I5I6, /* i1, i2, ..., i6 */ + CCMFMT_I1I2I3I4I5I6I7I8, /* i1, i2, ..., i8 */ + CCMFMT_LAST +} Ccm_Fmttype_t; + +/* + * Low- and high-level types for commentary parameters. + */ + +typedef enum ccm_primtype +{ + CCM_PRIMTYPE_NONE, + CCM_PRIMTYPE_STRING, + CCM_PRIMTYPE_INTEGER, + CCM_PRIMTYPE_HEXSTRING +} Ccm_Primtype_t; + +typedef enum ccm_hightype +{ + CCM_HITYPE_NONE, + CCM_HITYPE_STRING, + CCM_HITYPE_PROCEDURE, + CCM_HITYPE_VARIABLE, + CCM_HITYPE_LOOPTAG, + CCM_HITYPE_REGIONTAG, + CCM_HITYPE_HEXSTRING, + CCM_HITYPE_INTEGER +} Ccm_Hitype_t; + +typedef struct ccm_attrs +{ + char *msg; /* I18N msg string */ + const char *name; /* Print name for this message ID */ + int32_t vis; /* Visibility bits */ + Ccm_Fmttype_t fmt; /* Format type */ +} Ccm_Attr_t; + +static Ccm_Attr_t *ccm_attrs; /* Table of per-msg attributes */ +static nl_catd ccm_catd = (nl_catd) - 1; /* messages id */ + +/* + * map COMPMSG_ID to table indices + */ +static int +ccm_vis_index (COMPMSG_ID m) +{ + int32_t high = m >> 8; + int32_t low = m & 0xFF; + for (int i = 0; i < 24; i++, high >>= 1) + if (high <= 1) + return (i << 8) + low + 1; + return 0; +} + +/* + * Return # parameters for this message; MAXINT for messages with + * parameter lists. + */ +static int +ccm_num_params (COMPMSG_ID m) +{ + int vindex; + int res; + vindex = ccm_vis_index (m); + switch (ccm_attrs[vindex].fmt) + { + case CCMFMT_NONE: + res = 0; + break; + case CCMFMT_S1: + case CCMFMT_P1: + case CCMFMT_V1: + case CCMFMT_L1: + case CCMFMT_R1: + case CCMFMT_I1: + res = 1; + break; + case CCMFMT_S1S2: + case CCMFMT_S1L2: + case CCMFMT_S1X2: + case CCMFMT_P1S2: + case CCMFMT_P1P2: + case CCMFMT_P1L2: + case CCMFMT_P1I2: + case CCMFMT_V1V2: + case CCMFMT_V1L2: + case CCMFMT_L1S2: + case CCMFMT_L1P2: + case CCMFMT_L1L2: + case CCMFMT_L1R2: + case CCMFMT_L1I2: + case CCMFMT_I1V2: + case CCMFMT_I1L2: + res = 2; + break; + case CCMFMT_P1S2P3: + case CCMFMT_P1S2I3: + case CCMFMT_P1I2L3: + case CCMFMT_P1I2I3: + case CCMFMT_L1S2L3: + case CCMFMT_L1P2I3: + case CCMFMT_L1L2L3: + case CCMFMT_L1I2L3: + case CCMFMT_I1P2I3: + case CCMFMT_I1V2V3: + res = 3; + break; + case CCMFMT_P1S2P3I4: + case CCMFMT_L1I2I3L4: + case CCMFMT_I1I2I3I4: + res = 4; + break; + case CCMFMT_L1I2I3I4I5: + res = 5; + break; + case CCMFMT_I1I2I3I4I5I6: + res = 6; + break; + case CCMFMT_L1I2I3I4I5I6I7: + res = 7; + break; + case CCMFMT_I1I2I3I4I5I6I7I8: + res = 8; + break; + case CCMFMT_L1I2I3I4I5I6I7I8I9: + res = 9; + break; + case CCMFMT_S1L2VV3: + case CCMFMT_S1R2VV3: + case CCMFMT_PP1: + case CCMFMT_P1I2LL3: + case CCMFMT_VV1: + case CCMFMT_L1PP2: + case CCMFMT_L1VV2: + case CCMFMT_LL1: + case CCMFMT_L1I2LL3: + case CCMFMT_L1II2: + case CCMFMT_R1VV2: + case CCMFMT_I1LL2: + res = MAXINT; + break; + case CCMFMT_LAST: + default: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) assert (0); + break; + } + return res; +} + +static int +ccm_paramlist_index (COMPMSG_ID m) +{ + int res; + int vindex = ccm_vis_index (m); + switch (ccm_attrs[vindex].fmt) + { + case CCMFMT_NONE: + case CCMFMT_S1: + case CCMFMT_S1S2: + case CCMFMT_S1L2: + case CCMFMT_S1X2: + case CCMFMT_P1: + case CCMFMT_P1S2: + case CCMFMT_P1S2P3: + case CCMFMT_P1S2P3I4: + case CCMFMT_P1S2I3: + case CCMFMT_P1P2: + case CCMFMT_P1L2: + case CCMFMT_P1I2: + case CCMFMT_P1I2L3: + case CCMFMT_P1I2I3: + case CCMFMT_V1: + case CCMFMT_V1V2: + case CCMFMT_V1L2: + case CCMFMT_L1: + case CCMFMT_L1S2: + case CCMFMT_L1S2L3: + case CCMFMT_L1P2: + case CCMFMT_L1P2I3: + case CCMFMT_L1L2: + case CCMFMT_L1L2L3: + case CCMFMT_L1R2: + case CCMFMT_L1I2: + case CCMFMT_L1I2L3: + case CCMFMT_L1I2I3L4: + case CCMFMT_L1I2I3I4I5: + case CCMFMT_L1I2I3I4I5I6I7: + case CCMFMT_L1I2I3I4I5I6I7I8I9: + case CCMFMT_R1: + case CCMFMT_I1: + case CCMFMT_I1P2I3: + case CCMFMT_I1V2: + case CCMFMT_I1V2V3: + case CCMFMT_I1L2: + case CCMFMT_I1I2I3I4: + case CCMFMT_I1I2I3I4I5I6: + case CCMFMT_I1I2I3I4I5I6I7I8: + res = 0; + break; + case CCMFMT_PP1: + case CCMFMT_VV1: + case CCMFMT_LL1: + res = 1; + break; + case CCMFMT_L1PP2: + case CCMFMT_L1VV2: + case CCMFMT_L1II2: + case CCMFMT_R1VV2: + case CCMFMT_I1LL2: + res = 2; + break; + case CCMFMT_S1L2VV3: + case CCMFMT_S1R2VV3: + case CCMFMT_P1I2LL3: + case CCMFMT_L1I2LL3: + res = 3; + break; + case CCMFMT_LAST: + default: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) assert (0); + break; + } + return res; +} + +static Ccm_Primtype_t +ccm_param_primtype (COMPMSG_ID m, int param_idx) +{ + int vindex; + Ccm_Primtype_t res; + if (param_idx <= 0 || param_idx > ccm_num_params (m)) + return CCM_PRIMTYPE_NONE; + + res = CCM_PRIMTYPE_NONE; /* should always be updated */ + vindex = ccm_vis_index (m); + switch (ccm_attrs[vindex].fmt) + { + /* + * Sort cases by: + * 1) # parameters + * 2) Strings before Integers + * 3) Enum tags + */ + case CCMFMT_NONE: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) + assert (0); + break; + case CCMFMT_S1: + case CCMFMT_P1: + case CCMFMT_V1: + case CCMFMT_L1: + case CCMFMT_R1: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_I1: + if (param_idx == 1) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_S1S2: + case CCMFMT_S1L2: + case CCMFMT_P1S2: + case CCMFMT_P1P2: + case CCMFMT_P1L2: + case CCMFMT_V1V2: + case CCMFMT_V1L2: + case CCMFMT_L1S2: + case CCMFMT_L1P2: + case CCMFMT_L1L2: + case CCMFMT_L1R2: + if (param_idx == 1 || param_idx == 2) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_S1X2: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2) + res = CCM_PRIMTYPE_HEXSTRING; + break; + case CCMFMT_P1I2: + case CCMFMT_L1I2: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1V2: + case CCMFMT_I1L2: + if (param_idx == 1) + res = CCM_PRIMTYPE_INTEGER; + else if (param_idx == 2) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_P1S2P3: + case CCMFMT_L1S2L3: + case CCMFMT_L1L2L3: + if (param_idx >= 1 && param_idx <= 3) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_P1S2I3: + case CCMFMT_L1P2I3: + if (param_idx == 1 || param_idx == 2) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 3) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_P1I2L3: + case CCMFMT_L1I2L3: + if (param_idx == 1 || param_idx == 3) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_P1I2I3: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2 || param_idx == 3) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1V2V3: + if (param_idx == 1) + res = CCM_PRIMTYPE_INTEGER; + else if (param_idx == 2 || param_idx == 3) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_I1P2I3: + if (param_idx == 1 || param_idx == 3) + res = CCM_PRIMTYPE_INTEGER; + else if (param_idx == 2) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_L1I2I3L4: + if (param_idx == 1 || param_idx == 4) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2 || param_idx == 3) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_P1S2P3I4: + if (param_idx >= 1 && param_idx <= 3) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 4) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4: + if (param_idx >= 1 && param_idx <= 4) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx >= 2 && param_idx <= 5) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4I5I6: + if (param_idx >= 1 && param_idx <= 6) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5I6I7: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx >= 2 && param_idx <= 7) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4I5I6I7I8: + if (param_idx >= 1 && param_idx <= 8) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5I6I7I8I9: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx >= 2 && param_idx <= 9) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_S1L2VV3: + case CCMFMT_S1R2VV3: + case CCMFMT_PP1: + case CCMFMT_VV1: + case CCMFMT_L1PP2: + case CCMFMT_L1VV2: + case CCMFMT_LL1: + case CCMFMT_R1VV2: + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_P1I2LL3: + case CCMFMT_L1I2LL3: + if (param_idx == 2) + res = CCM_PRIMTYPE_INTEGER; + else + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_L1II2: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1LL2: + if (param_idx == 1) + res = CCM_PRIMTYPE_INTEGER; + else + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_LAST: + default: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) + assert (0); + break; + } + return res; +} + +static Ccm_Hitype_t +ccm_param_hightype (COMPMSG_ID m, int param_idx) +{ + int vindex; + Ccm_Hitype_t res; + + if (param_idx <= 0 || param_idx > ccm_num_params (m)) + return CCM_HITYPE_NONE; + res = CCM_HITYPE_NONE; /* should always be updated */ + vindex = ccm_vis_index (m); + switch (ccm_attrs[vindex].fmt) + { + case CCMFMT_NONE: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) + assert (0); + break; + case CCMFMT_S1: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_S1S2: + if (param_idx == 1 || param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_S1L2: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_S1L2VV3: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + else + res = CCM_HITYPE_STRING; + break; + case CCMFMT_S1R2VV3: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + else if (param_idx == 2) + res = CCM_HITYPE_REGIONTAG; + else + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_S1X2: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + else if (param_idx == 2) + res = CCM_HITYPE_HEXSTRING; + break; + case CCMFMT_P1: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_P1S2: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_P1S2P3: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_P1S2P3I4: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + else if (param_idx == 4) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_P1S2I3: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + else if (param_idx == 3) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_P1P2: + if (param_idx == 1 || param_idx == 2) + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_P1L2: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_P1I2: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_P1I2L3: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 3) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_P1I2I3: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2 || param_idx == 3) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_P1I2LL3: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + else + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_PP1: + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_V1: + if (param_idx == 1) + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_V1V2: + if (param_idx == 1 || param_idx == 2) + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_V1L2: + if (param_idx == 1) + res = CCM_HITYPE_VARIABLE; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_VV1: + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_L1: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_L1S2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_L1S2L3: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_L1P2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_L1P2I3: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 3) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1PP2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_L1VV2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_L1L2: + if (param_idx == 1 || param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_L1L2L3: + if (param_idx >= 1 && param_idx <= 3) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_LL1: + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_L1R2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_REGIONTAG; + break; + case CCMFMT_L1I2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2L3: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2LL3: + if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + else + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_L1I2I3L4: + if (param_idx == 1 || param_idx == 4) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2 || param_idx == 3) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx >= 2 && param_idx <= 5) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5I6I7: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx >= 2 && param_idx <= 7) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5I6I7I8I9: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx >= 2 && param_idx <= 9) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1II2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_R1: + if (param_idx == 1) + res = CCM_HITYPE_REGIONTAG; + break; + case CCMFMT_R1VV2: + if (param_idx == 1) + res = CCM_HITYPE_REGIONTAG; + else + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_I1: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_I1P2I3: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 2) + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_I1V2: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 2) + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_I1V2V3: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 2 || param_idx == 3) + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_I1L2: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_I1LL2: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + else + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_I1I2I3I4: + if (param_idx >= 1 && param_idx <= 4) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4I5I6: + if (param_idx >= 1 && param_idx <= 6) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4I5I6I7I8: + if (param_idx >= 1 && param_idx <= 8) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_LAST: + default: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) + assert (0); + break; + } + return res; +} + +static void +ccm_vis_init () +{ + int size, vindex; + static int done = 0; + if (done) + return; + done = 1; + size = ccm_vis_index ((COMPMSG_ID) (CCMV_BASIC << 8)); + ccm_attrs = (Ccm_Attr_t *) calloc (size, sizeof (Ccm_Attr_t)); + if (ccm_attrs == NULL) + exit (1); + vindex = ccm_vis_index (CCM_MODDATE); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MODDATE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Source file %s, last modified on date %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1S2; + + vindex = ccm_vis_index (CCM_COMPVER); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_COMPVER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Component %s, version %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1S2; + + vindex = ccm_vis_index (CCM_COMPDATE); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_COMPDATE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Compilation date %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_COMPOPT); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_COMPOPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Compilation options %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_ACOMPOPT); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_ACOMPOPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Actual Compilation options %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_VAR_ALIAS); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_VAR_ALIAS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variable %s aliased to %s"); + ccm_attrs[vindex].fmt = CCMFMT_V1V2; + + vindex = ccm_vis_index (CCM_FBIRDIFF); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FBIRDIFF"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Profile feedback data inconsistent with" + " intermediate representation file; check compiler" + " version, flags and source file"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_OPTRED_SWAP); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_OPTRED_SWAP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Optimization level for %s reduced from %d to" + " %d due to insufficient swap space"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2I3; + + vindex = ccm_vis_index (CCM_OPTRED_CPLX); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_OPTRED_CPLX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Optimization level for %s reduced from %d to" + " %d due to program complexity"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2I3; + + vindex = ccm_vis_index (CCM_UNKNOWN); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNKNOWN"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Unexpected compiler comment %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_UNPAR_CALL); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_CALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it contains a" + " call to %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_PAR_SER); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_SER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Both serial and parallel versions generated for" + " loop below"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PAR_SER_VER); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_SER_VER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Both serial and parallel versions generated for" + " loop below; with parallel version used if %s," + " serial otherwise"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_PAR_DRECTV); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_DRECTV"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below parallelized by explicit user" + " directive"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_APAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_APAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below autoparallelized"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_AUTOPAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_AUTOPAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below autoparallelized; equivalent" + " explict directive is %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_UNPAR_DD); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_DD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below could not be parallelized because of a" + " data dependency on %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_UNPAR_DDA); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_DDA"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below could not be parallelized because of a" + " data dependency or aliasing of %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_UNPAR_ANONDD); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_ANONDD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below could not be parallelized because of" + " an anonymous data dependency"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_ANONDDA); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_ANONDDA"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below could not be parallelized because of" + " an anonymous data dependency or aliasing"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PAR_WORK); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_WORK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below parallelized, but might not contain" + " enough work to be efficiently run in parallel"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_EXIT); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_EXIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it contains" + " multiple exit points"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_STRNG); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_STRNG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it contains a" + " strange flow of control"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_IO); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_IO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it contains" + " I/O or other MT-unsafe calls"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PAR_BODY_NAME); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_BODY_NAME"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Parallel loop-body code is in function %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_UNPAR_NLOOPIDX); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NLOOPIDX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because loop index" + " not found"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_DRECTV); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_DRECTV"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because of explicit" + " user directive"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_NOTPROFIT); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NOTPROFIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it was not" + " profitable to do so"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_NEST); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NEST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it was" + " nested in a parallel loop"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_NOAUTO); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NOAUTO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because" + " autoparallelization is not enabled"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PR_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PR_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Private variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_SH_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SH_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Shared variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_TP_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_TP_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Threadprivate variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_RV_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_RV_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Reduction variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_IM_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IM_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_PR_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PR_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Private variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_SH_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SH_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Shared variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_TP_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_TP_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Threadprivate variables in OpenMP construct" + " below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_RV_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_RV_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Reduction variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_IM_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IM_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_UNPAR_IN_OMP); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_IN_OMP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it is inside" + " an OpenMP region"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_FP_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FP_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Firstprivate variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_LP_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LP_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Lastprivate variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_CP_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_CP_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Copyprivate variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_PR_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PR_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as PRIVATE in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_SH_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SH_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as SHARED in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_FP_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FP_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as FIRSTPRIVATE in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_LP_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LP_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as LASTPRIVATE in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_RV_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_RV_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as REDUCTION in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_FAIL_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_WARN | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FAIL_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables cannot be autoscoped in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_SERIALIZE_OAS); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_WARN | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SERIALIZE_OAS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "OpenMP parallel region below is serialized" + " because autoscoping has failed"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_CALL_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_CALL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it contains calls" + " to: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1PP2; + + vindex = ccm_vis_index (CCM_PAR_DRECTV_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PAR_DRECTV_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s parallelized by explicit user directive"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_APAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_APAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s autoparallelized"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_AUTOPAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_AUTOPAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s autoparallelized; equivalent" + " explict directive is %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1S2; + + vindex = ccm_vis_index (CCM_UNPAR_DD_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_DD_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be parallelized because of" + " data dependences on: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_UNPAR_DDA_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_DDA_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be parallelized because of a" + " data dependence or aliasing of: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_UNPAR_ANONDD_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_ANONDD_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be parallelized because of an" + " anonymous data dependence"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_ANONDDA_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_ANONDDA_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be parallelized because of an" + " anonymous data dependence or aliasing"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_PAR_WORK_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PAR_WORK_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s parallelized, but might not contain" + " enough work to run efficiently in parallel"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_EXIT_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_EXIT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it contains" + " multiple exit points"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_STRANGE_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_STRANGE_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it contains a" + " strange flow of control"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_IO_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_IO_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it contains" + " I/O or other MT-unsafe calls"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_PAR_BODY_NAME_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP; + ccm_attrs[vindex].name = "CCM_PAR_BODY_NAME_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s parallel loop-body code placed in" + " function %s along with %d inner loops"); + ccm_attrs[vindex].fmt = CCMFMT_L1P2I3; + + vindex = ccm_vis_index (CCM_UNPAR_NLOOPIDX_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_NLOOPIDX_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because loop index not" + " found"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_DRECTV_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_DRECTV_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because of explicit" + " user directive"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_NOTPROFIT_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_NOTPROFIT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it was not" + " profitable to do so"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_NEST_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_NEST_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it was" + " nested within a parallel loop"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_NOAUTO_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NOAUTO_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because" + " autoparallelization is not enabled"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_PR_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PR_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Private variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_SH_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_SH_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Shared variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_TP_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_TP_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Threadprivate variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_RV_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_RV_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Reduction variables of operator %s in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1L2VV3; + + vindex = ccm_vis_index (CCM_IM_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IM_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_PR_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PR_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Private variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_SH_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_SH_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Shared variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_TP_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_TP_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Threadprivate variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_RV_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_RV_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Reduction variables of operator %s in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1R2VV3; + + vindex = ccm_vis_index (CCM_IM_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IM_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_UNPAR_IN_OMP_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_IN_OMP_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it is inside" + " OpenMP region %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1R2; + + vindex = ccm_vis_index (CCM_FP_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FP_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Firstprivate variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_LP_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LP_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Lastprivate variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_CP_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_CP_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Copyprivate variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_PR_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PR_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as PRIVATE in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_SH_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_SH_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as SHARED in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_FP_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FP_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as FIRSTPRIVATE in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_LP_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LP_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as LASTPRIVATE in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_RV_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_RV_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as REDUCTION of operator" + " %s in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1R2VV3; + + vindex = ccm_vis_index (CCM_FAIL_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_WARN; + ccm_attrs[vindex].name = "CCM_FAIL_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables treated as shared because they cannot" + " be autoscoped in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_SERIALIZE_OAS_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_WARN; + ccm_attrs[vindex].name = "CCM_SERIALIZE_OAS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s will be executed by a single thread because" + " autoscoping for some variables was not successful"); + ccm_attrs[vindex].fmt = CCMFMT_R1; + + vindex = ccm_vis_index (CCM_QPERMVEC); + ccm_attrs[vindex].vis = CCMV_QUERY | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_QPERMVEC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Is %s a permutation vector during execution of" + " %s?"); + ccm_attrs[vindex].fmt = CCMFMT_V1L2; + + vindex = ccm_vis_index (CCM_QEXPR); + ccm_attrs[vindex].vis = CCMV_QUERY | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_QEXPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Is expression %s true for %s?"); + ccm_attrs[vindex].fmt = CCMFMT_S1L2; + + vindex = ccm_vis_index (CCM_QSAFECALL); + ccm_attrs[vindex].vis = CCMV_QUERY | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_QSAFECALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Is subroutine %s MP-safe as used in %s?"); + ccm_attrs[vindex].fmt = CCMFMT_P1L2; + + vindex = ccm_vis_index (CCM_LCOST); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LCOST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below estimated to cost %d cycles per" + " iteration"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_UNROLL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNROLL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below unrolled %d times"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_IMIX); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IMIX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls, and" + " %d FPdivs per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_I1I2I3I4I5I6; + + vindex = ccm_vis_index (CCM_SPILLS); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_UNIMPL | CCMV_WANT | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SPILLS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below required %d integer register spills," + " %d FP register spills, and used" + " %d integer registers and %d FP registers"); + ccm_attrs[vindex].fmt = CCMFMT_I1I2I3I4; + + vindex = ccm_vis_index (CCM_LFISSION); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LFISSION"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below fissioned into %d loops"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_LPEEL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LPEEL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below had iterations peeled off for better" + " unrolling and/or parallelization"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LBLOCKED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LBLOCKED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below blocked by %d for improved cache" + " performance"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_LTILED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LTILED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below tiled for better performance"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LUNRJAM); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LUNRJAM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below unrolled and jammed"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LWHILE2DO); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LWHILE2DO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Bounds test for loop below moved to top of loop"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_L2CALL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_L2CALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below replaced by a call to %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_LDEAD); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LDEAD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below deleted as dead code"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LINTRCHNG); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LINTRCHNG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below interchanged with loop on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_FUSEDTO); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FUSEDTO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below fused with loop on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_FUSEDFROM); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FUSEDFROM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop from line %d fused with loop below"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_VECINTRNSC); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_VECINTRNSC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below transformed to use calls to vector" + " intrinsic %s"); + ccm_attrs[vindex].fmt = CCMFMT_PP1; + + vindex = ccm_vis_index (CCM_LSTRIPMINE); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LSTRIPMINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below strip-mined"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LNEST2LOOPS); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LNEST2LOOPS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below collapsed with loop on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_LREVERSE); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LREVERSE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below has had its iteration direction" + " reversed"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_IMIX2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IMIX2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls," + " %d FPdivs, %d FPsubs, and %d FPsqrts per" + " iteration"); + ccm_attrs[vindex].fmt = CCMFMT_I1I2I3I4I5I6I7I8; + + vindex = ccm_vis_index (CCM_LUNRFULL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LUNRFULL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below fully unrolled"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_ELIM_NOAMORTINST); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_ELIM_NOAMORTINST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below was eliminated as it contains no" + " non-amortizable instructions"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_COMP_DALIGN); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_COMP_DALIGN"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Performance of loop below could be improved" + " by compiling with -dalign"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_INTIMIX); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_INTIMIX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below has %d int-loads, %d int-stores," + " %d alu-ops, %d muls, %d int-divs and" + " %d shifts per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_I1I2I3I4I5I6; + + vindex = ccm_vis_index (CCM_LMULTI_VERSION); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LMULTI_VERSION"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s multi-versioned. Specialized version" + " is %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2; + + vindex = ccm_vis_index (CCM_LCOST_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LCOST_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s estimated to cost %d cycles per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_UNROLL_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNROLL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s unrolled %d times"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_IMIX_B); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IMIX_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls, and" + " %d FPdivs per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5I6I7; + + vindex = ccm_vis_index (CCM_SPILLS_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_SPILLS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s required %d integer register spills," + " %d FP register spills, and used" + " %d integer registers and %d FP registers"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5; + + vindex = ccm_vis_index (CCM_LFISSION_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LFISSION_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s fissioned into %d loops, generating:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2LL3; + + vindex = ccm_vis_index (CCM_LFISSION_FRAG); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LFISSION_FRAG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s contains code from lines: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1II2; + + vindex = ccm_vis_index (CCM_LPEEL_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LPEEL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s had iterations peeled off for better" + " unrolling and/or parallelization"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_LBLOCKED_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LBLOCKED_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s blocked by %d for improved memory" + " hierarchy performance, new inner loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2L3; + + vindex = ccm_vis_index (CCM_LOUTER_UNROLL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LOUTER_UNROLL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s is outer-unrolled %d times as part" + " of unroll and jam"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_LJAMMED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LJAMMED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "All %d copies of %s are fused together" + " as part of unroll and jam"); + ccm_attrs[vindex].fmt = CCMFMT_I1L2; + + vindex = ccm_vis_index (CCM_LWHILE2DO_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LWHILE2DO_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Bounds test for %s moved to top of loop"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_L2CALL_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_L2CALL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s replaced by a call to %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1P2; + + vindex = ccm_vis_index (CCM_LDEAD_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LDEAD_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s deleted as dead code"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_LINTRCHNG_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LINTRCHNG_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s interchanged with %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2; + + vindex = ccm_vis_index (CCM_LINTRCHNG_ORDER); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LINTRCHNG_ORDER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "For loop nest below, the final order of loops" + " after interchanging and subsequent" + " transformations is: %s"); + ccm_attrs[vindex].fmt = CCMFMT_LL1; + + vindex = ccm_vis_index (CCM_FUSED_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FUSED_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s fused with %s, new loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2L3; + + vindex = ccm_vis_index (CCM_VECINTRNSC_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_VECINTRNSC_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s transformed to use calls to vector" + " intrinsics: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1PP2; + + vindex = ccm_vis_index (CCM_LSTRIPMINE_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LSTRIPMINE_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s strip-mined by %d, new inner loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2L3; + + vindex = ccm_vis_index (CCM_LNEST2LOOPS_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LNEST2LOOPS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s collapsed with %s, new loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2L3; + + vindex = ccm_vis_index (CCM_LREVERSE_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LREVERSE_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has had its iteration direction reversed"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_IMIX2_B); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_IMIX2_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls," + " %d FPdivs, %d FPsubs, and %d FPsqrts per" + " iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5I6I7I8I9; + + vindex = ccm_vis_index (CCM_LUNRFULL_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LUNRFULL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s fully unrolled"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_ELIM_NOAMORTINST_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_ELIM_NOAMORTINST_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s was eliminated as it contains no" + " non-amortizable instructions"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_COMP_DALIGN_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_COMP_DALIGN_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Performance of %s could be improved by" + " compiling with -dalign"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_INTIMIX_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INTIMIX_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has %d int-loads, %d int-stores," + " %d alu-ops, %d muls, %d int-divs and" + " %d shifts per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5I6I7; + + vindex = ccm_vis_index (CCM_OMP_REGION); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_OMP_REGION"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Source OpenMP region below has tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1; + + vindex = ccm_vis_index (CCM_LMICROVECTORIZE); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LMICROVECTORIZE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s is micro-vectorized"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_LMULTI_VERSION_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LMULTI_VERSION_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s multi-versioned for %s." + " Specialized version is %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1S2L3; + + vindex = ccm_vis_index (CCM_LCLONED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LCLONED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s cloned for %s. Clone is %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1S2L3; + + vindex = ccm_vis_index (CCM_LUNSWITCHED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LUNSWITCHED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s is unswitched. New loops" + " are %s and %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2L3; + + vindex = ccm_vis_index (CCM_LRESWITCHED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LRESWITCHED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loops %s and %s and their surrounding" + " conditional code have been merged to" + " form loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2L3; + + vindex = ccm_vis_index (CCM_LSKEWBLOCKED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LSKEWBLOCKED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s skew-blocked by %d with slope" + " %d for improved memory hierarchy" + " performance, new inner loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3L4; + + vindex = ccm_vis_index (CCM_IVSUB); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_IVSUB"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Induction variable substitution performed on %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_ONEITER_REPLACED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_ONEITER_REPLACED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s determined to have a trip count of 1;" + " converted to straight-line code"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_IMIX3_B); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_IMIX3_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls," + " %d FPmuladds, %d FPdivs, and %d FPsqrts per" + " iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5I6I7I8I9; + + vindex = ccm_vis_index (CCM_PIPELINE); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPELINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below pipelined"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PIPESTATS); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPESTATS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below scheduled with steady-state cycle" + " count = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_CALL); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_CALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " calls"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_INTCC); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTCC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it sets" + " multiple integer condition codes."); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_MBAR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_MBAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains a" + " memory barrier instruction"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_MNMX); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_MNMX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " a minimum or a maximum operation"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_U2FLT); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_U2FLT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " an unsigned to float conversion"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_GOT); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_GOT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it sets the" + " Global Offset Table pointer"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_IDIV); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_IDIV"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " an integer divide"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_PRFTCH); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_PRFTCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " a prefetch operation"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_EXIT); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_EXIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " an exit operation"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_REG); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_REG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " instructions that set the %%gsr or %%fsr register"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_UNS); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_UNS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it has an" + " unsigned loop counter"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_UNSUIT); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_UNSUIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop was unsuitable for pipelining"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_INTRINSIC); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTRINSIC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it has an" + " intrinsic call to %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NOPIPE_BIG); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_BIG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined as it is too big"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_INVINTPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVINTPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined as it contains too" + " many loop invariant integers = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_INVFLTPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVFLTPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined as it contains too" + " many loop invariant floats = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_INVDBLPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVDBLPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined as it contains too" + " many loop invariant doubles = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFIPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFIPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below was adversely affected by high" + " integer register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFDPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFDPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below was adversely affected by high" + " double register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFFPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFFPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below was adversely affected by high" + " float register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_INTPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined due to high" + " integer register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_DBLPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_DBLPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined due to high" + " double register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_FLTPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_FLTPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined due to high" + " float register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PIPELINE_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPELINE_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s pipelined"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_PIPESTATS_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPESTATS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s scheduled with steady-state cycle" + " count = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_CALL_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_CALL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " calls"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_INTCC_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTCC_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it sets" + " multiple integer condition codes."); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_MBAR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_MBAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " a memory barrier instruction"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_MNMX_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_MNMX_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " a minimum or a maximum operation"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_U2FLT_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_U2FLT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " an unsigned to float conversion"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_GOT_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP; + ccm_attrs[vindex].name = "CCM_NOPIPE_GOT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it sets the" + " Global Offset Table pointer"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_IDIV_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_IDIV_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " an integer divide"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_PRFTCH_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_PRFTCH_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " a prefetch operation"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_EXIT_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_EXIT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " an exit operation"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_REG_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP; + ccm_attrs[vindex].name = "CCM_NOPIPE_REG_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " instructions that set the %%gsr or %%fsr register"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_UNS_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_UNS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it has an" + " unsigned loop counter"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_UNSUIT_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_UNSUIT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s is unsuitable for pipelining"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_INTRINSIC_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTRINSIC_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " a call to intrinsic %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1P2; + + vindex = ccm_vis_index (CCM_NOPIPE_BIG_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_BIG_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined as it is too big"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_INVINTPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVINTPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined as it contains too" + " many loop invariant integers = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_INVFLTPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVFLTPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined as it contains too" + " many loop invariant floats = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_INVDBLPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVDBLPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined as it contains too" + " many loop invariant doubles = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFIPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFIPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s was adversely affected by high" + " integer register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFDPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFDPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s was adversely affected by high" + " double register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFFPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFFPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s was adversely affected by high" + " float register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_INTPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined due to high" + " integer register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_DBLPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_DBLPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined due to high" + " double register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_FLTPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_FLTPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined due to high" + " float register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_INLINE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_INLINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s into" + " the code for the following line"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_INLINE2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_INLINE2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s into" + " inline copy of function %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3; + + vindex = ccm_vis_index (CCM_INLINE_TMPLT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_TMPLT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from template file %s" + " into the code for the following line"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_INLINE_TMPLT2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_TMPLT2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from template file %s" + " into inline copy of function %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3; + + vindex = ccm_vis_index (CCM_INLINE_OUT_COPY); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_OUT_COPY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Out-of-line copy of inlined function %s from" + " source file %s generated"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_NINLINE_REC); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_REC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Recursive function %s inlined only up to" + " depth %d"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2; + + vindex = ccm_vis_index (CCM_NINLINE_NEST); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_NEST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because inlining is" + " already nested too deeply"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CMPLX); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CMPLX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " too many operations"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_FB); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_FB"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because the" + " profile-feedback execution count is too low"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_PAR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_PAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " explicit parallel pragmas"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_OPT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_OPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is" + " compiled with optimization level <= 2"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_USR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_USR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because either command" + " line option or source code pragma prohibited it," + " or it's not safe to inline it"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_AUTO); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_AUTO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because doing so" + " would make automatic storage for %s too large"); + ccm_attrs[vindex].fmt = CCMFMT_P1P2; + + vindex = ccm_vis_index (CCM_NINLINE_CALLS); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALLS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " too many calls"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_ACTUAL); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_ACTUAL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it has more" + " actual parameters than formal parameters"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_FORMAL); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_FORMAL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it has more" + " formal parameters than actual parameters"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_TYPE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_TYPE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because formal" + " argument type does not match actual type"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_ATYPE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_ATYPE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because array formal" + " argument does not match reshaped array actual" + " argument type"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RETTYPE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RETTYPE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because return type" + " does not match"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_EXCPT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_EXCPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it" + " guarded by an exception handler"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_UNSAFE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_UNSAFE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it might be" + " unsafe (call alloca(), etc)"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_ALIAS); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_ALIAS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because inlining it" + " will make the alias analysis in the calling" + " function more conservative"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_FEMARK); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_FEMARK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " setjmp/longjmp, or indirect goto, etc"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RAREX); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RAREX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is known" + " to be rarely executed"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_CLONING); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_CLONING"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s from source file %s cloned," + " creating cloned function %s; constant" + " parameters propagated to clone"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3; + + vindex = ccm_vis_index (CCM_INLINE_B); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s into" + " the code for the following line. %d loops" + " inlined"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2I3; + + vindex = ccm_vis_index (CCM_INLINE2_B); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE2_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s into" + " inline copy of function %s. %d loops inlined"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3I4; + + vindex = ccm_vis_index (CCM_INLINE_LOOP); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop in function %s, line %d has" + " tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2L3; + + vindex = ccm_vis_index (CCM_NINLINE_MULTIENTRY); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_MULTIENTRY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it" + " contains an ENTRY statement"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_VARARGS); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_VARARGS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because variable" + " argument routines cannot be inlined"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_UNSEEN_BODY); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_UNSEEN_BODY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because the compiler" + " has not seen the body of the function. Use" + " -xcrossfile or -xipo in order to inline it"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_UPLEVEL); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_UPLEVEL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is a" + " nested routine containing references to" + " variables defined in an outer function"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CMDLINE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CMDLINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because either" + " -xinline or source code pragma prohibited it"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_CMPLX); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_CMPLX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because of the" + " complexity of the calling routine"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_LANG_MISMATCH); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_LANG_MISMATCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it is in" + " a different language"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RTN_WEAK); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RTN_WEAK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it" + " is marked weak"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_WEAKFILE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_WEAKFILE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it is" + " in a different file and it contains a" + " call to a weak routine"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_TRYCATCH); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_TRYCATCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it is" + " in a different file and contains an" + " explicit try/catch"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_REGP); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_REGP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it would" + " cause excessive register pressure"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RTN_REGP); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RTN_REGP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it would" + " cause excessive register pressure"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_XPENSV); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_XPENSV"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because analysis" + " exceeds the compilation time limit"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_READONLYIR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_READONLYIR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is in a file" + " specified as read-only by -xipo_archive=readonly" + " and it contains calls to static functions"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_THUNK); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_THUNK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it is in a" + " compiler-generated function that does not" + " permit inlining"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_XTARGETS); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_XTARGETS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Indirect callsite has too many targets;" + " callsite marked do not inline"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NINLINE_SELFTAIL_RECURSIVE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_SELFTAIL_RECURSIVE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because" + " of a recursive tail-call to itself"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_PRAGMA); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_PRAGMA"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " explicit parallel or alias pragmas"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CMPLX2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CMPLX2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains too" + " many operations. Increase max_inst_hard in order" + " to inline it: -xinline_param=max_inst_hard:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RARE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RARE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because the call" + " is rarely executed"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_PAR2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_PAR2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is called" + " within a region guarded by an explicit" + " parallel pragmas"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_G_LIMIT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_G_LIMIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it would exceed" + " the permitted global code size growth limit. Try" + " to increase max_growth in order to inline it:" + " -xinline_param=max_growth:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_L_LIMIT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_L_LIMIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it would exceed" + " the maximum function size growth limit. Increase" + " max_function_inst in order to inline it:" + " -xinline_param=max_function_inst:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_REC2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_REC2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Recursive function %s is inlined only up to" + " %d levels and up to %d size. Increase" + " max_recursive_deptha or max_recursive_inst in" + " order to inline it:" + " -xinline_param=max_recursive_depth:n," + " -xinline_param=max_recursive_inst:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2I3; + + vindex = ccm_vis_index (CCM_NINLINE_FB2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_FB2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because the" + " profile-feedback execution count is too" + " low. Decrease min_counter in order to inline it:" + " -xinline_param:min_counter:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CS_CMPLX); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CS_CMPLX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because called" + " function's size is too big. Increase" + " max_inst_soft in order to inline it:" + " -xinline_param=max_inst_soft:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_R_EXCPT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_R_EXCPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " an exception handler"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_ASM); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_ASM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because" + " it contains asm statements"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_R_READONLYIR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_R_READONLYIR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is in a file" + " specified as read-only by -xipo_archive=readonly" + " and it is a static function"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_C_READONLYIR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_C_READONLYIR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because the calling" + " function is in a file specified as read-only" + " by -xipo_archive=readonly"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_NEVERRETURN); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_NEVERRETURN"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it" + " never returns"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_MPREFETCH); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MPREFETCH_LD); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_LD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted for load at %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MPREFETCH_ST); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_ST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted for store at %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MPREFETCH_FB); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_FB"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted based on feedback data"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MPREFETCH_FB_LD); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_FB_LD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted for load at %s based" + " on feedback data"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MPREFETCH_FB_ST); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_FB_ST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted for store at %s based" + " on feedback data"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MLOAD); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MLOAD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Load below refers to %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MSTORE); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MSTORE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Store below refers to %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MLOAD_P); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MLOAD_P"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Load below refers to %s, and was prefetched" + " at %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MSTORE_P); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MSTORE_P"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Store below refers to %s, and was prefetched" + " at %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_COPYIN); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_COPYIN"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Parameter %d caused a copyin in the following" + " call"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_COPYOUT); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_COPYOUT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Parameter %d caused a copyout in the following" + " call"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_COPYINOUT); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_COPYINOUT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Parameter %d caused both a copyin and copyout" + " in the following call"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PADDING); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PADDING"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Padding of %d bytes inserted before" + " array %s"); + ccm_attrs[vindex].fmt = CCMFMT_I1V2; + + vindex = ccm_vis_index (CCM_PADCOMMON); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_PADCOMMON"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Padding of %d bytes inserted before" + " array %s in common block %s"); + ccm_attrs[vindex].fmt = CCMFMT_I1V2V3; + + vindex = ccm_vis_index (CCM_ALIGN_EQ); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_ALIGN_EQ"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variable/array %s can not be double-aligned," + " because it is equivalenced"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_ALIGN_PERF); + ccm_attrs[vindex].vis = CCMV_FE; + ccm_attrs[vindex].name = "CCM_ALIGN_PERF"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Alignment of variables in common block may cause" + " performance degradation"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_ALIGN_STRUCT); + ccm_attrs[vindex].vis = CCMV_FE; + ccm_attrs[vindex].name = "CCM_ALIGN_STRUCT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Alignment of component %s in numeric sequence" + " structure %s may cause performance degradation"); + ccm_attrs[vindex].fmt = CCMFMT_S1S2; + + vindex = ccm_vis_index (CCM_TMP_COPY); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s copied to a temporary"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_TMP_COPYM); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s might be copied to a temporary;" + " runtime decision made"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_PROC_MISMATCH); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_PROC_MISMATCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %d to subprogram %s differs from" + " reference on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1P2I3; + + vindex = ccm_vis_index (CCM_PROC_MISMATCH2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_PROC_MISMATCH2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Scalar argument %d to subprogram %s is" + " referred to as an array on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1P2I3; + + vindex = ccm_vis_index (CCM_PROC_MISMATCH3); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_PROC_MISMATCH3"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Return type/rank from subprogram %s differs" + " from return on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2; + + vindex = ccm_vis_index (CCM_DO_EXPR); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_DO_EXPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "DO statement bounds lead to no executions of the" + " loop"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_AUTO_BND); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_AUTO_BND"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "The bounds for automatic variable %s are not" + " available at all entry points; zero-length" + " variable might be allocated"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_LIT_PAD); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LIT_PAD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "The character string literal %s padded" + " to the length specified for the dummy argument"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_ARRAY_LOOP); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_ARRAY_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Array statement below generated a loop"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_ARRAY_LOOPNEST); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_ARRAY_LOOPNEST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Array statement below generated %d nested loops"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_ALIGN_PERF2); + ccm_attrs[vindex].vis = CCMV_FE; + ccm_attrs[vindex].name = "CCM_ALIGN_PERF2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Alignment of variable %s in common block %s" + " may cause a performance degradation"); + ccm_attrs[vindex].fmt = CCMFMT_V1V2; + + vindex = ccm_vis_index (CCM_ALIGN_PERF3); + ccm_attrs[vindex].vis = CCMV_FE; + ccm_attrs[vindex].name = "CCM_ALIGN_PERF3"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Alignment of variable %s in blank common may" + " cause a performance degradation"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_IO_LOOP_ARRAY); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_IO_LOOP_ARRAY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "I/O implied do item below generated an array" + " section"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_TMPCONST); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_TMPCONST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit invocation of class %s constructor for" + " temporary"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_TMPDEST); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_TMPDEST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit invocation of class %s destructor for" + " temporary"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_DBL_CONST); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_DBL_CONST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Double constant %s used in float expression"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MINLINE); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MINLINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s by" + " front-end"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_MINLINE2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MINLINE2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s from source file %s inlined into" + " inline copy of method %s by front-end"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3; + + vindex = ccm_vis_index (CCM_MINLINE3); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MINLINE3"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it uses keyword" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_MINLINE4); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MINLINE4"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is too" + " complex"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_TMP_COPYOUT); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYOUT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s copied from a temporary"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_TMP_COPYOUTM); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYOUTM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s might be copied from a temporary;" + " runtime decision made"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_TMP_COPYINOUT); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYINOUT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s copied in and out of a temporary"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_TMP_COPYINOUTM); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYINOUTM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s might be copied in and out of" + " a temporary; runtime decision made"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_ARRAY_LOOP_2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_ARRAY_LOOP_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Array statement below generated loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_ARRAY_LOOPNEST_2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_ARRAY_LOOPNEST_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Array statement below generated %d nested" + " loops: %s"); + ccm_attrs[vindex].fmt = CCMFMT_I1LL2; + + vindex = ccm_vis_index (CCM_IO_LOOP_ARRAY_2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_IO_LOOP_ARRAY_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "I/O implied do item below generated an array" + " section: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_USER_LOOP); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_USER_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Source loop below has tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_FOUND_LOOP); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_FOUND_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Discovered loop below has tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_MFUNCTION_LOOP); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MFUNCTION_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Copy in M-function of loop below has tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_FSIMPLE); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FSIMPLE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Transformations for fsimple=%d applied"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_STACK); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_STACK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s requires %d Mbytes of stack" + " storage"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2; + + vindex = ccm_vis_index (CCM_TAILRECUR); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TAILRECUR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Recursive tail call in %s optimized to jump to" + " entry point"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_TAILCALL); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_TAILCALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to function %s was tail-call optimized"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NI_EXIT_OR_PSEUDO); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_EXIT_OR_PSEUDO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains the pseudo instruction %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_BAD_UNARY_OPC); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_UNARY_OPC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains the instruction opcode %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_INT_LDD_ON_V9); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_INT_LDD_ON_V9"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains integer ldd instructions, which are" + " deprecated in the v9 architecture"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_LATE_INL_OPC); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_LATE_INL_OPC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains the instruction opcode %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_BAD_IMM_OP); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_IMM_OP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because the" + " relocation or immediate operand %s is not well" + " understood by the optimizer"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_BAD_STATELEAF); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_STATELEAF"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " references the state register %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_BAD_ASR_19); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_ASR_19"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because" + " %%asr19 is not supported in pre v8plus code"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_BAD_FSR_USE); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_FSR_USE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because" + " references to %%fsr can only be optimized when the" + " -iaopts flag is used"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_BAD_REGISTER); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_REGISTER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " references the register %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_NO_RET_VAL); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_NO_RET_VAL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " does not return the value declared"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_DELAY); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_DELAY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains a non nop delay slot"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_SCALL); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_SCALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " calls a function which returns a structure"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_CASE_POSITION); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_CASE_POSITION"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Case block below was placed at position %d" + " based on execution frequency"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_CALL_WITH_CODE); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_CALL_WITH_CODE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s replaced with inline code. %d" + " loops created: %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2LL3; + + vindex = ccm_vis_index (CCM_NI_BAD_SP_ADDR); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_SP_ADDR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains a %%sp+reg address"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_BAD_SP_USAGE); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_SP_USAGE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " uses/defines the stack pointer in a non-load/store instruction"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_MIXED_REG_TYPES); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_MIXED_REG_TYPES"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains register %s used as both x-register and register pair"); + ccm_attrs[vindex].fmt = CCMFMT_S1; +} diff --git a/gprofng/src/comp_com.h b/gprofng/src/comp_com.h new file mode 100644 index 0000000..e410924 --- /dev/null +++ b/gprofng/src/comp_com.h @@ -0,0 +1,903 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _COMP_COM_H +#define _COMP_COM_H + +#include <sys/types.h> +#include <nl_types.h> + +/* + * This file describes format for the compiler-commentary + * section to be added to .o's and propagated to the a.out. It reflects + * information the compiler can expose to the user about his or her + * program. The section should be generated for all compiles where + * the user has specified -g on the compile line. + * + * In the analyzer, display of the messages will be governed by a user UI + * that sets a vis_bits bitmap, and matches it against a show_bits + * bitmap table, which is maintained separately from the producer + * code. For any message, if (vis_bits&show_bits) is non-zero, the + * message is shown. If zero, the message is not shown. A similar + * mechanism would be used for a stand-alone source or disassembly browser. + * + * + * The .compcom Section + * -------------------- + * The section will be named ".compcom"; it is generated for each + * .o, and aggregated into a single section in the a.out. In that + * section, each .o's data is separate, and the tools will loop + * over the data for each .o in order to find the subsection for + * the particular .o being annotated. + * + * + * Since the header is fixed-length, and the total size of the section + * can be easily determined as: + * + * sizeof(stuct compcomhdr) + * + msgcount * sizeof(struct compmsg) + * + paramcount * sizeof(int32_t) + * + stringlen + * + * there is no need to have the size in the header. + */ + +typedef struct +{ /* Header describing the section */ + int32_t srcname; /* index into strings of source file path */ + int32_t version; /* a version number for the .compcom format */ + int32_t msgcount; /* count of messages in the section */ + int32_t paramcount; /* count of parameters in the section */ + int32_t stringcount; /* count of strings in the section */ + int32_t stringlen; /* count of total bytes in strings */ +} compcomhdr; + +/* + * The data for the .o after the header as: + * + * compmsg msgs[msgcount]; the array of messages + * int32_t param[paramcount]; the parameters used in the messages + * parameters are either integers or + * string-indices + * char msgstrings[stringlen]; the strings used in the messages + */ + +/* + * Message Classes and Visualization Bits + * -------------------------------------- + * Each of the messages above may belong to zero or more visualization + * classes, governed by a table using zero or more of the following symbolic + * names for the classes: + */ +typedef enum { +CCMV_WANT = 0x000, /* High-priority RFE -- used only for human */ + /* reading of message list */ +CCMV_UNIMPL = 0x000, /* Unimplemented -- used only for human */ + /* reading of message list */ +CCMV_OBS = 0x000, /* Obsolete -- to be replaced by a different */ + /* message with different parameters -- */ + /* used only for human reading of message */ + /* list */ +CCMV_VER = 0x001, /* Versioning messages */ +CCMV_WARN = 0x002, /* Warning messages */ +CCMV_PAR = 0x004, /* Parallelization messages */ +CCMV_QUERY = 0x008, /* Compiler queries */ +CCMV_LOOP = 0x010, /* Loop detail messages */ +CCMV_PIPE = 0x020, /* Pipelining messages */ +CCMV_INLINE = 0x040, /* Inlining information */ +CCMV_MEMOPS = 0x080, /* Messages concerning memory operations */ +CCMV_FE = 0x100, /* Front-end messages (all compilers) */ +CCMV_CG = 0x200, /* Code-generator messages (all compilers) */ +CCMV_BASIC = 0x400, /* for default messages */ +CCMV_ALL = 0x7FFFFFFF /* for all messages */ +} COMPCLASS_ID; + +typedef enum ccm_msgid +{ + /* Group: Versioning Messages */ + /* All of these are global to the .o, and will */ + /* have lineno = pcoffset = 0 */ + +CCM_MODDATE=0x00100, /* Source file <s1>, last modified on date <s2> */ +CCM_COMPVER, /* Component <s1>, version <s2> */ + /* [Emitted for each component of the compiler.] */ +CCM_COMPDATE, /* Compilation date <s1> */ + /* [<s1> is an I18n string with the date and time] */ +CCM_COMPOPT, /* Compilation options <s1> */ + /* [As specified by the user] */ +CCM_ACOMPOPT, /* Actual Compilation options <s1> */ + /* [As expanded by the driver] */ + + /* Group: Warning Messages */ +CCM_VAR_ALIAS=0x00200, /* Variable <v1> aliased to <v2> */ +CCM_FBIRDIFF, /* Profile feedback data inconsistent with */ + /* intermediate representation file; check compiler */ + /* version, flags and source file */ +CCM_OPTRED_SWAP, /* Optimization level for <p1> reduced from <i2> to */ + /* <i3> due to insufficient swap space */ +CCM_OPTRED_CPLX, /* Optimization level for <p1> reduced from <i2> to */ + /* <i3> due to program complexity */ +CCM_UNKNOWN, /* Unexpected compiler comment <i1> */ + + /* Group: Parallelization Messages */ +CCM_UNPAR_CALL=0x00400, /* Loop below not parallelized because it contains a */ + /* call to <p1> */ + + /* CCMV_WANT: Don't generate CCM_PAR_SER; always use CCM_PAR_SER_VER */ +CCM_PAR_SER, /* Both serial and parallel versions generated for */ + /* loop below */ +CCM_PAR_SER_VER, /* Both serial and parallel versions generated for */ + /* loop below; with parallel version used if <s1>, */ + /* serial otherwise */ +CCM_PAR_DRECTV, /* Loop below parallelized by explicit user */ + /* directive */ +CCM_APAR, /* Loop below autoparallelized */ +CCM_AUTOPAR, /* Loop below autoparallelized; equivalent */ + /* explict directive is <s1> */ +CCM_UNPAR_DD, /* Loop below could not be parallelized because of a */ + /* data dependency on <v1>, <v2>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_UNPAR_DDA, /* Loop below could not be parallelized because of a */ + /* data dependency or aliasing of <v1>, <v2>, ... */ +CCM_UNPAR_ANONDD, /* Loop below could not be parallelized because of */ + /* an anonymous data dependency */ +CCM_UNPAR_ANONDDA, /* Loop below could not be parallelized because of */ + /* an anonymous data dependency or aliasing */ +CCM_PAR_WORK, /* Loop below parallelized, but might not contain */ + /* enough work to be efficiently run in parallel */ +CCM_UNPAR_EXIT, /* Loop below not parallelized because it contains */ + /* multiple exit points */ +CCM_UNPAR_STRNG, /* Loop below not parallelized because it contains a */ + /* strange flow of control */ +CCM_UNPAR_IO, /* Loop below not parallelized because it contains */ + /* I/O or other MT-unsafe calls */ +CCM_PAR_BODY_NAME, /* Parallel loop-body code is in function <p1> */ +CCM_UNPAR_NLOOPIDX, /* Loop below not parallelized because loop index */ + /* not found */ +CCM_UNPAR_DRECTV, /* Loop below not parallelized because of explicit */ + /* user directive */ +CCM_UNPAR_NOTPROFIT, /* Loop below not parallelized because it was not */ + /* profitable to do so */ +CCM_UNPAR_NEST, /* Loop below not parallelized because it was */ + /* nested in a parallel loop */ +CCM_UNPAR, /* Loop below not parallelized */ +CCM_UNPAR_NOAUTO, /* Loop below not parallelized because */ + /* autoparallelization is not enabled */ +CCM_PR_L_VAR, /* Private variables in loop below: */ + /* <v1>, <v2>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_SH_L_VAR, /* Shared variables in loop below: */ + /* <v1>, <v2>, ... */ +CCM_TP_L_VAR, /* Threadprivate variables in loop below: */ + /* <v1>, <v2>, ... */ +CCM_RV_L_VAR, /* Reduction variables in loop below: */ + /* <v1>, <v2>, ... */ +CCM_IM_L_VAR, /* Implicit variables in loop below: */ + /* <v1>, <v2>, ... */ +CCM_PR_O_VAR, /* Private variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_SH_O_VAR, /* Shared variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_TP_O_VAR, /* Threadprivate variables in OpenMP construct */ + /* below: <v1>, <v2>, ... */ +CCM_RV_O_VAR, /* Reduction variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_IM_O_VAR, /* Implicit variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_UNPAR_IN_OMP, /* Loop below not parallelized because it is inside */ + /* an OpenMP region */ +CCM_FP_O_VAR, /* Firstprivate variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_LP_O_VAR, /* Lastprivate variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_CP_O_VAR, /* Copyprivate variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_PR_OAS_VAR, /* Variables autoscoped as PRIVATE in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_SH_OAS_VAR, /* Variables autoscoped as SHARED in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_FP_OAS_VAR, /* Variables autoscoped as FIRSTPRIVATE in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_LP_OAS_VAR, /* Variables autoscoped as LASTPRIVATE in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_RV_OAS_VAR, /* Variables autoscoped as REDUCTION in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_FAIL_OAS_VAR, /* Variables cannot be autoscoped in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_SERIALIZE_OAS, /* OpenMP parallel region below is serialized */ + /* because autoscoping has failed */ +CCM_UNPAR_CALL_2, /* <l1> not parallelized because it contains calls */ + /* to: <p2>, <p3>, ... */ +CCM_PAR_DRECTV_2, /* <l1> parallelized by explicit user directive */ +CCM_APAR_2, /* <l1> autoparallelized */ +CCM_AUTOPAR_2, /* <l1> autoparallelized; equivalent */ + /* explict directive is <s2> */ +CCM_UNPAR_DD_2, /* <l1> could not be parallelized because of */ + /* data dependences on: <v2>, <v3>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_UNPAR_DDA_2, /* <l1> could not be parallelized because of a */ + /* data dependence or aliasing of: <v2>, <v3>, ... */ +CCM_UNPAR_ANONDD_2, /* <l1> could not be parallelized because of an */ + /* anonymous data dependence */ +CCM_UNPAR_ANONDDA_2, /* <l1> could not be parallelized because of an */ + /* anonymous data dependence or aliasing */ +CCM_PAR_WORK_2, /* <l1> parallelized, but might not contain */ + /* enough work to run efficiently in parallel */ +CCM_UNPAR_EXIT_2, /* <l1> not parallelized because it contains */ + /* multiple exit points */ +CCM_UNPAR_STRANGE_2, /* <l1> not parallelized because it contains a */ + /* strange flow of control */ +CCM_UNPAR_IO_2, /* <l1> not parallelized because it contains */ + /* I/O or other MT-unsafe calls */ +CCM_PAR_BODY_NAME_2, /* <l1> parallel loop-body code placed in */ + /* function <p2> along with <i3> inner loops */ +CCM_UNPAR_NLOOPIDX_2, /* <l1> not parallelized because loop index not */ + /* found */ +CCM_UNPAR_DRECTV_2, /* <l1> not parallelized because of explicit */ + /* user directive */ +CCM_UNPAR_NOTPROFIT_2, /* <l1> not parallelized because it was not */ + /* profitable to do so */ +CCM_UNPAR_NEST_2, /* <l1> not parallelized because it was */ + /* nested within a parallel loop */ +CCM_UNPAR_2, /* <l1> not parallelized */ +CCM_UNPAR_NOAUTO_2, /* <l1> not parallelized because */ + /* autoparallelization is not enabled */ +CCM_PR_L_VAR_2, /* Private variables in <l1>: */ + /* <v2>, <v3>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_SH_L_VAR_2, /* Shared variables in <l1>: */ + /* <v2>, <v3>, ... */ +CCM_TP_L_VAR_2, /* Threadprivate variables in <l1>: */ + /* <v2>, <v3>, ... */ +CCM_RV_L_VAR_2, /* Reduction variables of operator <s1> in <l2>: */ + /* <v3>, <v4>, ... */ +CCM_IM_L_VAR_2, /* Implicit variables in <l1>: */ + /* <v2>, <v3>, ... */ +CCM_PR_O_VAR_2, /* Private variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_SH_O_VAR_2, /* Shared variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_TP_O_VAR_2, /* Threadprivate variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_RV_O_VAR_2, /* Reduction variables of operator <s1> in <r2>: */ + /* <v3>, <v4>, ... */ +CCM_IM_O_VAR_2, /* Implicit variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_UNPAR_IN_OMP_2, /* <l1> not parallelized because it is inside */ + /* OpenMP region <r2> */ +CCM_FP_O_VAR_2, /* Firstprivate variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_LP_O_VAR_2, /* Lastprivate variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_CP_O_VAR_2, /* Copyprivate variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_PR_OAS_VAR_2, /* Variables autoscoped as PRIVATE in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_SH_OAS_VAR_2, /* Variables autoscoped as SHARED in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_FP_OAS_VAR_2, /* Variables autoscoped as FIRSTPRIVATE in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_LP_OAS_VAR_2, /* Variables autoscoped as LASTPRIVATE in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_RV_OAS_VAR_2, /* Variables autoscoped as REDUCTION of operator */ + /* <s1> in <r2>: <v3>, <v4>, ... */ +CCM_FAIL_OAS_VAR_2, /* Variables treated as shared because they cannot */ + /* be autoscoped in <r1>: <v2>, <v3>, ... */ +CCM_SERIALIZE_OAS_2, /* <r1> will be executed by a single thread because */ + /* autoscoping for some variables was not successful */ + + /* Group: Parallelization Questions asked of the user */ + /* How will the user answer these questions? */ +CCM_QPERMVEC=0x00800, /* Is <v1> a permutation vector during execution of */ + /* <l2>? */ +CCM_QEXPR, /* Is expression <s1> true for <l2>? */ +CCM_QSAFECALL, /* Is subroutine <p1> MP-safe as used in <l2>? */ + + /* Group: Loop Optimization Messages */ +CCM_LCOST=0x01000, /* Loop below estimated to cost <i1> cycles per */ + /* iteration */ +CCM_UNROLL, /* Loop below unrolled <i1> times */ + /* CCMV_WANT: the next one should be replaced by CCM_IMIX2 */ +CCM_IMIX, /* Loop below has <i1> loads, <i2> stores, */ + /* <i3> prefetches, <i4> FPadds, <i5> FPmuls, and */ + /* <i6> FPdivs per iteration */ +CCM_SPILLS, /* Loop below required <i1> integer register spills, */ + /* <i2> FP register spills, and used */ + /* <i3> integer registers and <i4> FP registers */ +CCM_LFISSION, /* Loop below fissioned into <i1> loops */ +CCM_LPEEL, /* Loop below had iterations peeled off for better */ + /* unrolling and/or parallelization */ +CCM_LBLOCKED, /* Loop below blocked by <i1> for improved cache */ + /* performance */ +CCM_LTILED, /* Loop below tiled for better performance */ +CCM_LUNRJAM, /* Loop below unrolled and jammed */ +CCM_LWHILE2DO, /* Bounds test for loop below moved to top of loop */ +CCM_L2CALL, /* Loop below replaced by a call to <p1> */ +CCM_LDEAD, /* Loop below deleted as dead code */ +CCM_LINTRCHNG, /* Loop below interchanged with loop on line <i1> */ +CCM_FUSEDTO, /* Loop below fused with loop on line <i1> */ +CCM_FUSEDFROM, /* Loop from line <i1> fused with loop below */ +CCM_VECINTRNSC, /* Loop below transformed to use calls to vector */ + /* intrinsic <p1>, <p2>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_LSTRIPMINE, /* Loop below strip-mined */ +CCM_LNEST2LOOPS, /* Loop below collapsed with loop on line <i1> */ +CCM_LREVERSE, /* Loop below has had its iteration direction */ + /* reversed */ +CCM_IMIX2, /* Loop below has <i1> loads, <i2> stores, */ + /* <i3> prefetches, <i4> FPadds, <i5> FPmuls, */ + /* <i6> FPdivs, <i7> FPsubs, and <i8> FPsqrts per */ + /* iteration */ +CCM_LUNRFULL, /* Loop below fully unrolled */ +CCM_ELIM_NOAMORTINST, /* Loop below was eliminated as it contains no */ + /* non-amortizable instructions */ +CCM_COMP_DALIGN, /* Performance of loop below could be improved */ + /* by compiling with -dalign */ +CCM_INTIMIX, /* Loop below has <i1> int-loads, <i2> int-stores, */ + /* <i3> alu-ops, <i4> muls, <i5> int-divs and */ + /* <i6> shifts per iteration */ +CCM_LMULTI_VERSION, /* <l1> multi-versioned. Specialized version */ + /* is <l2> */ +CCM_LCOST_2, /* <l1> estimated to cost <i2> cycles per iteration */ +CCM_UNROLL_2, /* <l1> unrolled <i2> times */ + + /* CCMV_WANT: the next one should be replaced by CCM_IMIX2_B or CCM_IMIX3_B */ +CCM_IMIX_B, /* <l1> has <i2> loads, <i3> stores, */ + /* <i4> prefetches, <i5> FPadds, <i6> FPmuls, and */ + /* <i7> FPdivs per iteration */ +CCM_SPILLS_2, /* <l1> required <i2> integer register spills, */ + /* <i3> FP register spills, and used */ + /* <i4> integer registers and <i5> FP registers */ +CCM_LFISSION_2, /* <l1> fissioned into <i2> loops, generating: */ + /* <l3>, <l4>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_LFISSION_FRAG, /* <l1> contains code from lines: <i2>, <i3>, ... */ +CCM_LPEEL_2, /* <l1> had iterations peeled off for better */ + /* unrolling and/or parallelization */ +CCM_LBLOCKED_2, /* <l1> blocked by <i2> for improved memory */ + /* hierarchy performance, new inner loop <l3> */ +CCM_LOUTER_UNROLL, /* <l1> is outer-unrolled <i2> times as part */ + /* of unroll and jam */ +CCM_LJAMMED, /* All <i1> copies of <l2> are fused together */ + /* as part of unroll and jam */ +CCM_LWHILE2DO_2, /* Bounds test for <l1> moved to top of loop */ +CCM_L2CALL_2, /* <l1> replaced by a call to <p2> */ +CCM_LDEAD_2, /* <l1> deleted as dead code */ +CCM_LINTRCHNG_2, /* <l1> interchanged with <l2> */ +CCM_LINTRCHNG_ORDER, /* For loop nest below, the final order of loops */ + /* after interchanging and subsequent */ + /* transformations is: <l1>, <l2>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_FUSED_2, /* <l1> fused with <l2>, new loop <l3> */ +CCM_VECINTRNSC_2, /* <l1> transformed to use calls to vector */ + /* intrinsics: <p2>, <p3>, ... */ +CCM_LSTRIPMINE_2, /* <l1> strip-mined by <i2>, new inner loop <l3> */ +CCM_LNEST2LOOPS_2, /* <l1> collapsed with <l2>, new loop <l3> */ +CCM_LREVERSE_2, /* <l1> has had its iteration direction reversed */ +CCM_IMIX2_B, /* <l1> has <i2> loads, <i3> stores, */ + /* <i4> prefetches, <i5> FPadds, <i6> FPmuls, */ + /* <i7> FPdivs, <i8> FPsubs, and <i9> FPsqrts per */ + /* iteration */ +CCM_LUNRFULL_2, /* <l1> fully unrolled */ +CCM_ELIM_NOAMORTINST_2, /* <l1> was eliminated as it contains no */ + /* non-amortizable instructions */ +CCM_COMP_DALIGN_2, /* Performance of <l1> could be improved by */ + /* compiling with -dalign */ +CCM_INTIMIX_2, /* <l1> has <i2> int-loads, <i3> int-stores, */ + /* <i4> alu-ops, <i5> muls, <i6> int-divs and */ + /* <i7> shifts per iteration */ +CCM_OMP_REGION, /* Source OpenMP region below has tag <r1> */ +CCM_LMICROVECTORIZE, /* <l1> is micro-vectorized */ +CCM_LMULTI_VERSION_2, /* <l1> multi-versioned for <s2>. */ + /* Specialized version is <l3> */ +CCM_LCLONED, /* <l1> cloned for <s2>. Clone is <l3> */ +CCM_LUNSWITCHED, /* <l1> is unswitched. New loops */ + /* are <l2> and <l3> */ +CCM_LRESWITCHED, /* Loops <l1> and <l2> and their surrounding */ + /* conditional code have been merged to */ + /* form loop <l3> */ +CCM_LSKEWBLOCKED, /* <l1> skew-blocked by <i2> with slope */ + /* <i3> for improved memory hierarchy */ + /* performance, new inner loop <l4> */ +CCM_IVSUB, /* Induction variable substitution performed on <l1> */ +CCM_ONEITER_REPLACED, /* <l1> determined to have a trip count of 1; */ + /* converted to straight-line code */ +CCM_IMIX3_B, /* <l1> has <i2> loads, <i3> stores, */ + /* <i4> prefetches, <i5> FPadds, <i6> FPmuls, */ + /* <i7> FPmuladds, <i8> FPdivs, and <i9> FPsqrts per */ + /* iteration */ + + /* Group: Pipelining Messages */ +CCM_PIPELINE=0x02000, /* Loop below pipelined */ +CCM_PIPESTATS, /* Loop below scheduled with steady-state cycle */ + /* count = <i1> */ +CCM_NOPIPE_CALL, /* Loop could not be pipelined because it contains */ + /* calls */ +CCM_NOPIPE_INTCC, /* Loop could not be pipelined because it sets */ + /* multiple integer condition codes. */ +CCM_NOPIPE_MBAR, /* Loop could not be pipelined because it contains a */ + /* memory barrier instruction */ +CCM_NOPIPE_MNMX, /* Loop could not be pipelined because it contains */ + /* a minimum or a maximum operation */ +CCM_NOPIPE_U2FLT, /* Loop could not be pipelined because it contains */ + /* an unsigned to float conversion */ +CCM_NOPIPE_GOT, /* Loop could not be pipelined because it sets the */ + /* Global Offset Table pointer */ +CCM_NOPIPE_IDIV, /* Loop could not be pipelined because it contains */ + /* an integer divide */ +CCM_NOPIPE_PRFTCH, /* Loop could not be pipelined because it contains */ + /* a prefetch operation */ +CCM_NOPIPE_EXIT, /* Loop could not be pipelined because it contains */ + /* an exit operation */ +CCM_NOPIPE_REG, /* Loop could not be pipelined because it contains */ + /* instructions that set the %gsr or %fsr register */ +CCM_NOPIPE_UNS, /* Loop could not be pipelined because it has an */ + /* unsigned loop counter */ +CCM_NOPIPE_UNSUIT, /* Loop was unsuitable for pipelining */ +CCM_NOPIPE_INTRINSIC, /* Loop could not be pipelined because it has an */ + /* intrinsic call to <p1> */ +CCM_NOPIPE_BIG, /* Loop could not be pipelined as it is too big */ +CCM_NOPIPE_INVINTPR, /* Loop could not be pipelined as it contains too */ + /* many loop invariant integers = <i1> */ +CCM_NOPIPE_INVFLTPR, /* Loop could not be pipelined as it contains too */ + /* many loop invariant floats = <i1> */ +CCM_NOPIPE_INVDBLPR, /* Loop could not be pipelined as it contains too */ + /* many loop invariant doubles = <i1> */ +CCM_PIPE_SCHEDAFIPR, /* Loop below was adversely affected by high */ + /* integer register pressure = <i1> */ +CCM_PIPE_SCHEDAFDPR, /* Loop below was adversely affected by high */ + /* double register pressure = <i1> */ +CCM_PIPE_SCHEDAFFPR, /* Loop below was adversely affected by high */ + /* float register pressure = <i1> */ +CCM_NOPIPE_INTPR, /* Loop could not be pipelined due to high */ + /* integer register pressure = <i1> */ +CCM_NOPIPE_DBLPR, /* Loop could not be pipelined due to high */ + /* double register pressure = <i1> */ +CCM_NOPIPE_FLTPR, /* Loop could not be pipelined due to high */ + /* float register pressure = <i1> */ +CCM_PIPELINE_2, /* <l1> pipelined */ +CCM_PIPESTATS_2, /* <l1> scheduled with steady-state cycle */ + /* count = <i2> */ +CCM_NOPIPE_CALL_2, /* <l1> could not be pipelined because it contains */ + /* calls */ +CCM_NOPIPE_INTCC_2, /* <l1> could not be pipelined because it sets */ + /* multiple integer condition codes. */ +CCM_NOPIPE_MBAR_2, /* <l1> could not be pipelined because it contains */ + /* a memory barrier instruction */ +CCM_NOPIPE_MNMX_2, /* <l1> could not be pipelined because it contains */ + /* a minimum or a maximum operation */ +CCM_NOPIPE_U2FLT_2, /* <l1> could not be pipelined because it contains */ + /* an unsigned to float conversion */ +CCM_NOPIPE_GOT_2, /* <l1> could not be pipelined because it sets the */ + /* Global Offset Table pointer */ +CCM_NOPIPE_IDIV_2, /* <l1> could not be pipelined because it contains */ + /* an integer divide */ +CCM_NOPIPE_PRFTCH_2, /* <l1> could not be pipelined because it contains */ + /* a prefetch operation */ +CCM_NOPIPE_EXIT_2, /* <l1> could not be pipelined because it contains */ + /* an exit operation */ +CCM_NOPIPE_REG_2, /* <l1> could not be pipelined because it contains */ + /* instructions that set the %gsr or %fsr register */ +CCM_NOPIPE_UNS_2, /* <l1> could not be pipelined because it has an */ + /* unsigned loop counter */ +CCM_NOPIPE_UNSUIT_2, /* <l1> is unsuitable for pipelining */ +CCM_NOPIPE_INTRINSIC_2, /* <l1> could not be pipelined because it contains */ + /* a call to intrinsic <p2> */ +CCM_NOPIPE_BIG_2, /* <l1> could not be pipelined as it is too big */ +CCM_NOPIPE_INVINTPR_2, /* <l1> could not be pipelined as it contains too */ + /* many loop invariant integers = <i2> */ +CCM_NOPIPE_INVFLTPR_2, /* <l1> could not be pipelined as it contains too */ + /* many loop invariant floats = <i2> */ +CCM_NOPIPE_INVDBLPR_2, /* <l1> could not be pipelined as it contains too */ + /* many loop invariant doubles = <i2> */ +CCM_PIPE_SCHEDAFIPR_2, /* <l1> was adversely affected by high */ + /* integer register pressure = <i2> */ +CCM_PIPE_SCHEDAFDPR_2, /* <l1> was adversely affected by high */ + /* double register pressure = <i2> */ +CCM_PIPE_SCHEDAFFPR_2, /* <l1> was adversely affected by high */ + /* float register pressure = <i2> */ +CCM_NOPIPE_INTPR_2, /* <l1> could not be pipelined due to high */ + /* integer register pressure = <i2> */ +CCM_NOPIPE_DBLPR_2, /* <l1> could not be pipelined due to high */ + /* double register pressure = <i2> */ +CCM_NOPIPE_FLTPR_2, /* <l1> could not be pipelined due to high */ + /* float register pressure = <i2> */ + + /* Group: Inlining Messages */ +CCM_INLINE=0x04000, /* Function <p1> inlined from source file <s2> into */ + /* the code for the following line */ +CCM_INLINE2, /* Function <p1> inlined from source file <s2> into */ + /* inline copy of function <p3> */ +CCM_INLINE_TMPLT, /* Function <p1> inlined from template file <s2> */ + /* into the code for the following line */ +CCM_INLINE_TMPLT2, /* Function <p1> inlined from template file <s2> */ + /* into inline copy of function <p3> */ +CCM_INLINE_OUT_COPY, /* Out-of-line copy of inlined function <p1> from */ + /* source file <s2> generated */ +CCM_NINLINE_REC, /* Recursive function <p1> inlined only up to */ + /* depth <i2> */ +CCM_NINLINE_NEST, /* Function <p1> not inlined because inlining is */ + /* already nested too deeply */ +CCM_NINLINE_CMPLX, /* Function <p1> not inlined because it contains */ + /* too many operations */ +CCM_NINLINE_FB, /* Function <p1> not inlined because the */ + /* profile-feedback execution count is too low */ +CCM_NINLINE_PAR, /* Function <p1> not inlined because it contains */ + /* explicit parallel pragmas */ +CCM_NINLINE_OPT, /* Function <p1> not inlined because it is */ + /* compiled with optimization level <= 2 */ +CCM_NINLINE_USR, /* Function <p1> not inlined because either command */ + /* line option or source code pragma prohibited it, */ + /* or it's not safe to inline it */ +CCM_NINLINE_AUTO, /* Function <p1> not inlined because doing so */ + /* would make automatic storage for <p2> too large */ +CCM_NINLINE_CALLS, /* Function <p1> not inlined because it contains */ + /* too many calls */ +CCM_NINLINE_ACTUAL, /* Function <p1> not inlined because it has more */ + /* actual parameters than formal parameters */ +CCM_NINLINE_FORMAL, /* Function <p1> not inlined because it has more */ + /* formal parameters than actual parameters */ +CCM_NINLINE_TYPE, /* Function <p1> not inlined because formal */ + /* argument type does not match actual type */ +CCM_NINLINE_ATYPE, /* Function <p1> not inlined because array formal */ + /* argument does not match reshaped array actual */ + /* argument type */ +CCM_NINLINE_RETTYPE, /* Function <p1> not inlined because return type */ + /* does not match */ +CCM_NINLINE_EXCPT, /* Function <p1> not inlined because it */ + /* guarded by an exception handler */ +CCM_NINLINE_UNSAFE, /* Function <p1> not inlined because it might be */ + /* unsafe (call alloca(), etc) */ +CCM_NINLINE_ALIAS, /* Function <p1> not inlined because inlining it */ + /* will make the alias analysis in the calling */ + /* function more conservative */ +CCM_NINLINE_FEMARK, /* Function <p1> not inlined because it contains */ + /* setjmp/longjmp, or indirect goto, etc */ +CCM_NINLINE_RAREX, /* Function <p1> not inlined because it is known */ + /* to be rarely executed */ +CCM_CLONING, /* Function <p1> from source file <s2> cloned, */ + /* creating cloned function <p3>; constant */ + /* parameters propagated to clone */ +CCM_INLINE_B, /* Function <p1> inlined from source file <s2> into */ + /* the code for the following line. <i3> loops */ + /* inlined */ +CCM_INLINE2_B, /* Function <p1> inlined from source file <s2> into */ + /* inline copy of function <p3>. <i4> loops inlined */ +CCM_INLINE_LOOP, /* Loop in function <p1>, line <i2> has */ + /* tag <l3> */ +CCM_NINLINE_MULTIENTRY, /* Function <p1> not inlined because it */ + /* contains an ENTRY statement */ +CCM_NINLINE_VARARGS, /* Function <p1> not inlined because variable */ + /* argument routines cannot be inlined */ +CCM_NINLINE_UNSEEN_BODY, /* Function <p1> not inlined because the compiler */ + /* has not seen the body of the function. Use */ + /* -xcrossfile or -xipo in order to inline it */ +CCM_NINLINE_UPLEVEL, /* Function <p1> not inlined because it is a */ + /* nested routine containing references to */ + /* variables defined in an outer function */ +CCM_NINLINE_CMDLINE, /* Function <p1> not inlined because either */ + /* -xinline or source code pragma prohibited it */ +CCM_NINLINE_CALL_CMPLX, /* Call to <p1> not inlined because of the */ + /* complexity of the calling routine */ +CCM_NINLINE_LANG_MISMATCH, /* Call to <p1> not inlined because it is in */ + /* a different language */ +CCM_NINLINE_RTN_WEAK, /* Function <p1> not inlined because it */ + /* is marked weak */ +CCM_NINLINE_CALL_WEAKFILE, /* Call to <p1> not inlined because it is */ + /* in a different file and it contains a */ + /* call to a weak routine */ +CCM_NINLINE_CALL_TRYCATCH, /* Call to <p1> not inlined because it is */ + /* in a different file and contains an */ + /* explicit try/catch */ +CCM_NINLINE_CALL_REGP, /* Call to <p1> not inlined because it would */ + /* cause excessive register pressure */ +CCM_NINLINE_RTN_REGP, /* Function <p1> not inlined because it would */ + /* cause excessive register pressure */ +CCM_NINLINE_CALL_XPENSV, /* Call to <p1> not inlined because analysis */ + /* exceeds the compilation time limit */ +CCM_NINLINE_READONLYIR, /* Function <p1> not inlined because it is in a file */ + /* specified as read-only by -xipo_archive=readonly */ + /* and it contains calls to static functions */ +CCM_NINLINE_CALL_THUNK, /* Call to <p1> not inlined because it is in a */ + /* compiler-generated function that does not */ + /* permit inlining */ +CCM_NINLINE_CALL_XTARGETS, /* Indirect callsite has too many targets; */ + /* callsite marked do not inline */ +CCM_NINLINE_SELFTAIL_RECURSIVE, /* Function <p1> not inlined because */ + /* of a recursive tail-call to itself */ +CCM_NINLINE_PRAGMA, /* Function <p1> not inlined because it contains */ + /* explicit parallel or alias pragmas */ +CCM_NINLINE_CMPLX2, /* Function <p1> not inlined because it contains too */ + /* many operations. Increase max_inst_hard in order */ + /* to inline it: -xinline_param=max_inst_hard:n */ +CCM_NINLINE_RARE, /* Function <p1> not inlined because the call */ + /* is rarely executed */ +CCM_NINLINE_PAR2, /* Function <p1> not inlined because it is called */ + /* within a region guarded by an explicit */ + /* parallel pragmas */ +CCM_NINLINE_G_LIMIT, /* Function <p1> not inlined because it would exceed */ + /* the permitted global code size growth limit. Try */ + /* to increase max_growth in order to inline it: */ + /* -xinline_param=max_growth:n */ +CCM_NINLINE_L_LIMIT, /* Function <p1> not inlined because it would exceed */ + /* the maximum function size growth limit. Increase */ + /* max_function_inst in order to inline it: */ + /* -xinline_param=max_function_inst:n */ +CCM_NINLINE_REC2, /* Recursive function <p1> is inlined only up to */ + /* <i2> levels and up to <i3> size. Increase */ + /* max_recursive_deptha or max_recursive_inst in */ + /* order to inline it: */ + /* -xinline_param=max_recursive_depth:n, */ + /* -xinline_param=max_recursive_inst:n */ +CCM_NINLINE_FB2, /* Function <p1> not inlined because the */ + /* profile-feedback execution count is too */ + /* low. Decrease min_counter in order to inline it: */ + /* -xinline_param:min_counter:n */ +CCM_NINLINE_CS_CMPLX, /* Function <p1> not inlined because called */ + /* function's size is too big. Increase */ + /* max_inst_soft in order to inline it: */ + /* -xinline_param=max_inst_soft:n */ +CCM_NINLINE_R_EXCPT, /* Function <p1> not inlined because it contains */ + /* an exception handler */ +CCM_NINLINE_ASM, /* Function <p1> not inlined because */ + /* it contains asm statements */ +CCM_NINLINE_R_READONLYIR, /* Function <p1> not inlined because it is in a file */ + /* specified as read-only by -xipo_archive=readonly */ + /* and it is a static function */ +CCM_NINLINE_C_READONLYIR, /* Call to <p1> not inlined because the calling */ + /* function is in a file specified as read-only */ + /* by -xipo_archive=readonly */ +CCM_NINLINE_NEVERRETURN, /* Function <p1> not inlined because it */ + /* never returns */ + + /* Group: Messages Concerning Memory Operations */ + /* Notes: */ + /* a. In all of these, <s1> is a string that is something like */ + /* "A(i+5*k)" or "structure.field", giving the high-level */ + /* construct that is being loaded or stored. */ + /* */ + /* b. In all of these, <x2> refers to an instruction offset, */ + /* expressed as a 32-bit signed integer. It is assumed */ + /* that any prefetches will be within this range of the */ + /* load/store they are prefetching for. */ +CCM_MPREFETCH=0x08000, /* Prefetch of <s1> inserted */ + /* [This message has a lineno for the source, */ + /* but no instaddr for the disassembly.] */ +CCM_MPREFETCH_LD, /* Prefetch of <s1> inserted for load at <x2> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MPREFETCH_ST, /* Prefetch of <s1> inserted for store at <x2> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MPREFETCH_FB, /* Prefetch of <s1> inserted based on feedback data */ + /* [This message has a lineno for the source, */ + /* but no instaddr for the disassembly.] */ +CCM_MPREFETCH_FB_LD, /* Prefetch of <s1> inserted for load at <x2> based */ + /* on feedback data */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MPREFETCH_FB_ST, /* Prefetch of <s1> inserted for store at <x2> based */ + /* on feedback data */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MLOAD, /* Load below refers to <s1> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MSTORE, /* Store below refers to <s1> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MLOAD_P, /* Load below refers to <s1>, and was prefetched */ + /* at <x2> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MSTORE_P, /* Store below refers to <s1>, and was prefetched */ + /* at <x2> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ + + /* Group: Front-end messages [all compilers] */ + /* Group: F95 Front-end Messages */ +CCM_COPYIN=0x10000, /* Parameter <i1> caused a copyin in the following */ + /* call */ +CCM_COPYOUT, /* Parameter <i1> caused a copyout in the following */ + /* call */ +CCM_COPYINOUT, /* Parameter <i1> caused both a copyin and copyout */ + /* in the following call */ +CCM_PADDING, /* Padding of <i1> bytes inserted before */ + /* array <v2> */ +CCM_PADCOMMON, /* Padding of <i1> bytes inserted before */ + /* array <v2> in common block <v3> */ +CCM_ALIGN_EQ, /* Variable/array <v1> can not be double-aligned, */ + /* because it is equivalenced */ +CCM_ALIGN_PERF, /* Alignment of variables in common block may cause */ + /* performance degradation */ +CCM_ALIGN_STRUCT, /* Alignment of component <s1> in numeric sequence */ + /* structure <s2> may cause performance degradation */ +CCM_TMP_COPY, /* Argument <v1> copied to a temporary */ +CCM_TMP_COPYM, /* Argument <v1> might be copied to a temporary; */ + /* runtime decision made */ +CCM_PROC_MISMATCH, /* Argument <i1> to subprogram <p2> differs from */ + /* reference on line <i3> */ +CCM_PROC_MISMATCH2, /* Scalar argument <i1> to subprogram <p2> is */ + /* referred to as an array on line <i3> */ +CCM_PROC_MISMATCH3, /* Return type/rank from subprogram <p1> differs */ + /* from return on line <i2> */ +CCM_DO_EXPR, /* DO statement bounds lead to no executions of the */ + /* loop */ +CCM_AUTO_BND, /* The bounds for automatic variable <v1> are not */ + /* available at all entry points; zero-length */ + /* variable might be allocated */ +CCM_LIT_PAD, /* The character string literal <s1> padded */ + /* to the length specified for the dummy argument */ +CCM_ARRAY_LOOP, /* Array statement below generated a loop */ +CCM_ARRAY_LOOPNEST, /* Array statement below generated <i1> nested loops */ +CCM_ALIGN_PERF2, /* Alignment of variable <v1> in common block <v2> */ + /* may cause a performance degradation */ +CCM_ALIGN_PERF3, /* Alignment of variable <v1> in blank common may */ + /* cause a performance degradation */ +CCM_IO_LOOP_ARRAY, /* I/O implied do item below generated an array */ + /* section */ + + /* Group: C++ Front-end Messages */ +CCM_TMPCONST, /* Implicit invocation of class <s1> constructor for */ + /* temporary */ +CCM_TMPDEST, /* Implicit invocation of class <s1> destructor for */ + /* temporary */ +CCM_DBL_CONST, /* Double constant <s1> used in float expression */ +CCM_MINLINE, /* Function <p1> inlined from source file <s2> by */ + /* front-end */ + /* [This refers to front-end inlining, */ + /* not the backend inlining above.] */ +CCM_MINLINE2, /* Function <p1> from source file <s2> inlined into */ + /* inline copy of method <p3> by front-end */ + /* [This refers to front-end inlining, */ + /* not the backend inlining above.] */ +CCM_MINLINE3, /* Function <p1> not inlined because it uses keyword */ + /* <s2> */ +CCM_MINLINE4, /* Function <p1> not inlined because it is too */ + /* complex */ +CCM_TMP_COPYOUT, /* Argument <v1> copied from a temporary */ +CCM_TMP_COPYOUTM, /* Argument <v1> might be copied from a temporary; */ + /* runtime decision made */ +CCM_TMP_COPYINOUT, /* Argument <v1> copied in and out of a temporary */ +CCM_TMP_COPYINOUTM, /* Argument <v1> might be copied in and out of */ + /* a temporary; runtime decision made */ + + /* Group: C Front-end Messages */ + /* Group: NJC Front-end Messages */ + /* Group: Updated F95 Front-end Messages */ +CCM_ARRAY_LOOP_2, /* Array statement below generated loop <l1> */ +CCM_ARRAY_LOOPNEST_2, /* Array statement below generated <i1> nested */ + /* loops: <l2>, <l3>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_IO_LOOP_ARRAY_2, /* I/O implied do item below generated an array */ + /* section: <l1> */ +CCM_USER_LOOP, /* Source loop below has tag <l1> */ +CCM_FOUND_LOOP, /* Discovered loop below has tag <l1> */ +CCM_MFUNCTION_LOOP, /* Copy in M-function of loop below has tag <l1> */ + + /* Group: Code-generator Messages */ +CCM_FSIMPLE=0x20000, /* Transformations for fsimple=<i1> applied */ +CCM_STACK, /* Function <p1> requires <i2> Mbytes of stack */ + /* storage */ +CCM_TAILRECUR, /* Recursive tail call in <p1> optimized to jump to */ + /* entry point */ +CCM_TAILCALL, /* Call to function <p1> was tail-call optimized */ +CCM_NI_EXIT_OR_PSEUDO, /* Template could not be early inlined because it */ + /* contains the pseudo instruction <s1> */ +CCM_NI_BAD_UNARY_OPC, /* Template could not be early inlined because it */ + /* contains the instruction opcode <s1> */ +CCM_NI_INT_LDD_ON_V9, /* Template could not be early inlined because it */ + /* contains integer ldd instructions, which are */ + /* deprecated in the v9 architecture */ +CCM_NI_LATE_INL_OPC, /* Template could not be early inlined because it */ + /* contains the instruction opcode <s1> */ +CCM_NI_BAD_IMM_OP, /* Template could not be early inlined because the */ + /* relocation or immediate operand <s1> is not well */ + /* understood by the optimizer */ +CCM_NI_BAD_STATELEAF, /* Template could not be early inlined because it */ + /* references the state register <s1> */ +CCM_NI_BAD_ASR_19, /* Template could not be early inlined because */ + /* %asr19 is not supported in pre v8plus code */ +CCM_NI_BAD_FSR_USE, /* Template could not be early inlined because */ + /* references to %fsr can only be optimized when the */ + /* -iaopts flag is used */ +CCM_NI_BAD_REGISTER, /* Template could not be early inlined because it */ + /* references the register <s1> */ +CCM_NI_NO_RET_VAL, /* Template could not be early inlined because it */ + /* does not return the value declared */ +CCM_NI_DELAY, /* Template could not be early inlined because it */ + /* contains a non nop delay slot */ +CCM_NI_SCALL, /* Template could not be early inlined because it */ + /* calls a function which returns a structure */ +CCM_CASE_POSITION, /* Case block below was placed at position <i1> */ + /* based on execution frequency */ +CCM_CALL_WITH_CODE, /* Call to <p1> replaced with inline code. <i2> */ + /* loops created: <l3>, <l4>, ... */ +CCM_NI_BAD_SP_ADDR, /* Template could not be early inlined because it */ + /* contains a %sp+reg address */ +CCM_NI_BAD_SP_USAGE, /* Template could not be early inlined because it */ + /* uses/defines the stack pointer in a non-load/store instruction */ +CCM_NI_MIXED_REG_TYPES, /* Template could not be early inlined because it */ + /* contains register <s1> used as both x-register and register pair */ +CCM_LAST +} COMPMSG_ID; +/* + * The Message Structure + * Each message is a fixed-length structure as follows: + */ +typedef struct +{ + int64_t instaddr; /* the PC offset, relative to the .o .text section */ + int32_t lineno; /* the source line to which it refers */ + COMPMSG_ID msg_type; /* the specific message index */ + int32_t nparam; /* number of parameters to this message */ + int32_t param_index; /* the index of the first parameter */ +} compmsg; + +#if defined(__cplusplus) +extern "C" +{ +#endif + /* + * Initializes the data structures, converts the source name to a string, + * and fills in srcname and version in the header + */ + void compcom_p_open (char *srcname, int32_t version); + + /* + * Finds or enters the string s into the string table, and returns the index + * of the string + */ + int32_t compcom_p_string (char *s); + + /* + * Enter the single message. Any string parameters should have been converted + * to int32's by calling compcom_p_string() + */ + void compcom_p_putmsg (int32_t show_bits, int64_t pcoffset, int32_t lineno, + COMPMSG_ID m, int32_t nparams); + + /* + * Whatever is needed to close the section and write it out to the .o + */ + void compcom_p_finalize (); + +#if defined(__cplusplus) +} +#endif + +#endif /* _COMP_COM_H */ diff --git a/gprofng/src/count.cc b/gprofng/src/count.cc new file mode 100644 index 0000000..6005a49 --- /dev/null +++ b/gprofng/src/count.cc @@ -0,0 +1,237 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <strings.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <i18n.h> +#include <Elf.h> +#include <collctrl.h> +#include <StringBuilder.h> +#include "collect.h" + +/* get_count_data -- format exec of bit to do the real work */ +void +collect::get_count_data () +{ + char command[8192]; + char *s; + struct stat statbuf; + + // reserve space for original args, plus 30 arguments to bit + nargs = origargc + 30; + char **narglist = (char **) calloc (nargs, sizeof (char *)); + arglist = narglist; + + // construct the command for bit + snprintf (command, sizeof (command), NTXT ("%s"), run_dir); + s = strstr_r (command, NTXT ("/bin")); + if (s != NULL) + { + // build command line for launching it + snprintf (s, sizeof (command) - (s - command), NTXT ("/lib/compilers/bit")); + if (stat (command, &statbuf) == -1) + { + // if bit command does not exist there + char *first_look = strdup (command); + snprintf (command, sizeof (command), NTXT ("%s"), run_dir); + s = strstr (command, NTXT ("/bin")); + snprintf (s, sizeof (command) - (s - command), NTXT ("/prod/bin/bit")); + if (stat (command, &statbuf) == -1) + { + // if bit command does not exist + dbe_write (2, GTXT ("bit is not installed as `%s' or `%s'\nNo experiment is possible\n"), first_look, command); + exit (2); + } + free (first_look); + } + *arglist++ = strdup (command); + } + else + { + dbe_write (2, GTXT ("collect can't find install bin directory\n")); + exit (1); + } + + // Tell it to collect data + *arglist++ = NTXT ("collect"); + + // add the flag for real-data vs. static data + switch (cc->get_count ()) + { + case -1: + *arglist++ = NTXT ("-i"); + *arglist++ = NTXT ("static"); + *arglist++ = NTXT ("-M"); + break; + case 1: + *arglist++ = NTXT ("-M"); + *arglist++ = NTXT ("-u"); + break; + default: + abort (); + } + + // tell bit to produce an experiment + *arglist++ = NTXT ("-e"); + + // now copy an edited list of collect options to the arglist + char **oargv = origargv; + + // skip the "collect" + oargv++; + int argc = 1; + while (argc != targ_index) + { + char *p = *oargv; + switch (p[1]) + { + // pass these arguments along, with parameter + case 'o': + case 'd': + case 'g': + case 'A': + case 'C': + case 'O': + case 'N': + *arglist++ = *oargv++; + *arglist++ = *oargv++; + argc = argc + 2; + break; + case 'I': + *arglist++ = *oargv++; // set the -I flag + *arglist++ = *oargv; // and the directory name + *arglist++ = NTXT ("-d"); // and the -d flag + *arglist++ = *oargv++; // to the same directory name + argc = argc + 2; + break; + case 'n': + case 'v': + // pass these arguments along as is + *arglist++ = *oargv++; + argc = argc + 1; + break; + case 'x': + // skip one argument + oargv++; + argc++; + break; + case 'c': + case 'L': + case 'y': + case 'l': + case 'F': + case 'j': + case 'J': + case 'p': + case 's': + case 'h': + case 'S': + case 'm': + case 'M': + case 'H': + case 'r': + case 'i': + // skip two arguments + oargv++; + oargv++; + argc = argc + 2; + break; + case 'R': + case 'Z': + default: + // these should never get this far + dbe_write (2, GTXT ("unexpected argument %s\n"), p); + abort (); + } + } + + // now copy the target and its arguments + if (access (prog_name, X_OK) != 0) // not found + *arglist++ = *oargv++; + else + { + oargv++; + *arglist++ = prog_name; + } + while (*oargv != NULL) + *arglist++ = *oargv++; + + /* now we have the full argument list composed; if verbose, print it */ + if ((verbose == 1) || (disabled)) + { + /* describe the experiment */ + char *ccret = cc->show (0); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + } + ccret = cc->show_expt (); + if (ccret != NULL) + { + /* write this to stdout */ + writeStr (1, ccret); + free (ccret); + } + /* print the arguments to bit */ + arglist = narglist; + StringBuilder sb; + sb.append (NTXT ("Exec argv[] = ")); + for (int ret = 0; ret < nargs; ret++) + { + if (narglist[ret] == NULL) + break; + if (ret > 0) + sb.append (NTXT (" ")); + sb.append (narglist[ret]); + } + sb.append (NTXT ("\n\n")); + write (2, sb.toString (), sb.length ()); + } + + /* check for dry run */ + if (disabled) + exit (0); + + /* ensure original outputs restored for target */ + reset_output (); + + /* now exec the bit to instrument and run the target ... */ + // (void) execve( *narglist, narglist, origenvp); + (void) execvp (*narglist, narglist); + + /* exec failed; no experiment to delete */ + /* restore output for collector */ + set_output (); + char *em = strerror (errno); + if (em == NULL) + dbe_write (2, GTXT ("execve of %s failed: errno = %d\n"), narglist[0], errno); + else + dbe_write (2, GTXT ("execve of %s failed: %s\n"), narglist[0], em); + exit (1); +} diff --git a/gprofng/src/data_pckts.h b/gprofng/src/data_pckts.h new file mode 100644 index 0000000..93d0307 --- /dev/null +++ b/gprofng/src/data_pckts.h @@ -0,0 +1,595 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DATA_PCKTS_H +#define _DATA_PCKTS_H + +/* + * This file contains structure definitions for the binary file formats + * used in the experiment. It is implemented as C header file so that + * it can be processed by both ANSI-C and C++. + */ + +#include <pthread.h> +#include <stdint.h> + +#include "gp-defs.h" +#include "gp-time.h" + +#if WSIZE(64) +typedef uint64_t Vaddr_type; /* process address for 64 bit apps */ +typedef uint64_t Size_type; /* size_t for 64 bit apps */ +#else +typedef uint32_t Vaddr_type; /* process address */ +typedef uint32_t Size_type; /* size_t for 32 bit apps */ +#endif + +/* marker to indicate dump of O7 register on stack (support for leaf routines) */ +#define SP_LEAF_CHECK_MARKER ((uint64_t)(-1)) + +/* marker to indicate truncated stack */ +#define SP_TRUNC_STACK_MARKER ((uint64_t)(-2)) + +/* marker to indicate failed stack unwind */ +#define SP_FAILED_UNWIND_MARKER ((uint64_t)(-3)) + +#define PROFILE_BUFFER_CHUNK 16384 + +typedef enum +{ + MASTER_SMPL = 0, + PROGRAM_SMPL, + PERIOD_SMPL, + MANUAL_SMPL +} Smpl_type; + +typedef enum +{ /* values for "profpckt kind" stored in log.xml */ + EMPTY_PCKT = 0, + PROF_PCKT, + SYNC_PCKT, + HW_PCKT, + XHWC_PCKT, + HEAP_PCKT, + MPI_PCKT, + MHWC_PCKT, + OPROF_PCKT, + OMP_PCKT, + RACE_PCKT, + FRAME_PCKT, + OMP2_PCKT, + DEADLOCK_PCKT, + OMP3_PCKT, + OMP4_PCKT, + OMP5_PCKT, + UID_PCKT, + FRAME2_PCKT, + IOTRACE_PCKT, + LAST_PCKT, /* last data packet type */ + CLOSED_PCKT = 65535 /* -1, this packet closes a block */ +} Pckt_type; + +typedef enum +{ + EMPTY_INFO = 0, + STACK_INFO, + JAVA_INFO, + OMP_INFO, + MPI_INFO, + OMP2_INFO, + LAST_INFO /* keep this one last */ +} Info_type; + +#define COMPRESSED_INFO 0x80000000 + +#define JAVA_PCKT 0x80 +#define OMPS_PCKT 0x40 /* packet contains OMP state info */ +#define PCKT_TYPE(x) ((x) & 0x1f) + +typedef struct CommonHead_packet +{ + unsigned int tsize : 16; + unsigned int type : 16; +} CommonHead_packet; + +// All collector modules record their packets as extensions of CM_Packet +typedef struct CM_Packet +{ + unsigned int tsize : 16; + unsigned int type : 16; +} CM_Packet; + +typedef struct Common_packet +{ + unsigned int tsize : 16; /* packet size */ + unsigned int type : 16; + pthread_t lwp_id; + pthread_t thr_id; + uint32_t cpu_id; + hrtime_t tstamp; + uint64_t frinfo; +} Common_packet; + +/* Definition of values stored in the experiment PROP_MSTATE field */ +/* They include: + * LWP microstates (copied from msacct.h). Also see PrUsage class. + * Linux's CPU time + * er_kernel time + */ +/* Can be used with LMS_STATE_STRINGS (below) */ +#define LMS_USER 0 /* running in user mode */ +#define LMS_SYSTEM 1 /* running in sys call or page fault */ +#define LMS_TRAP 2 /* running in other trap */ +#define LMS_TFAULT 3 /* asleep in user text page fault */ +#define LMS_DFAULT 4 /* asleep in user data page fault */ +#define LMS_KFAULT 5 /* asleep in kernel page fault */ +#define LMS_USER_LOCK 6 /* asleep waiting for user-mode lock */ +#define LMS_SLEEP 7 /* asleep for any other reason */ +#define LMS_WAIT_CPU 8 /* waiting for CPU (latency) */ +#define LMS_STOPPED 9 /* stopped (/proc, jobcontrol, or lwp_stop) */ +#define LMS_LINUX_CPU 10 /* LINUX timer_create(CLOCK_THREAD_CPUTIME_ID) */ +#define LMS_KERNEL_CPU 11 /* LINUX timer_create(CLOCK_THREAD_CPUTIME_ID) */ +#define LMS_NUM_STATES 12 /* total number of above states */ +#define LMS_NUM_SOLARIS_MSTATES 10 /* LMS microstates thru LMS_STOPPED */ + +// Magic value stored in experiments that identifies which LMS states are valid +#define LMS_MAGIC_ID_SOLARIS 10 // Solaris: LMS_USER thru LMS_STOPPED +#define LMS_MAGIC_ID_ERKERNEL_USER 2 // er_kernel user: LMS_USER, LMS_SYSTEM +#define LMS_MAGIC_ID_ERKERNEL_KERNEL 3 // er_kernel kernel: LMS_KERNEL_CPU +#define LMS_MAGIC_ID_LINUX 1 // Linux: LMS_LINUX_CPU + +#define LMS_STATE_STRINGS \ +{ \ + NTXT("USER"), /* LMS_USER */ \ + NTXT("SYSTEM"), /* LMS_SYSTEM */ \ + NTXT("TRAP"), /* LMS_TRAP */ \ + NTXT("TFAULT"), /* LMS_TFAULT */ \ + NTXT("DFAULT"), /* LMS_DFAULT */ \ + NTXT("KFAULT"), /* LMS_KFAULT */ \ + NTXT("USER_LOCK"), /* LMS_USER_LOCK */ \ + NTXT("SLEEP"), /* LMS_SLEEP */ \ + NTXT("WAIT_CPU"), /* LMS_WAIT_CPU */ \ + NTXT("STOPPED"), /* LMS_STOPPED */ \ + NTXT("LINUX_CPU"), /* LMS_LINUX_CPU */ \ + NTXT("KERNEL_CPU") /* LMS_KERNEL_CPU */ \ +} +#define LMS_STATE_USTRINGS \ +{ \ + GTXT("User CPU"), /* LMS_USER */ \ + GTXT("System CPU"), /* LMS_SYSTEM */ \ + GTXT("Trap CPU"), /* LMS_TRAP */ \ + GTXT("Text Page Fault"), /* LMS_TFAULT */ \ + GTXT("Data Page Fault"), /* LMS_DFAULT */ \ + GTXT("Kernel Page Fault"), /* LMS_KFAULT */ \ + GTXT("User Lock"), /* LMS_USER_LOCK */ \ + GTXT("Sleep"), /* LMS_SLEEP */ \ + GTXT("Wait CPU"), /* LMS_WAIT_CPU */ \ + GTXT("Stopped"), /* LMS_STOPPED */ \ + GTXT("User+System CPU"), /* LMS_LINUX_CPU */ \ + GTXT("Kernel CPU") /* LMS_KERNEL_CPU */ \ +} + +typedef enum +{ + MALLOC_TRACE = 0, + FREE_TRACE, + REALLOC_TRACE, + MMAP_TRACE, + MUNMAP_TRACE, + HEAPTYPE_LAST +} Heap_type; + +#define HEAPTYPE_STATE_STRINGS \ +{ \ + NTXT("MALLOC"), \ + NTXT("FREE"), \ + NTXT("REALLOC"), \ + NTXT("MMAP"), \ + NTXT("MUNMAP") \ +} +#define HEAPTYPE_STATE_USTRINGS \ +{ \ + GTXT("malloc"), \ + GTXT("free"), \ + GTXT("realloc"), \ + GTXT("mmap"), \ + GTXT("munmap") \ +} + +typedef enum +{ + ZFS_TYPE = 0, + NFS_TYPE, + UFS_TYPE, + UDFS_TYPE, + LOFS_TYPE, + VXFS_TYPE, + TMPFS_TYPE, + PCFS_TYPE, + HSFS_TYPE, + PROCFS_TYPE, + FIFOFS_TYPE, + SWAPFS_TYPE, + CACHEFS_TYPE, + AUTOFS_TYPE, + SPECFS_TYPE, + SOCKFS_TYPE, + FDFS_TYPE, + MNTFS_TYPE, + NAMEFS_TYPE, + OBJFS_TYPE, + SHAREFS_TYPE, + EXT2FS_TYPE, + EXT3FS_TYPE, + EXT4FS_TYPE, + UNKNOWNFS_TYPE, + FSTYPE_LAST +} FileSystem_type; + +typedef enum +{ + READ_TRACE = 0, + WRITE_TRACE, + OPEN_TRACE, + CLOSE_TRACE, + OTHERIO_TRACE, + READ_TRACE_ERROR, + WRITE_TRACE_ERROR, + OPEN_TRACE_ERROR, + CLOSE_TRACE_ERROR, + OTHERIO_TRACE_ERROR, + IOTRACETYPE_LAST +} IOTrace_type; + +#define IOTRACETYPE_STATE_STRINGS \ +{ \ + NTXT("READ"), \ + NTXT("WRITE"), \ + NTXT("OPEN"), \ + NTXT("CLOSE"), \ + NTXT("OTHERIO"), \ + NTXT("READERROR"), \ + NTXT("WRITEERROR"), \ + NTXT("OPENERROR"), \ + NTXT("CLOSEERROR"), \ + NTXT("OTHERIOERROR") \ +} +#define IOTRACETYPE_STATE_USTRINGS \ +{ \ + GTXT("Read"), \ + GTXT("Write"), \ + GTXT("Open"), \ + GTXT("Close"), \ + GTXT("Other I/O"), \ + GTXT("Read error"), \ + GTXT("Write error"), \ + GTXT("Open error"), \ + GTXT("Close error"), \ + GTXT("Other I/O error") \ +} + +// the type of racing memory access with redundance flag +typedef enum +{ + WRITE_RACE = 0, + WRITE_RACE_RED, + READ_RACE, + READ_RACE_RED, + RACETYPE_LAST +} Race_type; + +typedef struct Frame_packet +{ + unsigned int tsize : 16; /* packet size */ + unsigned int type : 16; + uint32_t hsize; /* header size */ + uint64_t uid; /* unique id (experiment wide) */ +} Frame_packet; + +typedef struct Uid_packet +{ + unsigned int tsize : 16; /* packet size */ + unsigned int type : 16; + uint32_t flags; + uint64_t uid; /* unique id (experiment wide) */ +} Uid_packet; + +/* + * Components of the variable part of Frame_packet + */ +typedef struct Common_info +{ + unsigned int hsize; /* size of this info */ + unsigned int kind; + uint64_t uid; /* unique id of this info if any */ +} Common_info; + +typedef struct Stack_info +{ /* Native call stack */ + unsigned int hsize; + unsigned int kind; + uint64_t uid; +} Stack_info; + +typedef struct Java_info +{ /* Java call stack */ + unsigned int hsize; + unsigned int kind; + uint64_t uid; +} Java_info; + +typedef struct OMP_info +{ /* OMP thread state */ + unsigned int hsize; + unsigned int kind; + uint32_t omp_state; + uint32_t pad; +} OMP_info; + +typedef struct OMP2_info +{ /* OpenMP user call stack */ + unsigned int hsize; + unsigned int kind; + uint32_t omp_state; + uint32_t pad; + uint64_t uid; +} OMP2_info; + +/* OMP thread states as recorded in the experiment */ +/* Definition of values stored in the experiment PROP_OMPSTATE field */ + +/* Can be used with OMP_THR_STATE_STRINGS (below) */ +typedef enum +{ + OMP_NO_STATE = 0, /* Not initialized */ + OMP_OVHD_STATE, /* Overhead */ + OMP_WORK_STATE, /* Useful work, excluding reduction, master, single, critical */ + OMP_IBAR_STATE, /* In an implicit barrier */ + OMP_EBAR_STATE, /* In an explicit barrier */ + OMP_IDLE_STATE, /* Slave waiting */ + OMP_SERL_STATE, /* User OMPead not in any OMP parallel region */ + OMP_RDUC_STATE, /* Reduction */ + OMP_LKWT_STATE, /* Waiting for lock */ + OMP_CTWT_STATE, /* Waiting to enter critical section */ + OMP_ODWT_STATE, /* Waiting to execute an ordered section */ + OMP_ATWT_STATE, /* Wait for atomic */ + OMP_TSKWT_STATE, /* Task wait */ + OMP_LAST_STATE +} OMP_THR_STATE; +#define OMP_THR_STATE_STRINGS \ +{ \ + NTXT("NO"), /* OMP_NO_STATE */ \ + NTXT("OVHD"), /* OMP_OVHD_STATE */ \ + NTXT("WORK"), /* OMP_WORK_STATE */ \ + NTXT("IBAR"), /* OMP_IBAR_STATE */ \ + NTXT("EBAR"), /* OMP_EBAR_STATE */ \ + NTXT("IDLE"), /* OMP_IDLE_STATE */ \ + NTXT("SERL"), /* OMP_SERL_STATE */ \ + NTXT("RDUC"), /* OMP_RDUC_STATE */ \ + NTXT("LKWT"), /* OMP_LKWT_STATE */ \ + NTXT("CTWT"), /* OMP_CTWT_STATE */ \ + NTXT("ODWT"), /* OMP_ODWT_STATE */ \ + NTXT("ATWT"), /* OMP_ATWT_STATE */ \ + NTXT("TSKWT") /* OMP_TSKWT_STATE */ \ +} +#define OMP_THR_STATE_USTRINGS \ +{ \ + GTXT("None"), /* OMP_NO_STATE */ \ + GTXT("Overhead"), /* OMP_OVHD_STATE */ \ + GTXT("Work"), /* OMP_WORK_STATE */ \ + GTXT("Implicit Barrier"), /* OMP_IBAR_STATE */ \ + GTXT("Explicit Barrier"), /* OMP_EBAR_STATE */ \ + GTXT("Idle"), /* OMP_IDLE_STATE */ \ + GTXT("Serial"), /* OMP_SERL_STATE */ \ + GTXT("Reduction"), /* OMP_RDUC_STATE */ \ + GTXT("Lock Wait"), /* OMP_LKWT_STATE */ \ + GTXT("Critical Section Wait"), /* OMP_CTWT_STATE */ \ + GTXT("Ordered Section Wait"), /* OMP_ODWT_STATE */ \ + GTXT("Atomic Wait"), /* OMP_ATWT_STATE */ \ + GTXT("Task Wait") /* OMP_TSKWT_STATE */ \ +} + +/* sub-packet for MPI state information */ +typedef struct MPI_info +{ /* MPI thread state */ + unsigned int hsize; + unsigned int kind; + uint32_t mpi_state; + uint32_t pad; +} MPI_info; + +/* MPI thread states, as recorded in the experiment */ +typedef enum +{ + MPI_NO_STATE = 0, /* Not initialized */ + MPI_USER, /* Executing user code, not in MPI */ + MPI_PROG, /* Executing in the MPI library (progressing) */ + MPI_WAIT /* Waiting in the MPI library */ +} MPI_THR_STATE; + +/* + * Dyntext file structure + */ +typedef enum +{ + DT_HEADER = 1, + DT_CODE, + DT_LTABLE, + DT_SRCFILE +} DT_type; + +typedef struct DT_common +{ + DT_type type; + unsigned int size; +} DT_common; + +typedef struct DT_header +{ + DT_type type; + unsigned int size; + hrtime_t time; /* time of loading */ + uint64_t vaddr; +} DT_header; + +typedef struct DT_code +{ + DT_type type; + unsigned int size; +} DT_code; + +typedef struct DT_ltable +{ + DT_type type; + unsigned int size; +} DT_ltable; + +typedef struct DT_lineno +{ + unsigned int offset; + unsigned int lineno; +} DT_lineno; + +typedef struct DT_srcfile +{ + DT_type type; + unsigned int size; +} DT_srcfile; + +/* + * Archive file structure + */ +#define ARCH_VERSION 0x100 /* version 1.0 */ + +/* For compatibility with older archives append new types only */ +typedef enum +{ + ARCH_SEGMENT_TYPE = 1, + ARCH_MSG_TYPE, + ARCH_PLT_TYPE, + ARCH_MODULE_TYPE, + ARCH_FUNCTION_TYPE, + ARCH_LDINSTR_TYPE, + ARCH_STINSTR_TYPE, + ARCH_PREFETCH_TYPE, + ARCH_BRTARGET_TYPE, + ARCH_JCLASS_TYPE, + ARCH_JMETHOD_TYPE, + ARCH_JUNLOAD_TYPE, + ARCH_INF_TYPE, + ARCH_JCLASS_LOCATION_TYPE +} ARCH_type; + +#define ARCH_TYPE(x,y) ((ARCH_##x##_TYPE<<8)|y) + +typedef struct +{ + unsigned int type : 16; + unsigned int size : 16; +} ARCH_common; + +/* The maximum value that fits into ARCH_common.size */ +#define ARCH_MAX_SIZE 0xffff + +#define ARCH_SEGMENT ARCH_TYPE(SEGMENT, 0) + +typedef struct +{ + ARCH_common common; + int version; + uint32_t inode; + uint32_t textsz; /* text segment size */ + uint32_t platform; /* sparc, intel, etc. */ +} ARCH_segment; + +#define ARCH_MSG ARCH_TYPE(MSG, 0) + +typedef struct +{ + ARCH_common common; + uint32_t errcode; +} ARCH_message; + +#define ARCH_INF ARCH_TYPE(INF, 0) + +typedef struct +{ + ARCH_common common; +} ARCH_info; + +#define ARCH_MODULE ARCH_TYPE(MODULE, 0) + +typedef struct +{ + ARCH_common common; + unsigned int lang_code; + unsigned int fragmented; +} ARCH_module; + +#define ARCH_FUNCTION ARCH_TYPE(FUNCTION, 0) + +typedef struct +{ + ARCH_common common; + uint32_t offset; + uint32_t size; + uint32_t save_addr; +} ARCH_function; + +#define ARCH_LDINSTR ARCH_TYPE(LDINSTR, 0) +#define ARCH_STINSTR ARCH_TYPE(STINSTR, 0) +#define ARCH_PREFETCH ARCH_TYPE(PREFETCH, 0) +#define ARCH_BRTARGET ARCH_TYPE(BRTARGET, 0) + +typedef struct +{ + ARCH_common common; +} ARCH_aninfo; + +#define ARCH_JCLASS_LOCATION ARCH_TYPE(JCLASS_LOCATION, 3) + +typedef struct +{ + CM_Packet comm; + uint32_t pad; + uint64_t class_id; +} ARCH_jclass_location; + +#define ARCH_JCLASS ARCH_TYPE(JCLASS, 3) + +typedef struct +{ + CM_Packet comm; + uint32_t pad; + uint64_t class_id; + hrtime_t tstamp; +} ARCH_jclass; + +#define ARCH_JMETHOD ARCH_TYPE(JMETHOD, 3) + +typedef struct +{ + CM_Packet comm; + uint32_t pad; + uint64_t class_id; + uint64_t method_id; +} ARCH_jmethod; + +#endif /* _DATA_PCKTS_H */ diff --git a/gprofng/src/dbe_collctrl.cc b/gprofng/src/dbe_collctrl.cc new file mode 100644 index 0000000..9219a8e --- /dev/null +++ b/gprofng/src/dbe_collctrl.cc @@ -0,0 +1,28 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "config.h" +#include <stdio.h> +#include <stdarg.h> + +#include "i18n.h" + +#include "collctrl.cc" diff --git a/gprofng/src/dbe_hwc.h b/gprofng/src/dbe_hwc.h new file mode 100644 index 0000000..74b6838 --- /dev/null +++ b/gprofng/src/dbe_hwc.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef dbe_hwc_h +#define dbe_hwc_h + +#include <stdio.h> +#include <stdarg.h> + +#include "i18n.h" + +#define HWC_TRACELEVEL -1 +#if HWC_TRACELEVEL < 0 +#define TprintfT(x1,...) +#define Tprintf(x1,...) +#else +#define TprintfT(x1,...) if( x1<=HWC_TRACELEVEL ) fprintf(stderr,__VA_ARGS__) +#define Tprintf(x1,...) if( x1<=HWC_TRACELEVEL ) fprintf(stderr,__VA_ARGS__) +#endif + +#endif diff --git a/gprofng/src/dbe_hwcdrv.c b/gprofng/src/dbe_hwcdrv.c new file mode 100644 index 0000000..a471c3e --- /dev/null +++ b/gprofng/src/dbe_hwcdrv.c @@ -0,0 +1,23 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "dbe_hwc.h" +#include "hwcdrv.c" diff --git a/gprofng/src/dbe_hwcfuncs.c b/gprofng/src/dbe_hwcfuncs.c new file mode 100644 index 0000000..66d371a --- /dev/null +++ b/gprofng/src/dbe_hwcfuncs.c @@ -0,0 +1,23 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "dbe_hwc.h" +#include "hwcfuncs.c" diff --git a/gprofng/src/dbe_hwctable.c b/gprofng/src/dbe_hwctable.c new file mode 100644 index 0000000..ce077a1 --- /dev/null +++ b/gprofng/src/dbe_hwctable.c @@ -0,0 +1,23 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "dbe_hwc.h" +#include "hwctable.c" diff --git a/gprofng/src/dbe_memmgr.c b/gprofng/src/dbe_memmgr.c new file mode 100644 index 0000000..2c2e2b4 --- /dev/null +++ b/gprofng/src/dbe_memmgr.c @@ -0,0 +1,118 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <dlfcn.h> +#include "util.h" + +#define CHECK_OUT_OF_MEM(ptr, size) if (ptr == NULL) err_out_of_memory(size) + +/* Report Out of Memory error and exit */ +static void +err_out_of_memory (unsigned nbytes) +{ + char *nm = get_prog_name (1); + if (nm) + fprintf (stderr, GTXT ("%s: Error: Memory capacity exceeded.\n"), nm); + else + fprintf (stderr, GTXT ("Error: Memory capacity exceeded.\n")); + fprintf (stderr, GTXT (" Requested %u bytes.\n"), nbytes); + exit (16); +} + +#define CALL_REAL(x) (__real_##x) +#define NULL_PTR(x) ( __real_##x == NULL ) + +static void *(*__real_malloc)(size_t) = NULL; +static void (*__real_free)(void *) = NULL; +static void *(*__real_realloc)(void *, size_t) = NULL; +static void *(*__real_calloc)(size_t, size_t) = NULL; +static char *(*__real_strdup)(const char*) = NULL; +static volatile int in_init = 0; + +static int +init_heap_intf () +{ + in_init = 1; + __real_malloc = (void*(*)(size_t))dlsym (RTLD_NEXT, "malloc"); + __real_free = (void(*)(void *))dlsym (RTLD_NEXT, "free"); + __real_realloc = (void*(*)(void *, size_t))dlsym (RTLD_NEXT, "realloc"); + __real_calloc = (void*(*)(size_t, size_t))dlsym (RTLD_NEXT, "calloc"); + __real_strdup = (char*(*)(const char*))dlsym (RTLD_NEXT, "strdup"); + in_init = 0; + return 0; +} + +/* --------------------------------------------------------------------------- */ +/* libc's memory management functions substitutions */ + +/* Allocate memory and make sure we got some */ +void * +malloc (size_t size) +{ + if (NULL_PTR (malloc)) + init_heap_intf (); + void *ptr = CALL_REAL (malloc)(size); + CHECK_OUT_OF_MEM (ptr, size); + return ptr; +} + + +/* Implement a workaround for a libdl recursion problem */ +void * +calloc (size_t nelem, size_t size) +{ + if (NULL_PTR (calloc)) + { + /* If a program is linked with libpthread then the following + * calling sequence occurs: + * init_heap_intf -> dlsym -> calloc -> malloc -> init_heap_intf + * We break some performance improvement in libdl by returning + * NULL but preserve functionality. + */ + if (in_init) + return NULL; + init_heap_intf (); + } + return CALL_REAL (calloc)(nelem, size); +} + +/* Free the storage associated with data */ +void +free (void *ptr) +{ + if (ptr == NULL) + return; + if (NULL_PTR (free)) + init_heap_intf (); + CALL_REAL (free)(ptr); + return; +} + +/* Reallocate buffer */ +void * +realloc (void *ptr, size_t size) +{ + if (NULL_PTR (realloc)) + init_heap_intf (); + ptr = CALL_REAL (realloc)(ptr, size); + CHECK_OUT_OF_MEM (ptr, size); + return ptr; +} diff --git a/gprofng/src/dbe_structs.h b/gprofng/src/dbe_structs.h new file mode 100644 index 0000000..e6eaed6 --- /dev/null +++ b/gprofng/src/dbe_structs.h @@ -0,0 +1,219 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_STRUCTS_H +#define _DBE_STRUCTS_H + +#include "dbe_types.h" +#include "enums.h" + +typedef enum +{ + Sp_lang_unknown = 0, + Sp_lang_asm = 1, + Sp_lang_c = 2, + Sp_lang_ansic = 3, + Sp_lang_cplusplus = 4, + Sp_lang_fortran = 5, + Sp_lang_pascal = 6, + Sp_lang_fortran90 = 7, + Sp_lang_java = 8, + Sp_lang_c99 = 9, + Sp_lang_gcc = 16, + Sp_lang_KAI_KPTS = 32, + Sp_lang_KAI_KCC = 33, + Sp_lang_KAI_Kcc = 34 +} Sp_lang_code; + +struct Value +{ + union + { + short s; + int i; + float f; + double d; + timestruc_t t; + char *l; // Label + unsigned long long ll; // address + }; +}; + +// sync enum changes with both AnMetric.java and AnVariable.java +enum ValueTag +{ + VT_SHORT = 1, + VT_INT, + VT_LLONG, + VT_FLOAT, + VT_DOUBLE, + VT_HRTIME, + VT_LABEL, + VT_ADDRESS, + VT_OFFSET, + VT_ULLONG +}; + +// Tagged numeric value +struct TValue +{ + ValueTag tag; + bool sign; // The print result will always begin with a sign (+ or -). + union + { + short s; + int i; + float f; + double d; + char *l; + void *p; + long long ll; + unsigned long long ull; + }; + double to_double (); + int to_int (); + char *to_str (char *str, size_t strsz); + size_t get_len (); + void make_delta (TValue *v1, TValue *v2); + void make_ratio (TValue *v1, TValue *v2); + int compare (TValue *v); +}; + +// XXX MAX_HWCOUNT may need to be managed dynamically, not #defined +#define MAX_HWCOUNT 64 + +// Experiment collection parameters +struct Collection_params +{ + int profile_mode; // if clock-profiling is on + long long ptimer_usec; // Clock profile timer interval (microseconds) + int lms_magic_id; // identifies which LMS_* states are live + int sync_mode; // if synctrace is on + int sync_threshold; // value of synctrace threshold, in microseconds + int sync_scope; // value of synctrace scope: Java and/or native + + int heap_mode; // if heaptrace is on + int io_mode; // if iotrace is on + int race_mode; // if race-detection is on + int race_stack; // setting for stack data collection + int deadlock_mode; // if deadlock-detection is on + int omp_mode; // if omptrace is on + + int hw_mode; // if hw-counter profiling is on + int xhw_mode; // if extended (true-PC) HW counter profiling for any counter + + char *hw_aux_name[MAX_HWCOUNT]; + char *hw_username[MAX_HWCOUNT]; + int hw_interval[MAX_HWCOUNT]; // nominal interval for count + int hw_tpc[MAX_HWCOUNT]; // non-zero, if aggressive TPC/VA requested + int hw_metric_tag[MAX_HWCOUNT]; // tag as used for finding metrics + int hw_cpu_ver[MAX_HWCOUNT]; // Chip version number for this metric + + int sample_periodic; // if periodic sampling is on + int sample_timer; // Sample timer (sec) + int limit; // experiment size limit + const char *pause_sig; // Pause/resume signal string + const char *sample_sig; // Sampling signal string + const char *start_delay; // Data collect start delay string + const char *terminate; // Data collection termination time string + char *linetrace; +}; + +const hrtime_t ZERO_TIME = (hrtime_t) 0; +const hrtime_t MAX_TIME = (hrtime_t) 0x7fffffffffffffffLL; + +#define PCInvlFlag ((int) 0x8LL) +#define PCLineFlag ((int) 0x4LL) +#define PCTrgtFlag ((int) 0x2LL) +#define MAKE_ADDRESS(idx, off) (((unsigned long long)(idx)<<32) | off) +#define ADDRESS_SEG(x) ((unsigned int)(((x)>>32) & 0xffffffff)) +#define ADDRESS_OFF(x) ((unsigned int)((x) & 0xffffffff)) + +// +// Analyzer info +#define AnalyzerInfoVersion 2 + +typedef struct +{ + uint64_t text_labelref; + int32_t entries; + uint32_t version; +} AnalyzerInfoHdr; // => header from .__analyzer_info + +typedef struct +{ + uint32_t offset; // offset relative to text_labelref + uint32_t id; // profiled instruction identifier + uint32_t signature; // signature of profiled instruction + uint32_t datatype_id; // referenced datatype identifier +} memop_info_t; // => used for table_type=0,1,2 + +typedef struct +{ + uint32_t offset; // offset relative to text_labelref +} target_info_t; // => used for table_type=3 + +typedef struct +{ + uint32_t type; + uint32_t offset; + union + { + memop_info_t *memop; + target_info_t *target; + }; +} inst_info_t; + +class DataObject; + +typedef struct +{ + uint32_t datatype_id; // datatype identifier (local) + uint32_t memop_refs; // count of referencing memops + uint32_t event_data; // count of event data + DataObject *dobj; // corresponding dataobject (unique) +} datatype_t; + +typedef struct +{ + uint32_t offset; // entry offset in compilation unit + uint32_t extent; // sibling offset + void *parent; // container symbol + void *object; // resolved object +} symbol_t; + +typedef struct +{ + char *old_prefix; + char *new_prefix; +} pathmap_t; + +typedef struct +{ + char *libname; + enum LibExpand expand; +} lo_expand_t; + +typedef struct +{ + int index1; + int index2; +} int_pair_t; +#endif /* _DBE_STRUCTS_H */ diff --git a/gprofng/src/dbe_types.h b/gprofng/src/dbe_types.h new file mode 100644 index 0000000..79fd6c7 --- /dev/null +++ b/gprofng/src/dbe_types.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _DBE_TYPES_H +#define _DBE_TYPES_H + +#include <stdint.h> +#include "gp-time.h" + +typedef unsigned long long Size; /* object sizes in 64 bit apps */ +typedef unsigned long long Vaddr; /* process address for 64 bit apps */ + +typedef unsigned long long ull_t; +typedef long long ll_t; +typedef unsigned long ul_t; + +// Note: these values are stored in archive files; changing them +// may cause old archives to become incompatible. +enum Platform_t +{ + Unknown = 0, + Sparc, + Sparcv9, + Intel, + Sparcv8plus, + Java, + Amd64, + Aarch64 +}; + +enum WSize_t +{ + Wnone, + W32, + W64 +}; + +enum VMode +{ + VMODE_MACHINE = 0, + VMODE_USER, + VMODE_EXPERT +}; + +#endif /* _DBE_TYPES_H */ diff --git a/gprofng/src/debug.h b/gprofng/src/debug.h new file mode 100644 index 0000000..9761f2a --- /dev/null +++ b/gprofng/src/debug.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _PERFAN_DEBUG_H +#define _PERFAN_DEBUG_H + +extern unsigned int mpmt_debug_opt; +// To set mpmt_debug_opt use: +// MPMT_DEBUG=4095 ; export MPMT_DEBUG +#define DEBUG_FLAG (mpmt_debug_opt & 1) +#define DUMP_ELF_SEC (mpmt_debug_opt & 2) +#define DUMP_ELF_SYM (mpmt_debug_opt & 4) +#define DUMP_RELA_SEC (mpmt_debug_opt & 8) +#define DUMP_ELF_RELOC DUMP_RELA_SEC +#define DUMP_DWARFLIB (mpmt_debug_opt & 16) +#define DUMP_DWR_LINE_REGS (mpmt_debug_opt & 32) +#define DUMP_USER_LABELS (mpmt_debug_opt & 64) +#define DEBUG_MAPS (mpmt_debug_opt & 128) +#define DEBUG_DBE_FILE (mpmt_debug_opt & 256) +#define DEBUG_DATA_WINDOW (mpmt_debug_opt & 512) +#define DEBUG_STABS (mpmt_debug_opt & 1024) +#define DEBUG_DATAOBJ (mpmt_debug_opt & 2048) +#define DEBUG_LOADOBJ (mpmt_debug_opt & 4096) +#define DEBUG_SAXPARSER (mpmt_debug_opt & 8192) +#define DUMP_JAVA_CLASS (mpmt_debug_opt & 16384) +#define DEBUG_COMPARISON (mpmt_debug_opt & 32768) +#define DEBUG_READ_AR (mpmt_debug_opt & 65536) +#define DEBUG_ERR_MSG (mpmt_debug_opt & 131072) +#define DUMP_JCLASS_READER (mpmt_debug_opt & 262144) +#define DEBUG_DBE (mpmt_debug_opt & 524288) +#define DEBUG_ARCHIVE (mpmt_debug_opt & 1048576) +#define DEBUG_IO (mpmt_debug_opt & 2097152) +#define DUMP_DYN_FILE (mpmt_debug_opt & 4194304) +#define DUMP_JAR_FILE (mpmt_debug_opt & 8388608) +#define DUMP_CALL_STACK (mpmt_debug_opt & 16777216) +#define DEBUG_THREADS (mpmt_debug_opt & 33554432) +#define DBE_USE_MMAP (mpmt_debug_opt & 67108864) + +#ifdef DEBUG + +// Turn on assertion checking whenever debugging +#define ASSERTS 1 + +// debug macro - provides a clean way of inserting debugging code without +// having the distracting #ifdef DEBUG ... #else ... #endif directives +// interspersed throughout the code. It also provides an easy way +// to turn them off with no loss of efficiency. It is not limited +// to printf() commands; any code may be inserted. Variables +// needed only by the debugging code can be declared inside a +// debug { ... } statement. +// +// usage: +// debug <statement> +// or, debug { <statements> } +// If DEBUG is on, map "DEBUG_CODE" to nothing! +// This results in the <statement> being executed normally + +#define DEBUG_CODE + +#else +// If DEBUG is off, map "DEBUG_CODE" to something harmless. +// The clever hack used here is to use a conditional with a +// constant condition, which is optimized out by the compiler, +// so that <statement> is not present in the compiled code! + +#define DEBUG_CODE if (0) + +#endif /*DEBUG*/ + +#define Dprintf(x, ...) DEBUG_CODE if(x) fprintf(stderr, __VA_ARGS__) + +#endif /* ! _DEBUG_H */ diff --git a/gprofng/src/enums.h b/gprofng/src/enums.h new file mode 100644 index 0000000..a2c9500 --- /dev/null +++ b/gprofng/src/enums.h @@ -0,0 +1,195 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _PERFAN_ENUMS_H +#define _PERFAN_ENUMS_H + +#include "comp_com.h" + +enum Cmd_status +{ + CMD_OK = 0, + CMD_BAD, + CMD_AMBIGUOUS, + CMD_BAD_ARG, + CMD_OUTRANGE, + CMD_INVALID +}; + +enum LibExpand +{ + LIBEX_SHOW = 0, + LIBEX_HIDE = 1, + LIBEX_API = 2 +}; + +enum SrcVisible +{ + SRC_NA = 0, + SRC_CODE = 1, + SRC_METRIC = 2 +}; + +enum MetricType +{ // sync enum changes with Settings.java + MET_NORMAL = 0, // functions, lines, pcs; src & disasm (non-compare) + MET_CALL, // callers-callees + MET_DATA, // dataspace + MET_INDX, // index objects + MET_CALL_AGR, // call tree + MET_COMMON, // Analyzer uses for DSP_DISASM, DSP_SOURCE, ... + MET_IO, // IO activity + MET_SRCDIS, // src & disasm (non comparison mode) + MET_HEAP // Heap leaked list +}; + +enum ValueType +{ // Bitmask (!) sync enum changes with AnMetric.java + VAL_NA = 0, // nothing specified (use this enum instead of 0) + VAL_TIMEVAL = 1, + VAL_VALUE = 2, + VAL_PERCENT = 4, + VAL_DELTA = 8, + VAL_RATIO = 16, + VAL_INTERNAL = 32, + VAL_HIDE_ALL = 64 // hide all, but allows settings to be remembered +}; + +enum CompCom +{ // no value here can be the same as CCMV_ + COMP_SRC = CCMV_BASIC + 1, + COMP_SRC_METRIC, + COMP_NOSRC, + COMP_HEX, + COMP_NOHEX, + COMP_THRESHOLD, + COMP_CMPLINE, + COMP_FUNCLINE +}; + +enum TLStack_align +{ + TLSTACK_ALIGN_ROOT = 1, + TLSTACK_ALIGN_LEAF +}; + +enum Reorder_status +{ + REORDER_SUCCESS, + REORDER_FAIL, + REORDER_ZERO, + REORDER_ONE_FUNC, + REORDER_FILE_OPEN, + REORDER_FILE_WRITE, + REORDER_COMP, + REORDER_NO_LOAD_OBJ, + REORDER_NO_OBJECT, + REORDER_INVALID +}; + +enum AnUtility_state +{ + EXP_SUCCESS = 0, + EXP_FAILURE = 1, + EXP_INCOMPLETE = 2, + EXP_BROKEN = 4, + EXP_OBSOLETE = 8 +}; + +enum Presentation_align_type +{ + TEXT_LEFT = 1, + TEXT_CENTER = 2, + TEXT_RIGHT = 3 +}; + +enum Message_type +{ + ERROR_MSG = 1, + WARNING_MSG = 2, + PSTAT_MSG = 3, + PWARN_MSG = 4 +}; + +enum Presentation_clock_unit +{ + CUNIT_NULL = -1, + CUNIT_BYTES = -2, + CUNIT_TIME = -3 +}; + +enum FuncListDisp_type +{ + DSP_FUNCTION = 1, + DSP_LINE = 2, + DSP_PC = 3, + DSP_SOURCE = 4, + DSP_DISASM = 5, + DSP_SELF = 6, // not a tab; ID for Callers-Callees fragment data + DSP_CALLER = 7, + DSP_CALLEE = 8, // not a tab; ID for Callers-Callees callees data + DSP_CALLTREE = 9, + DSP_TIMELINE = 10, + DSP_STATIS = 11, + DSP_EXP = 12, + DSP_LEAKLIST = 13, + DSP_MEMOBJ = 14, // requires a specific subtype to define a tab + DSP_DATAOBJ = 15, + DSP_DLAYOUT = 16, + DSP_SRC_FILE = 17, // not a tab; Details information (?) + DSP_IFREQ = 18, + DSP_RACES = 19, + DSP_INDXOBJ = 20, // requires a specific subtype to define a tab + DSP_DUALSOURCE = 21, + DSP_SOURCE_DISASM = 22, + DSP_DEADLOCKS = 23, + DSP_MPI_TL = 24, + DSP_MPI_CHART = 25, + //DSP_TIMELINE_CLASSIC_TBR = 26, + DSP_SOURCE_V2 = 27, // comparison + DSP_DISASM_V2 = 28, // comparison + //DSP_THREADS_TL = 29; + //DSP_THREADS_CHART = 30; + DSP_IOACTIVITY = 31, + DSP_OVERVIEW = 32, + DSP_IOVFD = 33, + DSP_IOCALLSTACK = 34, + DSP_MINICALLER = 37, + DSP_HEAPCALLSTACK = 39, + DSP_CALLFLAME = 40, + DSP_SAMPLE = 99 +}; + +enum CmpMode +{ + CMP_DISABLE = 0, + CMP_ENABLE = 1, + CMP_RATIO = 2, + CMP_DELTA = 4 +}; + +enum PrintMode +{ + PM_TEXT = 0, + PM_HTML = 1, + PM_DELIM_SEP_LIST = 2 +}; + +#endif // _ENUMS_H diff --git a/gprofng/src/envsets.cc b/gprofng/src/envsets.cc new file mode 100644 index 0000000..de06fbf --- /dev/null +++ b/gprofng/src/envsets.cc @@ -0,0 +1,420 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <assert.h> +#include <ctype.h> +#include <sys/param.h> +#include <unistd.h> + +#include "gp-defs.h" +#include "util.h" +#include "collctrl.h" +#include "collect.h" +#include "StringBuilder.h" +#include "Settings.h" + +#define STDEBUFSIZE 24000 + +#define LIBGP_COLLECTOR "libgp-collector.so" +#define GPROFNG_PRELOAD_LIBDIRS "GPROFNG_PRELOAD_LIBDIRS" +#define SP_COLLECTOR_EXPNAME "SP_COLLECTOR_EXPNAME" +#define SP_COLLECTOR_FOLLOW_SPEC "SP_COLLECTOR_FOLLOW_SPEC" +#define SP_COLLECTOR_PARAMS "SP_COLLECTOR_PARAMS" +#define SP_COLLECTOR_FOUNDER "SP_COLLECTOR_FOUNDER" +#define SP_COLLECTOR_ORIGIN_COLLECT "SP_COLLECTOR_ORIGIN_COLLECT" + +static const char *LD_AUDIT[] = { + // "LD_AUDIT", Do not set LD_AUDIT on Linux + NULL +}; + +static const char *LD_PRELOAD[] = { + "LD_PRELOAD", + NULL +}; + +static const char *SP_PRELOAD[] = { + "SP_COLLECTOR_PRELOAD", + NULL +}; + +static const char *LD_LIBRARY_PATH[] = { + "LD_LIBRARY_PATH", + NULL, +}; + +static int +add_env (char *ev) +{ + int r = putenv (ev); + if (r != 0) + { + dbe_write (2, GTXT ("Can't putenv of %s: run aborted\n"), ev); + free (ev); + } + return r; +} + +int +collect::putenv_libcollector_ld_audits () +{ + StringBuilder sb; + for (unsigned int ii = 0; ii < ARR_SIZE (LD_AUDIT) && LD_AUDIT[ii]; ++ii) + { + sb.sprintf ("%s=%s", LD_AUDIT[ii], SP_LIBAUDIT_NAME); + // Append the current value. Check if already set + char *old_val = getenv (LD_AUDIT[ii]); + if (old_val != NULL) + { + while (isspace (*old_val)) + ++old_val; + if (*old_val != (char) 0) + { + int fromIdx = sb.length (); + sb.append (" "); + sb.append (old_val); + if (sb.indexOf (SP_LIBAUDIT_NAME, fromIdx) >= 0) + continue; // Already set. Do nothing. + } + } + if (add_env (sb.toString ())) + return 1; + } + return 0; +} + +int +collect::putenv_libcollector_ld_preloads () +{ + // for those data types that get extra libs LD_PRELOAD'd, add them + if (cc->get_synctrace_mode () != 0) + add_ld_preload ("libgp-sync.so"); + if (cc->get_heaptrace_mode () != 0) + add_ld_preload ("libgp-heap.so"); + if (cc->get_iotrace_mode () != 0) + add_ld_preload ("libgp-iotrace.so"); + add_ld_preload (SP_LIBCOLLECTOR_NAME); + + // --- putenv SP_COLLECTOR_PRELOAD* + int ii; + for (ii = 0; SP_PRELOAD[ii]; ii++) + { + // construct the SP_PRELOAD_* environment variables + // and put them into the environment + if (add_env (dbe_sprintf ("%s=%s", SP_PRELOAD[ii], sp_preload_list[ii]))) + return 1; + } + // --- putenv LD_PRELOADS + /* purge LD_PRELOAD* of values containing contents of SP_LIBCOLLECTOR_NAME */ + if (putenv_purged_ld_preloads (SP_LIBCOLLECTOR_NAME)) + dbe_write (2, GTXT ("Warning: %s is already defined in one or more LD_PRELOAD environment variables\n"), + SP_LIBCOLLECTOR_NAME); + if (putenv_ld_preloads ()) + return 1; + return 0; +} + +int +collect::putenv_libcollector_ld_misc () +{ +#if 0 // XXX 1 turns on LD_DEBUG + putenv (strdup ("LD_DEBUG=audit,bindings,detail")); +#endif + // workaround to have the dynamic linker use absolute names + if (add_env (dbe_strdup ("LD_ORIGIN=yes"))) + return 1; + + // On Linux we have to provide SP_COLLECTOR_LIBRARY_PATH and LD_LIBRARY_PATH + // so that -agentlib:gp-collector works + // and so that collect -F works with 32/64-bit mix of processes + + // Set GPROFNG_PRELOAD_LIBDIRS + char *ev = getenv (GPROFNG_PRELOAD_LIBDIRS); + char *libpath_list = NULL; + if (ev == NULL && settings->preload_libdirs == NULL) + { + settings->read_rc (false); + ev = settings->preload_libdirs; + } + ev = dbe_strdup (ev); + StringBuilder sb; + sb.appendf ("%s=", "SP_COLLECTOR_LIBRARY_PATH"); + int len = sb.length (); + int cnt = 0; + for (char *s = ev; s;) + { + char *s1 = strchr (s, ':'); + if (s1) + *(s1++) = 0; + char *fname; + if (*s == '/') + { + fname = dbe_sprintf ("%s/%s", s, LIBGP_COLLECTOR); + if (access (fname, R_OK | F_OK) == 0) + { + if (++cnt != 1) + sb.append (':'); + sb.appendf ("%s", s); + } + } + else + { + fname = dbe_sprintf ("%s/%s/%s", run_dir, s, LIBGP_COLLECTOR); + if (access (fname, R_OK | F_OK) == 0) + { + if (++cnt != 1) + sb.append (':'); + sb.appendf ("%s/%s", run_dir, s); + } + } + free (fname); + s = s1; + } + free (ev); + if (cnt == 0) + { + dbe_write (2, GTXT ("configuration error: can not find %s. run aborted\n"), + LIBGP_COLLECTOR); + return 1; + } + libpath_list = sb.toString (); + if (add_env (libpath_list)) + return 1; + libpath_list += len; + + // --- set LD_LIBRARY_PATH using libpath_list + char *old = getenv (LD_LIBRARY_PATH[0]); + if (old) + ev = dbe_sprintf ("%s=%s:%s", LD_LIBRARY_PATH[0], libpath_list, old); + else + ev = dbe_sprintf ("%s=%s", LD_LIBRARY_PATH[0], libpath_list); + if (add_env (ev)) + return 1; + return 0; +} + +void +collect::add_ld_preload (const char *lib) +{ + for (int ii = 0; SP_PRELOAD[ii]; ii++) + { + char *old_sp = sp_preload_list[ii]; + if (old_sp == NULL) + sp_preload_list[ii] = strdup (lib); + else + { + sp_preload_list[ii] = dbe_sprintf ("%s %s", old_sp, lib); + free (old_sp); + } + } +} + +int +collect::putenv_memso () +{ + // Set environment variable "MEM_USE_LOG" to 1, to keep it out of stderr + if (add_env (dbe_strdup ("MEM_USE_LOG=1"))) + return 1; + // Set environment variable "MEM_ABORT_ON_ERROR", to force a core dump + if (add_env (dbe_strdup ("MEM_ABORT_ON_ERROR=1"))) + return 1; + add_ld_preload ("mem.so"); + return putenv_ld_preloads (); +} + +// set LD_PRELOAD and friends to prepend the given library or libraries + +int +collect::putenv_ld_preloads () +{ + for (int ii = 0; LD_PRELOAD[ii]; ii++) + { + char *old_val = getenv (LD_PRELOAD[ii]); + int sp_num = ii; + assert (SP_PRELOAD[sp_num]); + char *preload_def; + if (old_val) + preload_def = dbe_sprintf ("%s=%s %s", LD_PRELOAD[ii], sp_preload_list[sp_num], old_val); + else + preload_def = dbe_sprintf ("%s=%s", LD_PRELOAD[ii], sp_preload_list[sp_num]); + if (add_env (preload_def)) + return 1; + } + return 0; +} + +/* copied from linetrace.c */ +/* + function: env_strip() + Finds str in env; Removes + all characters from previous ':' or ' ' + up to and including any trailing ':' or ' '. + params: + env: environment variable + str: substring to find + return: count of instances removed from env + */ +int +collect::env_strip (char *env, const char *str) +{ + int removed = 0; + char *p, *q; + if (env == NULL || str == NULL || *str == 0) + return 0; + size_t maxlen = strlen (env); + size_t len = strlen (str); + q = env; + while ((p = strstr (q, str)) != NULL) + { + q = p; + p += len; + if (*p) + { + while ((*p) && (*p != ':') && (*p != ' ')) + p++; /* skip the rest of the name*/ + while ((*p == ':') || (*p == ' ')) + p++; /* strip trailing separator */ + } + while (*q != ':' && *q != ' ' && *q != '=' && q != env) + q--; /* strip path */ + if (*p) + { /* copy the rest of the string */ + if (q != env) + q++; /* restore leading separator (if any) */ + size_t n = (maxlen - (q - env)); + strncpy (q, p, n); + } + else + *q = 0; + removed++; + } + return removed; +} +/* + function: putenv_purged_ld_preloads() + Remove selected preload strings from all LD_PRELOAD* env vars. + params: + var: executable name (leading characters don't have to match) + return: number of instances removed from all PRELOAD vars. + */ +int +collect::putenv_purged_ld_preloads (const char *var) +{ + int total_removed = 0; + if (!var || *var == 0) + return 0; + for (int ii = 0; LD_PRELOAD[ii]; ii++) + { + char *ev = getenv (LD_PRELOAD[ii]); + int removed = 0; + if (!ev) + continue; + removed = env_strip (ev, var); + if (!removed) + continue; + if (putenv (ev) != 0) + dbe_write (2, GTXT ("Can't putenv of %s\n"), ev); + total_removed += removed; + } + return total_removed; +} +/* + function: putenv_append() + append string to current enviroment variable setting and then do a putenv() + params: + var: environment variable name + val: string to append + */ +int +collect::putenv_append (const char *var, const char *val) +{ + char *ev; + if (!var || !val) + return 1; + const char *old_val = getenv (var); + if (old_val == NULL || *old_val == 0) + ev = dbe_sprintf ("%s=%s", var, val); + else + ev = dbe_sprintf ("%s=%s %s", var, old_val, val); + + // now put the new variable into the environment + if (add_env (ev)) + return 1; + return 0; +} + +int +collect::putenv_libcollector (void) +{ + char buf[MAXPATHLEN + 1]; + // --- set SP_COLLECTOR_EXPNAME + // fetch the experiment name and CWD + char *exp = cc->get_experiment (); + char *cwd = getcwd (buf, MAXPATHLEN); + char *ev; + + // format the environment variable for the experiment directory name + if (cwd != NULL && exp[0] != '/') // experiment is a relative path + ev = dbe_sprintf ("%s=%s/%s", SP_COLLECTOR_EXPNAME, cwd, exp); + else // getcwd failed or experiment is a fullpath + ev = dbe_sprintf ("%s=%s", SP_COLLECTOR_EXPNAME, exp); + + // set the experiment directory name + if (add_env (ev)) + return 1; + + // --- set SP_COLLECTOR_PARAMS + // set the data descriptor + exp = cc->get_data_desc (); + if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_PARAMS, exp))) + return 1; + + // --- set SP_COLLECTOR_FOLLOW_SPEC + const char *follow_spec = cc->get_follow_cmp_spec (); + if (follow_spec) + // selective following has been enabled + if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_FOLLOW_SPEC, follow_spec))) + return 1; + + if (add_env (dbe_sprintf ("%s=%d", SP_COLLECTOR_FOUNDER, getpid ()))) + return 1; + if (add_env (dbe_sprintf ("%s=1", SP_COLLECTOR_ORIGIN_COLLECT))) + return 1; + + // --- set LD_* + if (putenv_libcollector_ld_misc ()) + return 1; + + // --- set LD_PRELOAD* + if (putenv_libcollector_ld_preloads () != 0) + return 1; + + // --- set JAVA_TOOL_OPTIONS + if (cc->get_java_mode () == 1) + if (putenv_append ("JAVA_TOOL_OPTIONS", "-agentlib:gp-collector")) + exit (1); +#if 0 + // --- set LD_AUDIT* + if (putenv_libcollector_ld_audits () != 0) + return 1; +#endif + return 0; +} diff --git a/gprofng/src/gethrtime.c b/gprofng/src/gethrtime.c new file mode 100644 index 0000000..8ba7295 --- /dev/null +++ b/gprofng/src/gethrtime.c @@ -0,0 +1,166 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/resource.h> + +#include "gp-defs.h" +#include "gp-time.h" + +/* =============================================================== */ +/* + * Below this are the get_clock_rate() and get_ncpus() for all architectures + */ + +static int clock_rate = 0; +static int ncpus = 0; +static char msgbuf[1024]; + +int +get_clock_rate (void) +{ + /* Linux version -- read /proc/cpuinfo + * Note the parsing is different on intel-Linux and sparc-Linux + */ + FILE *fp = fopen ("/proc/cpuinfo", "r"); + if (fp != NULL) + { + + char temp[1024]; + while (fgets (temp, sizeof (temp), fp) != NULL) + { +#if ARCH(SPARC) + /* cpu count for SPARC linux -- read from /proc/cpuinfo */ + if (strncmp (temp, "ncpus active", 12) == 0) + { + char *val = strchr (temp, ':'); + ncpus = val ? atol (val + 1) : 0; + } +#endif /* ARCH(SPARC) */ + + if (clock_rate == 0) + { + /* pick the first line that gives a CPU clock rate */ +#if ARCH(SPARC) + long long clk; + if (strncmp (temp, "Cpu0ClkTck", 10) == 0) + { + char *val = strchr (temp, ':'); + clk = val ? strtoll (val + 1, NULL, 16) : 0; + clock_rate = (int) (clk / 1000000); + } +#else + if (strncmp (temp, "cpu MHz", 7) == 0) + { + char *val = strchr (temp, ':'); + clock_rate = val ? atoi (val + 1) : 0; + } +#endif /* ARCH() */ + } + + /* did we get a clock rate? */ + if (clock_rate != 0) + { +#if ARCH(SPARC) + /* since we got a cpu count, we can break from the look */ + break; +#endif /* ARCH(SPARC) */ + } +#if ARCH(Intel) + /* On intel-Linux, count cpus based on "cpu MHz" lines */ + if (strncmp (temp, "cpu MHz", 7) == 0) + ncpus++; +#endif /* ARCH(Intel) */ + } + fclose (fp); + } + + if (clock_rate != 0) + sprintf (msgbuf, + "Clock rate = %d MHz (from reading /proc/cpuinfo) %d CPUs\n", + clock_rate, ncpus); + + /* did we get a clock rate? */ + if (clock_rate == 0) + { + clock_rate = 1000; + sprintf (msgbuf, "Clock rate = %d MHz (set by default) %d CPUs\n", + clock_rate, ncpus); + } + return clock_rate; +} + +int +get_ncpus (void) +{ + if (clock_rate == 0) + get_clock_rate (); + return ncpus; +} + +/* gethrvtime -- generic solution, getting user time from + * clock_gettime(CLOCK_THREAD_CPUTIME_ID,..), and reformatting. + * need -lrt to compile.*/ +hrtime_t +gethrvtime () +{ + struct timespec tp; + hrtime_t rc = 0; + int r = clock_gettime (CLOCK_THREAD_CPUTIME_ID, &tp); + if (r == 0) + rc = ((hrtime_t) tp.tv_sec) * 1000000000 + (hrtime_t) tp.tv_nsec; + return rc; +} + +/* + * CLOCK_MONOTONIC + * Clock that cannot be set and represents monotonic time since some + * unspecified starting point. + */ +hrtime_t +gethrtime (void) +{ + struct timespec tp; + hrtime_t rc = 0; + + /* + * For er_kernel on Linux, we want to match how DTrace gets its timestamps. + * This is CLOCK_MONOTONIC_RAW. It might be changing to CLOCK_MONOTONIC. + * For now, we change to "RAW" and can change back if DTrace changes. + * + * The two can be different. Check the clock_gettime() man page. + * CLOCK_MONOTONIC_RAW is Linux-specific and introduced in 2.6.28. + * It is impervious to NTP or adjtime adjustments. + * + * We must match the timer used in perfan/libcollector/src/gethrtime.c. + * + * There is no issue on Solaris, where gethrtime() is provided by the kernel + * and used by DTrace. + */ + int r = clock_gettime (CLOCK_MONOTONIC_RAW, &tp); + if (r == 0) + rc = ((hrtime_t) tp.tv_sec) * 1000000000 + (hrtime_t) tp.tv_nsec; + return rc; +} diff --git a/gprofng/src/gp-archive.cc b/gprofng/src/gp-archive.cc new file mode 100644 index 0000000..8d3fc23 --- /dev/null +++ b/gprofng/src/gp-archive.cc @@ -0,0 +1,700 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <getopt.h> + +#include "util.h" +#include "StringMap.h" +#include "LoadObject.h" +#include "DbeSession.h" +#include "DbeFile.h" +#include "SourceFile.h" +#include "Elf.h" +#include "gp-archive.h" +#include "ArchiveExp.h" +#include "Print.h" +#include "Module.h" + +er_archive::er_archive (int argc, char *argv[]) : DbeApplication (argc, argv) +{ + force = 0; + common_archive_dir = NULL; + quiet = 0; + descendant = 1; + use_relative_path = 0; + s_option = ARCH_EXE_ONLY; + mask = NULL; +} + +er_archive::~er_archive () +{ + if (mask) + { + for (long i = 0, sz = mask->size (); i < sz; i++) + { + regex_t *regex_desc = mask->get (i); + regfree (regex_desc); + delete regex_desc; + } + delete mask; + } + delete common_archive_dir; +} + +int +er_archive::mask_is_on (const char *str) +{ + if (mask == NULL) + return 1; + for (long i = 0, sz = mask->size (); i < sz; i++) + { + regex_t *regex_desc = mask->get (i); + if (regexec (regex_desc, str, 0, NULL, 0) == 0) + return 1; + } + return 0; +} + +void +er_archive::usage () +{ +/* + fprintf (stderr, GTXT ("Usage: %s [-nqFV] [-a on|ldobjects|src|usedldobjects|usedsrc|off] [-m regexp] experiment\n"), whoami); +*/ + +/* + Ruud - Isolate this line because it has an argument. Otherwise it would be at the + end of this long list. +*/ + printf ( GTXT ( + "Usage: gprofng archive [OPTION(S)] EXPERIMENT\n")); + + printf ( GTXT ( + "\n" + "Archive the associated application binaries and source files in a gprofng\n" + "experiment to make it self contained and portable.\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + " -a {off|on|ldobjects|src|usedldobjects|usedsrc} specify archiving of binaries and other files;\n" + " in addition to disable this feature (off), or enable archiving off all\n" + " loadobjects and sources (on), the other options support a more\n" + " refined selection. All of these options enable archiving, but the\n" + " keyword controls what exactly is selected: all load objects (ldobjects),\n" + " all source files (src), the loadobjects asscoiated with a program counter\n" + " (usedldobjects), or the source files associated with a program counter\n" + " (usedsrc); the default is \"-a ldobjects\".\n" + "\n" + " -n archive the named experiment only, not any of its descendants.\n" + "\n" + " -q do not write any warnings to stderr; messages are archived and\n" + " can be retrieved later.\n" + "\n" + " -F force writing or rewriting of the archive; ignored with the -n\n" + " or -m options, or if this is a subexperiment.\n" + "\n" + " -d <path> specifies the location of a common archive; this is a directory that\n" + " contains archived files.\n" + "\n" + " -m <regex> archive only those source, object, and debug info files whose full\n" + " path name matches the given POSIX compliant regular expression.\n" + "\n" + "Limitations:\n" + "\n" + "Default archiving does not occur in case the application profiled terminates prematurely,\n" + "or if archiving is disabled when collecting the performance data. In such cases, this\n" + "tool can be used to afterwards archive the information, but it has to run on the same \n" + "system where the profiling data was recorded.\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gprofng(1), gp-collect-app(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n")); +// Ruud +/* + fprintf (stderr, GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION); +*/ + exit (1); +} + +Vector <LoadObject*> * +er_archive::get_loadObjs () +{ + Vector <LoadObject*> *objs = new Vector<LoadObject*>(); + Vector <LoadObject*> *loadObjs = dbeSession->get_text_segments (); + if (s_option != ARCH_NOTHING) + { + for (long i = 0, sz = VecSize(loadObjs); i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + if ((lo->flags & SEG_FLAG_DYNAMIC) != 0) + continue; + DbeFile *df = lo->dbeFile; + if (df && ((df->filetype & DbeFile::F_FICTION) != 0)) + continue; + if (!lo->isUsed && ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0)) + continue; + objs->append (lo); + } + } + if (DEBUG_ARCHIVE) + { + Dprintf (DEBUG_ARCHIVE, NTXT ("get_text_segments(): %d\n"), + (int) (loadObjs ? loadObjs->size () : -1)); + for (long i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d [%2ld] %s\n"), + get_basename (__FILE__), (int) __LINE__, i, STR (lo->dump ())); + } + Dprintf (DEBUG_ARCHIVE, NTXT ("\nget_loadObjs(): %d\n"), + (int) (objs ? objs->size () : -1)); + for (long i = 0, sz = VecSize(objs); i < sz; i++) + { + LoadObject *lo = objs->get (i); + Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d [%2ld] %s\n"), + get_basename (__FILE__), (int) __LINE__, i, STR (lo->dump ())); + } + } + delete loadObjs; + return objs; +} + +/** + * Clean old archive + * Except the following cases: + * 1. Founder experiment is an MPI experiment + * 2. "-n" option is passed (do not archive descendants) + * 3. "-m" option is passed (partial archiving) + * 4. Experiment name is not the founder experiment (it is a sub-experiment) + * @param expname + * @param founder_exp + * @return 0 - success + */ +int +er_archive::clean_old_archive (char *expname, ArchiveExp *founder_exp) +{ + if (0 == descendant) + { // do not archive descendants + fprintf (stderr, GTXT ("Warning: Option -F is ignored because -n option is specified (do not archive descendants)\n")); + return 1; + } + if (NULL != mask) + { // partial archiving + fprintf (stderr, GTXT ("Warning: Option -F is ignored because -m option is specified\n")); + return 1; + } + // Check if the experiment is the founder + char *s1 = dbe_strdup (expname); + char *s2 = dbe_strdup (founder_exp->get_expt_name ()); + if (!s1 || !s2) + { + fprintf (stderr, GTXT ("Cannot allocate memory\n")); + exit (1); + } + // remove trailing slashes + for (int n = strlen (s1); n > 0; n--) + { + if ('/' != s1[n - 1]) + break; + s1[n - 1] = 0; + } + for (int n = strlen (s2); n > 0; n--) + { + if ('/' != s2[n - 1]) + break; + s2[n - 1] = 0; + } + if (strcmp (s1, s2) != 0) + { // not founder + fprintf (stderr, GTXT ("Warning: Option -F is ignored because specified experiment name %s does not match founder experiment name %s\n"), s1, s2); + free (s1); + free (s2); + return 1; + } + // Remove old "archives" + char *arch = founder_exp->get_arch_name (); + fprintf (stderr, GTXT ("INFO: removing existing archive: %s\n"), arch); + if (dbe_stat (arch, NULL) == 0) + { + char *cmd = dbe_sprintf ("/bin/rm -rf %s", arch); + system (cmd); + free (cmd); + if (dbe_stat (arch, NULL) != 0) + { // create "archives" + if (!founder_exp->create_dir (founder_exp->get_arch_name ())) + { + fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ()); + exit (1); + } + } + } + free (s1); + free (s2); + return 0; +} // clean_old_archive_if_necessary + +void +er_archive::start (int argc, char *argv[]) +{ + int last = argc - 1; + if (check_args (argc, argv) != last) + usage (); + check_env_var (); + if (s_option == ARCH_NOTHING) + return; + + ArchiveExp *founder_exp = new ArchiveExp (argv[last]); + if (founder_exp->get_status () == Experiment::FAILURE) + { + if (!quiet) + fprintf (stderr, GTXT ("er_archive: %s: %s\n"), argv[last], + pr_mesgs (founder_exp->fetch_errors (), NTXT (""), NTXT (""))); + exit (1); + } + if (!founder_exp->create_dir (founder_exp->get_arch_name ())) + { + fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ()); + exit (1); + } + if (!common_archive_dir) + common_archive_dir = dbe_strdup (getenv ("GPROFNG_ARCHIVE_COMMON_DIR")); + if (common_archive_dir) + { + if (!founder_exp->create_dir (common_archive_dir)) + if (dbe_stat (common_archive_dir, NULL) != 0) + { + fprintf (stderr, GTXT ("Unable to create directory for common archive `%s'\n"), common_archive_dir); + exit (1); + } + } + // Clean old archives if necessary + if (force) + clean_old_archive (argv[last], founder_exp); + Vector<ArchiveExp*> *exps = new Vector<ArchiveExp*>(); + exps->append (founder_exp); + if (descendant) + { + Vector<char*> *exp_names = founder_exp->get_descendants_names (); + if (exp_names) + { + for (long i = 0, sz = exp_names->size (); i < sz; i++) + { + char *exp_path = exp_names->get (i); + ArchiveExp *exp = new ArchiveExp (exp_path); + if (exp->get_status () == Experiment::FAILURE) + { + if (!quiet) + fprintf (stderr, GTXT ("er_archive: %s: %s\n"), exp_path, + pr_mesgs (exp->fetch_errors (), NTXT (""), NTXT (""))); + delete exp; + continue; + } + exps->append (exp); + } + exp_names->destroy (); + delete exp_names; + } + } + for (long i = 0, sz = exps->size (); i < sz; i++) + { + ArchiveExp *exp = exps->get (i); + exp->read_data (s_option); + } + + Vector <DbeFile*> *copy_files = new Vector<DbeFile*>(); + Vector <LoadObject*> *loadObjs = get_loadObjs (); + for (long i = 0, sz = VecSize(loadObjs); i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + if (strcmp (lo->get_pathname (), "LinuxKernel") == 0) + continue; + DbeFile *df = lo->dbeFile; + if ((df->filetype & DbeFile::F_FICTION) != 0) + continue; + if (df->get_location () == NULL) + { + copy_files->append (df); + continue; + } + if ((df->filetype & DbeFile::F_JAVACLASS) != 0) + { + if (df->container) + { // Found in .jar file + copy_files->append (df->container); + } + copy_files->append (df); + if ((s_option & ARCH_EXE_ONLY) != 0) + continue; + } + lo->sync_read_stabs (); + Elf *elf = lo->get_elf (); + if (elf && (lo->checksum != 0) && (lo->checksum != elf->elf_checksum ())) + { + if (!quiet) + fprintf (stderr, GTXT ("er_archive: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored\n"), + df->get_location ()); + continue; + } + copy_files->append (df); + if (elf) + { + Elf *f = elf->find_ancillary_files (lo->get_pathname ()); + if (f) + copy_files->append (f->dbeFile); + for (long i1 = 0, sz1 = VecSize(elf->ancillary_files); i1 < sz1; i1++) + { + Elf *ancElf = elf->ancillary_files->get (i1); + copy_files->append (ancElf->dbeFile); + } + } + Vector<Module*> *modules = lo->seg_modules; + for (long i1 = 0, sz1 = VecSize(modules); i1 < sz1; i1++) + { + Module *mod = modules->get (i1); + if ((mod->flags & MOD_FLAG_UNKNOWN) != 0) + continue; + else if ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0 && + !mod->isUsed) + continue; + if ((s_option & ARCH_ALL) != 0) + mod->read_stabs (false); // Find all Sources + if (mod->dot_o_file && mod->dot_o_file->dbeFile) + copy_files->append (mod->dot_o_file->dbeFile); + } + } + delete loadObjs; + + int bmask = DbeFile::F_LOADOBJ | DbeFile::F_JAVACLASS | DbeFile::F_JAR_FILE | + DbeFile::F_DOT_O | DbeFile::F_DEBUG_FILE; + if ((s_option & (ARCH_USED_SRC_ONLY | ARCH_ALL)) != 0) + { + bmask |= DbeFile::F_JAVA_SOURCE | DbeFile::F_SOURCE; + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i = 0, sz = VecSize(sources); i < sz; i++) + { + SourceFile *src = sources->get (i); + if ((src->flags & SOURCE_FLAG_UNKNOWN) != 0) + continue; + else if ((s_option & ARCH_USED_SRC_ONLY) != 0) + { + if ((src->dbeFile->filetype & DbeFile::F_JAVA_SOURCE) != 0 && + !src->isUsed) + continue; + } + if (src->dbeFile) + copy_files->append (src->dbeFile); + } + } + + Vector <DbeFile*> *notfound_files = new Vector<DbeFile*>(); + for (long i = 0, sz = VecSize(copy_files); i < sz; i++) + { + DbeFile *df = copy_files->get (i); + char *fnm = df->get_location (); + char *nm = df->get_name (); + Dprintf (DEBUG_ARCHIVE, + "%s::%d copy_files[%ld] filetype=%4d inArchive=%d '%s' --> '%s'\n", + get_basename (__FILE__), (int) __LINE__, i, + df->filetype, df->inArchive ? 1 : 0, STR (nm), STR (fnm)); + Dprintf (DEBUG_ARCHIVE && df->container, + " copy_files[%ld]: Found '%s' in '%s'\n", + i, STR (nm), STR (df->container->get_name ())); + if (fnm == NULL) + { + if (!quiet) + notfound_files->append (df); + continue; + } + else if (df->inArchive) + { + Dprintf (DEBUG_ARCHIVE, + " NOT COPIED: copy_files[%ld]: inArchive=1 '%s'\n", + i, STR (nm)); + continue; + } + else if ((df->filetype & bmask) == 0) + { + Dprintf (DEBUG_ARCHIVE, + " NOT COPIED: copy_files[%ld]: container=%p filetype=%d bmask=%d '%s'\n", + i, df->container, df->filetype, bmask, STR (nm)); + continue; + } + else if (df->container && + (df->filetype & (DbeFile::F_JAVA_SOURCE | DbeFile::F_SOURCE)) == 0) + { + Dprintf (DEBUG_ARCHIVE, + " NOT COPIED: copy_files[%ld]: container=%p filetype=%d bmask=%d '%s'\n", + i, df->container, df->filetype, bmask, STR (nm)); + continue; + } + else if (!mask_is_on (df->get_name ())) + { + Dprintf (DEBUG_ARCHIVE, + " NOT COPIED: copy_files[%ld]: mask is off for '%s'\n", + i, STR (nm)); + continue; + } + char *anm = founder_exp->getNameInArchive (nm, false); + if (force) + unlink (anm); + int res = founder_exp->copy_file (fnm, anm, quiet, common_archive_dir, use_relative_path); + if (0 == res) // file successfully archived + df->inArchive = 1; + delete anm; + } + delete copy_files; + + if (notfound_files->size () > 0) + { + for (long i = 0, sz = notfound_files->size (); i < sz; i++) + { + DbeFile *df = notfound_files->get (i); + fprintf (stderr, GTXT ("er_archive: Cannot find file: `%s'\n"), df->get_name ()); + } + fprintf (stderr, GTXT ("\n If you know the correct location of the missing file(s)" + " you can help %s to find them by manually editing the .gprofng.rc file." + " See %s man pages for more details.\n"), + whoami, whoami); + } + delete notfound_files; +} + +int +er_archive::check_args (int argc, char *argv[]) +{ + int opt; + int rseen = 0; + int dseen = 0; + // Parsing the command line + opterr = 0; + optind = 1; + static struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"whoami", required_argument, 0, 'w'}, + {"outfile", required_argument, 0, 'O'}, + {NULL, 0, 0, 0} + }; + while (1) + { + int option_index = 0; + opt = getopt_long (argc, argv, NTXT (":VFa:d:qnr:m:"), + long_options, &option_index); + if (opt == EOF) + break; + switch (opt) + { + case 'F': + force = 1; + break; + case 'd': // Common archive directory (absolute path) + if (rseen) + { + fprintf (stderr, GTXT ("Error: invalid combination of options: -r and -d are in conflict.\n")); + return -1; + } + if (dseen) + fprintf (stderr, GTXT ("Warning: option -d was specified several times. Last value is used.\n")); + free (common_archive_dir); + common_archive_dir = strdup (optarg); + dseen = 1; + break; + case 'q': + quiet = 1; + break; + case 'n': + descendant = 0; + break; + case 'r': // Common archive directory (relative path) + if (dseen) + { + fprintf (stderr, GTXT ("Error: invalid combination of options: -d and -r are in conflict.\n")); + return -1; + } + if (rseen) + fprintf (stderr, GTXT ("Warning: option -r was specified several times. Last value is used.\n")); + free (common_archive_dir); + common_archive_dir = strdup (optarg); + use_relative_path = 1; + rseen = 1; + break; + case 'a': + if (strcmp (optarg, "off") == 0) + s_option = ARCH_NOTHING; + else if (strcmp (optarg, "on") == 0 || + strcmp (optarg, "ldobjects") == 0) + s_option = ARCH_EXE_ONLY; + else if (strcmp (optarg, "usedldobjects") == 0) + s_option = ARCH_USED_EXE_ONLY; + else if (strcmp (optarg, "usedsrc") == 0) + s_option = ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY; + else if (strcmp (optarg, "all") == 0 || strcmp (optarg, "src") == 0) + s_option = ARCH_ALL; + else + { + fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"), + optopt, optarg); + return -1; + } + break; + case 'm': + { + regex_t *regex_desc = new regex_t (); + if (regcomp (regex_desc, optarg, REG_EXTENDED | REG_NOSUB | REG_NEWLINE)) + { + delete regex_desc; + fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"), + optopt, optarg); + return -1; + } + if (mask == NULL) + mask = new Vector<regex_t *>(); + mask->append (regex_desc); + break; + } + case 'O': + { + int fd = open (optarg, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) + { + fprintf (stderr, GTXT ("er_archive: Can't open %s: %s\n"), + optarg, strerror (errno)); + break; + } + if (dup2 (fd, 2) == -1) + { + close (fd); + fprintf (stderr, GTXT ("er_archive: Can't divert stderr: %s\n"), + strerror (errno)); + break; + } + if (dup2 (fd, 1) == -1) + { + close (fd); + fprintf (stderr, GTXT ("er_archive: Can't divert stdout: %s\n"), + strerror (errno)); + break; + } + close (fd); + struct timeval tp; + gettimeofday (&tp, NULL); + fprintf (stderr, "### Start %s#", ctime (&tp.tv_sec)); + for (int i = 0; i < argc; i++) + fprintf (stderr, " %s", argv[i]); + fprintf (stderr, "\n"); + break; + } + case 'V': +// Ruud + Application::print_version_info (); +/* + printf (GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION); +*/ + exit (0); + case 'w': + whoami = optarg; + break; + case 'h': + usage (); + exit (0); + case ':': // -s -m without operand + fprintf (stderr, GTXT ("Option -%c requires an operand\n"), optopt); + return -1; + case '?': + default: + fprintf (stderr, GTXT ("Unrecognized option: -%c\n"), optopt); + return -1; + } + } + return optind; +} + +void +er_archive::check_env_var () +{ + char *ename = NTXT ("GPROFNG_ARCHIVE"); + char *var = getenv (ename); + if (var == NULL) + return; + var = dbe_strdup (var); + Vector<char*> *opts = new Vector<char*>(); + opts->append (ename); + for (char *s = var;;) + { + while (*s && isblank (*s)) + s++; + if (*s == 0) + break; + opts->append (s); + while (*s && !isblank (*s)) + s++; + if (*s == 0) + break; + *s = 0; + s++; + } + if (opts->size () > 0) + { + char **arr = (char **) malloc (sizeof (char *) *opts->size ()); + for (long i = 0; i < opts->size (); i++) + arr[i] = opts->get (i); + if (-1 == check_args (opts->size (), arr)) + fprintf (stderr, GTXT ("Error: Wrong SP_ER_ARCHIVE: '%s'\n"), var); + free (arr); + } + delete opts; + free (var); +} + +static int +real_main (int argc, char *argv[]) +{ + er_archive *archive = new er_archive (argc, argv); + dbeSession->archive_mode = 1; + archive->start (argc, argv); + dbeSession->unlink_tmp_files (); + return 0; +} + +/** + * Call catch_out_of_memory(int (*real_main)(int, char*[]), int argc, char *argv[]) which will call real_main() + * @param argc + * @param argv + * @return + */ +int +main (int argc, char *argv[]) +{ + return catch_out_of_memory (real_main, argc, argv); +} diff --git a/gprofng/src/gp-archive.h b/gprofng/src/gp-archive.h new file mode 100644 index 0000000..1d937e0 --- /dev/null +++ b/gprofng/src/gp-archive.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _GP_ARCHIVE_H_ +#define _GP_ARCHIVE_H_ + +#include <regex.h> +#include "DbeApplication.h" + +class ArchiveExp; +class LoadObject; +template <class ITEM> class Vector; + +enum +{ + ARCH_NOTHING = 0, + ARCH_EXE_ONLY = 1, + ARCH_USED_EXE_ONLY = 2, + ARCH_USED_SRC_ONLY = 4, + ARCH_ALL = 8 +}; + +class er_archive : public DbeApplication +{ +public: + er_archive (int argc, char *argv[]); + ~er_archive (); + void start (int argc, char *argv[]); + +private: + void usage (); + int check_args (int argc, char *argv[]); + int clean_old_archive (char *expname, ArchiveExp *founder_exp); + int mask_is_on (const char *str); + void check_env_var (); + Vector <LoadObject*> *get_loadObjs (); + + Vector<regex_t *> *mask; // -m <regexp> + int s_option; // -s NO|ALL|USED + char *common_archive_dir; // -d // absolute path to common archive + int force; // -F + int quiet; // -q + int descendant; // -n + int use_relative_path; // -r +}; + +#endif
\ No newline at end of file diff --git a/gprofng/src/gp-collect-app.cc b/gprofng/src/gp-collect-app.cc new file mode 100644 index 0000000..afaae70 --- /dev/null +++ b/gprofng/src/gp-collect-app.cc @@ -0,0 +1,1598 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <ctype.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <strings.h> +#include <string.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/wait.h> +#include <sys/utsname.h> +#include <fcntl.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#include <sys/ptrace.h> + +#include "gp-defs.h" +#include "cpu_frequency.h" +#include "util.h" +#include "collctrl.h" +#include "hwcdrv.h" +#include "gp-experiment.h" +#include "collect.h" +#include "StringBuilder.h" + +#define SP_COLLECTOR_FOUNDER "SP_COLLECTOR_FOUNDER" + +extern char **environ; + +static volatile int interrupt = 0; +static int saved_stdout = -1; +static int saved_stderr = -1; +static int no_short_usage = 0; +static int usage_fd = 2; +static collect *collect_obj = NULL; +extern "C" void sigint_handler (int sig, siginfo_t *info, void *context); +static char *outredirect = NULL; +static int precheck; +static int nprocesses; +static Process **processes; + +int +main (int argc, char *argv[]) +{ + // disable any alarm that might be pending + int r = alarm (0); + if (r != 0) + dbe_write (2, GTXT ("collect has alarm(%d) pending\n"), r); + collect_obj = new collect (argc, argv, environ); + collect_obj->start (argc, argv); + delete collect_obj; + return 0; +} + +extern "C" void +sigint_handler (int, siginfo_t *, void *) +{ + interrupt = 1; + if (collect_obj->cc != NULL) + collect_obj->cc->interrupt (); + return; +} + +extern "C" void +sigalrm_handler (int, siginfo_t *, void *) +{ + dbe_write (2, GTXT ("collect: unexpected alarm clock signal received\n")); + return; +} + +extern "C" void +sigterm_handler (int, siginfo_t *, void *) +{ + for (int i = 0; i < nprocesses; i++) + { + Process *proc = processes[i]; + if (proc != NULL) + kill (proc->pid, SIGTERM); + } +} + +collect::collect (int argc, char *argv[], char **envp) +: Application (argc, argv) +{ + verbose = 0; + disabled = 0; + cc = NULL; + collect_warnings = NULL; + collect_warnings_idx = 0; + int ii; + for (ii = 0; ii < MAX_LD_PRELOAD_TYPES; ii++) + sp_preload_list[ii] = NULL; + for (ii = 0; ii < MAX_LD_PRELOAD_TYPES; ii++) + sp_libpath_list[ii] = NULL; + java_path = NULL; + java_how = NULL; + jseen_global = 0; + nlabels = 0; + origargc = argc; + origargv = argv; + origenvp = envp; + mem_so_me = false; +} + +collect::~collect () +{ + delete cc; +} + +struct sigaction old_sigint_handler; +struct sigaction old_sigalrm_handler; + +void +collect::start (int argc, char *argv[]) +{ + char *ccret; + char *extype; + /* create a collector control structure, disabling aggressive warning */ + cc = new Coll_Ctrl (0, false, false); + if (prog_name) + { + char *s = strrchr (prog_name, '/'); + if (s && (s - prog_name) > 5) // Remove /bin/ + { + s = dbe_sprintf (NTXT ("%.*s"), (int) (s - prog_name - 4), prog_name); + cc->set_project_home (s); + free (s); + } + } + char * errenable = cc->enable_expt (); + if (errenable) + { + writeStr (2, errenable); + free (errenable); + } + + /* install a handler for SIGALRM */ + struct sigaction act; + memset (&act, 0, sizeof (struct sigaction)); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigalrm_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGALRM, &act, &old_sigalrm_handler) == -1) + { + writeStr (2, GTXT ("Unable to install SIGALRM handler\n")); + exit (-1); + } + + /* install a handler for SIGINT */ + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigint_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGINT, &act, &old_sigint_handler) == -1) + { + writeStr (2, GTXT ("Unable to install SIGINT handler\n")); + exit (-1); + } + + /* install a handler for SIGTERM */ + sigemptyset (&act.sa_mask); + act.sa_sigaction = sigterm_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGTERM, &act, NULL) == -1) + { + writeStr (2, GTXT ("Unable to install SIGTERM handler\n")); + exit (-1); + } + if (argc > 1 && strncmp (argv[1], NTXT ("--whoami="), 9) == 0) + { + whoami = argv[1] + 9; + argc--; + argv++; + } + + /* check for no arguments -- usage message */ + if (argc == 1) + { + verbose = 1; + usage_fd = 1; + validate_config (0); + usage (); + exit (0); + } + else if (argc == 2 && strcmp (argv[1], NTXT ("-h")) == 0) + { + /* only one argument, -h */ + verbose = 1; + validate_config (0); + /* now print the HWC usage message */ + show_hwc_usage (); + exit (0); + } + else if (argc == 2 && (strcmp (argv[1], NTXT ("-help")) == 0 || + strcmp (argv[1], NTXT ("--help")) == 0)) + { + /* only one argument, -help or --help */ + verbose = 1; + usage_fd = 1; + validate_config (0); + usage (); + exit (0); + } +// Ruud + else if ((argc == 2) && + (strcmp (argv[1], NTXT ("--version")) == 0)) + { + /* only one argument, --version */ + + /* print the version info */ + Application::print_version_info (); + exit (0); + } + + /* precheck the arguments -- scan for -O, -M flagS */ + precheck = 1; + targ_index = check_args (argc, argv); + if (targ_index < 0) + { + /* message has already been written */ + usage_fd = 2; + short_usage (); + exit (1); + } + /* crack the arguments */ + precheck = 0; + targ_index = check_args (argc, argv); + if (targ_index <= 0) + { + /* message has already been written */ + usage_fd = 2; + short_usage (); + exit (1); + } + if (targ_index != 0) + check_target (argc, argv); + if (disabled != 0 && cc->get_count () == 0) + { + // show collection parameters; count data + ccret = cc->show (0); + writeStr (1, ccret); + } + + // see if Java version should be checked + if (cc->get_java_default () == 0 && java_path != NULL) + validate_java (java_path, java_how, verbose); + + /* if count data is requested, exec bit to do the real work */ + /* even for a dryrun */ + if (cc->get_count () != 0) + get_count_data (); + + /* if a dry run, just exit */ + if (disabled != 0) + { + writeStr (1, cc->show_expt ()); + StringBuilder sb; + sb.append (GTXT ("Exec argv[] = ")); + for (int i = 0; i < nargs; i++) + sb.appendf (NTXT ("%s "), arglist[i]); + sb.append (NTXT ("\n")); + char *s = sb.toString (); + writeStr (1, s); + free (s); + exit (0); + } + + // If the mem_so_me flag is set, preload mem.so + // and launch the process + if (mem_so_me) + { + /* set env vars for mem.so */ + if (putenv_memso () != 0) + exit (1); /* message has already been written */ + /* ensure original outputs restored for target */ + reset_output (); + + /* now exec the target ... */ + if (cc->get_debug_mode () == 1) + { + traceme (arglist[0], arglist); + extype = NTXT ("traceme"); + } + else + { + execvp (arglist[0], arglist); + extype = NTXT ("exevcp"); + } + /* oops, exec of the target failed */ + char *em = strerror (errno); + set_output (); /* restore output for collector */ + if (em == NULL) + dbe_write (2, GTXT ("memso %s of %s failed: errno = %d\n"), extype, argv[targ_index], errno); + else + dbe_write (2, GTXT ("memso %s of %s failed: %s\n"), extype, argv[targ_index], em); + exit (1); + } + + /* normal path, setting up an experiment and launching the target */ + /* set up the experiment */ + ccret = cc->setup_experiment (); + if (ccret != NULL) + { + dbe_write (2, NTXT ("%s\n"), ccret); + free (ccret); + exit (1); + } + /* Beyond this point, the experiment is created */ + if (collect_warnings != NULL) + { + warn_open (); + for (int i = 0; i < collect_warnings_idx; i++) + warn_comment (SP_JCMD_CWARN, COL_WARN_APP_NOT_READY, collect_warnings[i], (int) strlen (collect_warnings[i])); + warn_close (); + } + /* check cpu frequency variation for intel*/ + unsigned char mode = COL_CPUFREQ_NONE; + int max_freq = get_cpu_frequency (&mode); + char freq_scaling[256]; + char turbo_mode[256]; + *freq_scaling = 0; + *turbo_mode = 0; + if (mode & COL_CPUFREQ_SCALING) + snprintf (freq_scaling, sizeof (freq_scaling), NTXT (" frequency_scaling=\"enabled\"")); + if (mode & COL_CPUFREQ_TURBO) + snprintf (turbo_mode, sizeof (turbo_mode), NTXT (" turbo_mode=\"enabled\"")); + if (mode != COL_CPUFREQ_NONE) + { + warn_open (); + if (warn_file != NULL) + { + warn_write ("<powerm>\n<frequency clk=\"%d\"%s%s/>\n</powerm>\n", + max_freq, freq_scaling, turbo_mode); + warn_close (); + } + } + + /* check for labels to write to notes file */ + if (nlabels != 0) + { + char *nbuf; + char nbuf2[MAXPATHLEN]; + // fetch the experiment name and CWD + char *exp = cc->get_experiment (); + char *ev = getcwd (nbuf2, sizeof (nbuf2)); + + // format the environment variable for the experiment directory name + if (ev != NULL && exp[0] != '/') + // cwd succeeded, and experiment is a relative path + nbuf = dbe_sprintf (NTXT ("%s/%s/%s"), nbuf2, exp, SP_NOTES_FILE); + else + // getcwd failed or experiment is a fullpath + nbuf = dbe_sprintf (NTXT ("%s/%s"), exp, SP_NOTES_FILE); + + FILE *f = fopen (nbuf, NTXT ("w")); + free (nbuf); + if (f != NULL) + { + for (int i = 0; i < nlabels; i++) + fprintf (f, NTXT ("%s\n"), label[i]); + fclose (f); + } + } + /* check for user interrupt */ + if (interrupt == 1) + { + cc->delete_expt (); + writeStr (2, GTXT ("User interrupt\n")); + exit (0); + } + + /* print data-collection parameters */ + if (verbose) + { + ccret = cc->show (0); + if (ccret != NULL) + writeStr (2, ccret); + } + ccret = cc->show_expt (); + if (ccret != NULL) + writeStr (1, ccret); /* write this to stdout */ + + pid_t pid = (pid_t) cc->get_attach_pid (); + if (pid == (pid_t) 0) + { + /* No attach */ + /* Set the environment for libcollector */ + if (putenv_libcollector () != 0) + { + /* message has already been written */ + cc->delete_expt (); + exit (1); + } + /* ensure original output fds restored for target */ + reset_output (); + + /* now exec the target ... */ + if (cc->get_debug_mode () == 1) + { + traceme (arglist[0], arglist); + extype = NTXT ("traceme"); + } + else + { + execvp (arglist[0], arglist); + extype = NTXT ("execvp"); + } + + /* we reach this point only if the target launch failed */ + char *em = strerror (errno); + + /* restore output for collector */ + set_output (); + + /* exec failed; delete experiment */ + cc->delete_expt (); + + /* print a message and exit */ + if (em == NULL) + dbe_write (2, GTXT ("%s of %s failed: errno = %d\n"), extype, argv[targ_index], errno); + else + dbe_write (2, GTXT ("%s of %s failed: %s\n"), extype, argv[targ_index], em); + exit (1); + } + else + abort (); +} + +/** + * Prepare a warning message and pass it to warn_write() + * @Parameters: + * kind Type of comment + * num ID + * s Comment sting + * len Length of the string + * @Return: none. + */ +void +collect::warn_comment (const char *kind, int num, char *s, int len) +{ + if (len != 0) + warn_write (NTXT ("<event kind=\"%s\" id=\"%d\">%.*s</event>\n"), + kind, num, len, s); + else if (s == NULL) + warn_write (NTXT ("<event kind=\"%s\" id=\"%d\"/>\n"), kind, num); + else + warn_write (NTXT ("<event kind=\"%s\" id=\"%d\">%s</event>\n"), kind, num, s); +} + +/** + * Open the warnings file in Append mode ("aw") + */ +void +collect::warn_open () +{ + // open the warnings file + warnfilename = dbe_sprintf (NTXT ("%s/%s"), cc->get_experiment (), SP_WARN_FILE); + int fd = open (warnfilename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + warn_file = fdopen (fd, NTXT ("aw")); +} + +/** + * Close the warnings file + */ +void +collect::warn_close () +{ + (void) fclose (warn_file); +} + +/** + * Format the warning message and write it to the warnings file + */ +void +collect::warn_write (const char *format, ...) +{ + char buf[4096]; + // format the input arguments into a string + va_list va; + va_start (va, format); + vsnprintf (buf, sizeof (buf), format, va); + va_end (va); + // write it to the warnings file (warnings.xml) + fwrite (buf, 1, strlen (buf), warn_file); + fflush (warn_file); +} + +/* process the args, setting expt. params, + * and finding offset for a.out name + */ +int +collect::check_args (int argc, char *argv[]) +{ + int hseen = 0; + int hoffseen = 0; + int lseen = 0; + int tseen = 0; + int pseen = 0; + int sseen = 0; + int yseen = 0; + int Fseen = 0; + int Aseen = 0; + int Sseen = 0; + int Hseen = 0; + int iseen = 0; + int Jseen = 0; + int ofseen = 0; + char *expName = NULL; + bool overwriteExp = false; + char *ccret; + char *ccwarn; + for (targ_index = 1; targ_index < argc; targ_index++) + { + if (argv[targ_index] == NULL) + break; + if (dbe_strcmp (argv[targ_index], "--") == 0) + { + targ_index++; + break; + } + if (argv[targ_index][0] != '-') + break; + int param; + switch (argv[targ_index][1]) + { + case 'y': + { + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + char *ptr; + int resume = 1; + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (yseen != 0) + { + dupflagseen ('y'); + return -1; + } + yseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-y requires a signal argument\n")); + return -1; + } + if ((ptr = strrchr (argv[targ_index], ',')) != NULL) + { + if ((*(ptr + 1) != 'r') || (*(ptr + 2) != 0)) + { + /* not the right trailer */ + dbe_write (2, GTXT ("Invalid delay signal %s\n"), argv[targ_index]); + return -1; + } + resume = 0; + *ptr = 0; + } + param = cc->find_sig (argv[targ_index]); + if (param < 0) + { + /* invalid signal */ + dbe_write (2, GTXT ("Invalid delay signal %s\n"), argv[targ_index]); + return -1; + } + ccret = cc->set_pauseresume_signal (param, resume); + if (ccret != NULL) + { + /* invalid signal; write message */ + writeStr (2, ccret); + return -1; + } + break; + } + case 'l': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (lseen != 0) + { + dupflagseen ('l'); + return -1; + } + lseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-l requires a signal argument\n")); + return -1; + } + param = cc->find_sig (argv[targ_index]); + if (param < 0) + { + /* invalid signal */ + dbe_write (2, GTXT ("Invalid sample signal %s\n"), argv[targ_index]); + return -1; + } + ccret = cc->set_sample_signal (param); + if (ccret != NULL) + { + /* invalid signal; write message */ + writeStr (2, ccret); + free (ccret); + return -1; + } + break; + +#ifdef GPROFNG_DOES_NOT_SUPPORT + case 'P': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (Pseen != 0) + { + dupflagseen ('P'); + return -1; + } + Pseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-P requires a process pid argument\n")); + return -1; + } + ccret = cc->set_attach_pid (argv[targ_index]); + if (ccret != NULL) + { + /* error; write message */ + writeStr (2, ccret); + free (ccret); + return -1; + } + break; +#endif + case 't': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (tseen != 0) + { + dupflagseen ('t'); + return -1; + } + tseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-t requires a run-duration argument\n")); + return -1; + } + ccret = cc->set_time_run (argv[targ_index]); + if (ccret != NULL) + { + /* error; write message */ + writeStr (2, ccret); + free (ccret); + return -1; + } + break; + case 'p': + { + char *warnmsg; + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (pseen != 0) + { + dupflagseen ('p'); + return -1; + } + pseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-p requires a clock-profiling argument\n")); + return -1; + } + ccret = cc->set_clkprof (argv[targ_index], &warnmsg); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + if (warnmsg != NULL) + { + writeStr (2, warnmsg); + free (warnmsg); + } + break; + } + case 's': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (sseen != 0) + { + dupflagseen ('s'); + return -1; + } + sseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-s requires a synchronization-tracing argument\n")); + return -1; + } + ccret = cc->set_synctrace (argv[targ_index]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + break; + case 'h': + { + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + targ_index++; + if ((argv[targ_index] == NULL) || (strlen (argv[targ_index]) == 0)) + { + writeStr (2, GTXT ("-h requires a HW-counter-profiling argument\n")); + return -1; + } + // Check for some special cases + char * string = argv[targ_index]; + if (strcmp (argv[targ_index], NTXT ("off")) == 0) + { + if (hseen != 0) + { + no_short_usage = 1; + writeStr (2, GTXT ("-h off cannot be used with any other -h arguments\n")); + return -1; + } + hoffseen = 1; + hseen = 1; + cc->disable_hwc (); + break; + } + // Check to see if we can use HWC + unsigned hwc_maxregs = hwc_get_max_concurrent (false); + if (hwc_maxregs == 0) + { + char buf[1024]; + char *pch = hwcfuncs_errmsg_get (buf, sizeof (buf), 0); + if (*pch) + dbe_write (2, GTXT ("HW counter profiling is not supported on this system: %s%s"), + pch, pch[strlen (pch) - 1] == '\n' ? "" : "\n"); + else + dbe_write (2, GTXT ("HW counter profiling is not supported on this system\n")); + no_short_usage = 1; + return -1; + } + // Make sure there's no other -h after -h off + if (hoffseen != 0) + { + no_short_usage = 1; + writeStr (2, GTXT ("No -h arguments can be used after -h off\n")); + return -1; + } + // set up to process HW counters (to know about default counters) + cc->setup_hwc (); + hseen++; + char *warnmsg; + if (strcmp (argv[targ_index], NTXT ("on")) == 0) + ccret = cc->add_default_hwcstring ("on", &warnmsg, true); + else if (strcmp (argv[targ_index], NTXT ("hi")) == 0 || + strcmp (argv[targ_index], NTXT ("high")) == 0) + ccret = cc->add_default_hwcstring ("hi", &warnmsg, true); + else if (strcmp (argv[targ_index], NTXT ("lo")) == 0 || + strcmp (argv[targ_index], NTXT ("low")) == 0) + ccret = cc->add_default_hwcstring ("lo", &warnmsg, true); + else if (strcmp (argv[targ_index], NTXT ("auto")) == 0) + ccret = cc->add_default_hwcstring ("auto", &warnmsg, true); + else + ccret = cc->add_hwcstring (string, &warnmsg); + if (ccret != NULL) + { + /* set global flag to suppress the short_usage message for any subsequent HWC errors */ + no_short_usage = 1; + writeStr (2, ccret); + free (ccret); + return -1; + } + if (warnmsg != NULL) + { + writeStr (2, warnmsg); + free (warnmsg); + } + break; + } + case 'O': + overwriteExp = true; + __attribute__ ((fallthrough)); + case 'o': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s must be followed by a file name\n"), + argv[targ_index]); + return -1; + } + if (expName != NULL) + { + dbe_write (2, GTXT ("Only one -o or -O argument may be used\n")); + dupflagseen ('o'); + return -1; + } + expName = argv[targ_index + 1]; + targ_index++; + break; + case 'S': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s must be followed by a sample interval name\n"), + argv[targ_index]); + return -1; + } + if (Sseen != 0) + { + dupflagseen ('S'); + return -1; + } + Sseen++; + ccret = cc->set_sample_period (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'H': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a heap-tracing argument\n"), + argv[targ_index]); + return -1; + } + if (Hseen != 0) + { + dupflagseen ('H'); + return -1; + } + Hseen++; + ccret = cc->set_heaptrace (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + if (cc->get_java_default () == 1) + cc->set_java_mode (NTXT ("off")); + targ_index++; + break; + case 'i': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + fprintf (stderr, GTXT ("Argument %s requires an I/O-tracing argument\n"), + argv[targ_index]); + return -1; + } + if (iseen != 0) + { + dupflagseen ('i'); + return -1; + } + iseen++; + ccret = cc->set_iotrace (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'j': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a java-profiling argument\n"), + argv[targ_index]); + return -1; + } + if (jseen_global != 0) + { + dupflagseen ('j'); + return -1; + } + jseen_global++; + ccret = cc->set_java_mode (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'J': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a java argument\n"), + argv[targ_index]); + return -1; + } + if (Jseen != 0) + { + dupflagseen ('J'); + return -1; + } + Jseen++; + ccret = cc->set_java_args (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'F': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a descendant-following argument\n"), + argv[targ_index]); + return -1; + } + if (Fseen != 0) + { + dupflagseen ('F'); + return -1; + } + Fseen++; + ccret = cc->set_follow_mode (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'a': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a load-object archiving argument\n"), + argv[targ_index]); + return -1; + } + if (Aseen != 0) + { + dupflagseen ('a'); + return -1; + } + Aseen++; + ccret = cc->set_archive_mode (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'C': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s must be followed by a comment\n"), + argv[targ_index]); + return -1; + } + if (nlabels == MAXLABELS) + { + dbe_write (2, GTXT ("No more than %d comments may be specified\n"), + MAXLABELS); + return -1; + } + label[nlabels] = argv[targ_index + 1]; + nlabels++; + targ_index++; + break; + case 'n': + case 'v': + case 'V': + if (precheck == 1) + break; + do_flag (&argv[targ_index][1]); + break; + case 'Z': + // special undocumented argument for debug builds only to allow analyzer to + // LD_PRELOAD mem.so for the target it spawns + mem_so_me = true; + break; + case '-': + if (strcmp (argv[targ_index], NTXT ("--verbose")) == 0) + do_flag ("v"); + else if (strcmp (argv[targ_index], "--outfile") == 0) + { + if (precheck == 0) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + // process this argument now + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a file argument\n"), + argv[targ_index]); + return -1; + } + if (ofseen != 0) + { + dupflagseen (argv[targ_index]); + return -1; + } + ofseen++; + if (outredirect == NULL) + { + outredirect = argv[targ_index + 1]; + set_output (); + } // else already redirected; ignore with no message + targ_index++; + } + else + { + dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), argv[targ_index]); + return -1; + } + break; + default: + dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), argv[targ_index]); + return -1; + } + } + if (targ_index >= argc) + return -1; + if (argv[targ_index] == NULL) + { + if (precheck == 1) + return 0; + if (cc->get_attach_pid () != 0) /* no target is OK, if we're attaching */ + return 0; + writeStr (2, GTXT ("Name of target must be specified\n")); + return -1; + } + if (expName) + { + ccwarn = NULL; + ccret = cc->set_expt (expName, &ccwarn, overwriteExp); + if (ccwarn) + { + writeStr (2, ccwarn); + free (ccwarn); + } + if (ccret) + { + writeStr (2, ccret); + return -1; + } + } + if (cc->get_attach_pid () != 0) + { + writeStr (2, GTXT ("Name of target must not be specified when -P is used\n")); + return -1; + } + return targ_index; +} + +int +collect::checkflagterm (const char *c) +{ + if (c[2] != 0) + { + dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), c); + return -1; + } + return 0; +} + +int +collect::do_flag (const char *flags) +{ + char *s; + for (int i = 0;; i++) + { + switch (flags[i]) + { + case 0: // end of string + return 0; + case 'n': + disabled = 1; + if (verbose != 1) + { +// Ruud + Application::print_version_info (); +/* + dbe_write (2, NTXT ("GNU %s version %s\n"), + get_basename (prog_name), VERSION); +*/ + verbose = 1; + } + break; + case 'x': + s = cc->set_debug_mode (1); + if (s) + { + writeStr (2, s); + free (s); + } + break; + case 'v': + if (verbose != 1) + { +// Ruud + Application::print_version_info (); +/* + dbe_write (2, NTXT ("GNU %s version %s\n"), + get_basename (prog_name), VERSION); +*/ + verbose = 1; + } + break; + case 'V': +// Ruud + Application::print_version_info (); +/* + dbe_write (2, NTXT ("GNU %s version %s\n"), + get_basename (prog_name), VERSION); +*/ + /* no further processing.... */ + exit (0); + } + } +} + +/* + * traceme - cause the caller to stop at the end of the next exec() + * so that a debugger can attach to the new program + * + * Takes same arguments as execvp() + */ +int +collect::traceme (const char *execvp_file, char *const execvp_argv[]) +{ + int ret = -1; + pid_t pid = fork (); + if (pid == 0) + { // child + // child will set up itself to be PTRACE'd, and then exec the target executable + /* reset the SP_COLLECTOR_FOUNDER value to the new pid */ + pid_t mypid = getpid (); + char *ev = dbe_sprintf (NTXT ("%s=%d"), SP_COLLECTOR_FOUNDER, mypid); + if (putenv (ev) != 0) + { + dbe_write (2, GTXT ("fork-child: Can't putenv of \"%s\": run aborted\n"), ev); + return 1; + } + ptrace (PTRACE_TRACEME, 0, NULL, NULL); // initiate trace + ret = execvp (execvp_file, execvp_argv); // execvp user command + return ret; // execvp failed + } + else if (pid > 0) + { // parent + int status; + if (waitpid (pid, &status, 0) != pid) + { // wait for execvp to cause signal + writeStr (2, GTXT ("parent waitpid() failed\n")); + return -2; + } + if (!WIFSTOPPED (status)) + writeStr (2, GTXT ("WIFSTOPPED(status) failed\n")); + + // originally, PTRACE_DETACH would send SIGTSTP, but now we do it here: + if (kill (pid, SIGTSTP) != 0) + writeStr (2, GTXT ("kill(pid, SIGTSTP) failed\n")); + if (ptrace (PTRACE_DETACH, pid, NULL, 0) != 0) + { // detach trace + writeStr (2, GTXT ("ptrace(PTRACE_DETACH) failed\n")); + return -4; + } + dbe_write (2, GTXT ("Waiting for attach from debugger: pid=%d\n"), (int) pid); + + // wait for an external debugger to attach + if (waitpid (pid, &status, 0) != pid) + { // keep parent alive until child quits + writeStr (2, GTXT ("parent final waitpid() failed\n")); + return -5; + } + } + else + return -1; // fork failed + exit (0); +} + +void +collect::dupflagseen (char c) +{ + dbe_write (2, GTXT ("Only one -%c argument may be used\n"), c); +} + +void +collect::dupflagseen (const char *s) +{ + dbe_write (2, GTXT ("Only one %s argument may be used\n"), s); +} + +int +collect::set_output () +{ + static int initial = 1; + if (outredirect) + { + int fd = open (outredirect, O_WRONLY | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) + { + dbe_write (2, GTXT ("Warning: Can't open collector output `%s': %s\n"), + outredirect, strerror (errno)); + } + else + { + if ((saved_stdout = dup (1)) == -1 || dup2 (fd, 1) == -1) + dbe_write (2, GTXT ("Warning: Can't divert collector %s: %s\n"), + NTXT ("stdout"), strerror (errno)); + if ((saved_stderr = dup (2)) == -1 || dup2 (fd, 2) == -1) + dbe_write (2, GTXT ("Warning: Can't divert collector %s: %s\n"), + NTXT ("stderr"), strerror (errno)); + close (fd); + if ((saved_stdout != -1) && (saved_stderr != -1)) + { + if (initial) + { + struct timeval tp; + gettimeofday (&tp, NULL); + writeStr (2, ctime (&tp.tv_sec)); + initial = 0; + } + return 1; // diversion in place + } + } + } + return 0; // no diversion +} + +void +collect::reset_output () +{ + if (saved_stdout != -1 && + (dup2 (saved_stdout, 1) == -1 || close (saved_stdout))) + dbe_write (2, GTXT ("Warning: Can't restore collector stdout: %s\n"), + strerror (errno)); + if (saved_stderr != -1 && + (dup2 (saved_stderr, 2) == -1 || close (saved_stderr))) + dbe_write (2, GTXT ("Warning: Can't restore collector stderr: %s\n"), + strerror (errno)); +} + +void +collect::usage () +{ + +/* + Ruud - Isolate this line because it has an argument. Otherwise it would be at the + end of this long list. +*/ + printf ( GTXT ( + "Usage: gprofng collect app [OPTION(S)] TARGET [TARGET_ARGUMENTS]\n")), + +/* +------------------------------------------------------------------------------- + For a reason I don't understand, the continuation line(s) need to start at + column 26 in order for help2man to do the righ thing. Ruud +------------------------------------------------------------------------------- +*/ + printf ( GTXT ( + "\n" + "Collect performance data on the target program. In addition to Program\n" + "Counter PC) sampling, hardware event counters and various tracing options\n" + "are supported.\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + " -p {off|on|lo|hi|<value>} disable (off) or enable (on) clock-profiling using a default\n" + " sampling granularity, or enable clock-profiling implicitly by\n" + " setting the sampling granularity (lo, hi, or a specific value\n" + " in ms); by default clock profiling is enabled.\n" + "\n" + " -h {<ctr_def>...,<ctr_n_def>} enable hardware event counter profiling and select\n" + " the counter(s); to see the supported counters on this system use\n" + " the -h option without other arguments.\n" + "\n" + " -o <exp_name> specify the name for (and path to) the experiment directory; the\n" + " the default path is the current directory.\n" + "\n" + " -O <exp_name> the same as -o, but unlike the -o option, silently overwrite an\n" + " existing experiment directory with the same name.\n" + "\n" + " -C <label> add up to 10 comment labels to the experiment; comments appear in\n" + " the notes section of the header.\n" + "\n" + " -j {on|off|<path>} enable (on), or disable (off) Java profiling when the target\n" + " program is a JVM; optionally set the <path> to a non-default JVM;\n" + " the default is \"-j on\".\n" + "\n" + " -J <java-args> specify arguments to the JVM.\n" + "\n" + " -t <duration>[m|s] specify the duration over which to record data; the default unit\n" + " is seconds (s), but can be set to minutes (m).\n" + "\n" + " -n dry run; display several run-time settings, but do not run the\n" + " target, or collect performance data.\n" + "\n" + " -y <signal>[,r] specify delayed initialization and a pause/resume signal; by default\n" + " the target starts in paused mode; if the optional r keyword is\n" + " provided, start in resumed mode.\n" + "\n" + " -F {off|on|=<regex>} control to follow descendant processes; disable (off), enable (on),\n" + " or collect data on all descendant processes whose name matches the\n" + " specified regular expression; the default is \"-F on\".\n" + "\n" + " -a {off|on|ldobjects|src|usedldobjects|usedsrc} specify archiving of binaries and other files;\n" + " in addition to disable this feature (off), or enable archiving off all\n" + " loadobjects and sources (on), the other options support a more\n" + " refined selection. All of these options enable archiving, but the\n" + " keyword controls what exactly is selected: all load objects (ldobjects),\n" + " all source files (src), the loadobjects asscoiated with a program counter\n" + " (usedldobjects), or the source files associated with a program counter\n" + " (usedsrc); the default is \"-a ldobjects\".\n" + "\n" + " -S {off|on|<seconds>} disable (off) or enable (on) periodic sampling of process-wide resource\n" + " utilization; by default sampling occurs every second; use the <seconds>\n" + " option to change this; the default is \"-S on\".\n" + "\n" + " -l <signal> specify a signal that will trigger a sample of process-wide resource utilization.\n" + "\n" + " -s <option>[,<API>] enable synchronization wait tracing; <option> is used to define the specifics\n" + " of the tracing (on, off, <threshold>, or all); <API> is used to select the API:\n" + " \"n\" selects native/Pthreads, \"j\" selects Java, and \"nj\" selects both;\n" + " the default is \"-s off\".\n" + "\n" + " -H {off|on} disable (off), or enable (on) heap tracing; the default is \"-H off\".\n" + "\n" + " -i {off|on} disable (off), or enable (on) I/O tracing; the default is \"-i off\".\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gprofng(1), gp-archive(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n")); +/* + char *s = dbe_sprintf (GTXT ("Usage: %s <args> target <target-args>\n"), + whoami); + writeStr (usage_fd, s); + free (s); + writeStr (usage_fd, GTXT (" -p {lo|on|hi|off|<value>}\tspecify clock-profiling\n")); + writeStr (usage_fd, GTXT ("\t`lo' per-thread rate of ~10 samples/second\n")); + writeStr (usage_fd, GTXT ("\t`on' per-thread rate of ~100 samples/second (default)\n")); + writeStr (usage_fd, GTXT ("\t`hi' per-thread rate of ~1000 samples/second\n")); + writeStr (usage_fd, GTXT ("\t`off' disable clock profiling\n")); + writeStr (usage_fd, GTXT ("\t<value> specify profile timer period in millisec.\n")); + s = dbe_sprintf (GTXT ("\t\t\tRange on this system is from %.3f to %.3f millisec.\n\t\t\tResolution is %.3f millisec.\n"), + (double) cc->get_clk_min () / 1000., + (double) cc->get_clk_max () / 1000., + (double) cc->get_clk_res () / 1000.); + writeStr (usage_fd, s); + free (s); + writeStr (usage_fd, GTXT (" -h <ctr_def>...[,<ctr_n_def>]\tspecify HW counter profiling\n")); + s = dbe_sprintf (GTXT ("\tto see the supported HW counters on this machine, run \"%s -h\" with no other arguments\n"), + whoami); + writeStr (usage_fd, s); + free (s); + writeStr (usage_fd, GTXT (" -s <threshold>[,<scope>]\tspecify synchronization wait tracing\n")); + writeStr (usage_fd, GTXT ("\t<scope> is \"j\" for tracing Java-APIs, \"n\" for tracing native-APIs, or \"nj\" for tracing both\n")); + writeStr (usage_fd, GTXT (" -H {on|off}\tspecify heap tracing\n")); + writeStr (usage_fd, GTXT (" -i {on|off}\tspecify I/O tracing\n")); + writeStr (usage_fd, GTXT (" -N <lib>\tspecify library to exclude count from instrumentation (requires -c also)\n")); + writeStr (usage_fd, GTXT (" \tmultiple -N arguments can be provided\n")); + writeStr (usage_fd, GTXT (" -j {on|off|path}\tspecify Java profiling\n")); + writeStr (usage_fd, GTXT (" -J <java-args>\tspecify arguments to Java for Java profiling\n")); + writeStr (usage_fd, GTXT (" -t <duration>\tspecify time over which to record data\n")); + writeStr (usage_fd, GTXT (" -n\tdry run -- don't run target or collect performance data\n")); + writeStr (usage_fd, GTXT (" -y <signal>[,r]\tspecify delayed initialization and pause/resume signal\n")); + writeStr (usage_fd, GTXT ("\tWhen set, the target starts in paused mode;\n\t if the optional r is provided, it starts in resumed mode\n")); + writeStr (usage_fd, GTXT (" -F {on|off|=<regex>}\tspecify following descendant processes\n")); + writeStr (usage_fd, GTXT (" -a {on|ldobjects|src|usedldobjects|usedsrc|off}\tspecify archiving of binaries and other files;\n")); + writeStr (usage_fd, GTXT (" -S {on|off|<seconds>}\t Set the interval for periodic sampling of process-wide resource utilization\n")); + writeStr (usage_fd, GTXT (" -l <signal>\tspecify signal that will trigger a sample of process-wide resource utilization\n")); + writeStr (usage_fd, GTXT (" -o <expt>\tspecify experiment name\n")); + writeStr (usage_fd, GTXT (" --verbose\tprint expanded log of processing\n")); + writeStr (usage_fd, GTXT (" -C <label>\tspecify comment label (up to 10 may appear)\n")); + writeStr (usage_fd, GTXT (" -V|--version\tprint version number and exit\n")); +*/ + /* don't document this feature */ + // writeStr (usage_fd, GTXT(" -Z\tPreload mem.so, and launch target [no experiment]\n") ); +/* + writeStr (usage_fd, GTXT ("\n See the gp-collect(1) man page for more information\n")); +*/ + +#if 0 + /* print an extended usage message */ + /* find a Java for Java profiling, set Java on to check Java */ + find_java (); + cc->set_java_mode (NTXT ("on")); + + /* check for variable-clock rate */ + unsigned char mode = COL_CPUFREQ_NONE; + get_cpu_frequency (&mode); + if (mode != COL_CPUFREQ_NONE) + writeStr (usage_fd, GTXT ("NOTE: system has variable clock frequency, which may cause variable program run times.\n")); + + /* show the experiment that would be run */ + writeStr (usage_fd, GTXT ("\n Default experiment:\n")); + char *ccret = cc->setup_experiment (); + if (ccret != NULL) + { + writeStr (usage_fd, ccret); + free (ccret); + exit (1); + } + cc->delete_expt (); + ccret = cc->show (1); + if (ccret != NULL) + { + writeStr (usage_fd, ccret); + free (ccret); + } +#endif +} + +void +collect::short_usage () +{ + if (no_short_usage == 0) + dbe_write (usage_fd, GTXT ("Run \"%s --help\" for a usage message.\n"), whoami); +} + +void +collect::show_hwc_usage () +{ + usage_fd = 1; + short_usage (); + cc->setup_hwc (); + hwc_usage (false, whoami, NULL); +} + +void +collect::writeStr (int f, const char *buf) +{ + if (buf != NULL) + write (f, buf, strlen (buf)); +} diff --git a/gprofng/src/gp-display-src.cc b/gprofng/src/gp-display-src.cc new file mode 100644 index 0000000..ca7853d --- /dev/null +++ b/gprofng/src/gp-display-src.cc @@ -0,0 +1,752 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <unistd.h> +#include <fcntl.h> + +#include "util.h" +#include "DbeApplication.h" +#include "DbeSession.h" +#include "Function.h" +#include "LoadObject.h" +#include "Module.h" +#include "DbeView.h" +#include "Print.h" +#include "DbeFile.h" +#include "Command.h" + +class er_src : public DbeApplication +{ +public: + er_src (int argc, char *argv[]); + void start (int argc, char *argv[]); + +private: + + // override methods in base class + void usage (); + int check_args (int argc, char *argv[]); + void run_args (int argc, char *argv[]); + + enum Obj_Types + { + OT_EXE_ELF = 0, OT_JAVA_CLASS, OT_JAR_FILE, OT_UNKNOWN + }; + + void open (char *exe); + void dump_annotated (char *name, char* sel, char *src, DbeView *dbev, + bool is_dis, bool first); + void checkJavaClass (char *exe); + void print_header (bool first, const char* text); + void proc_cmd (CmdType cmd_type, bool first, char *arg1, + const char *arg2, const char *arg3 = NULL); + FILE *set_outfile (char *cmd, FILE *&set_file); + + bool is_java_class () { return obj_type == OT_JAVA_CLASS; } + + int dbevindex; + DbeView *dbev; + LoadObject *lo; + Obj_Types obj_type; + const char *out_fname; + FILE *out_file; + bool isDisasm; + bool isFuncs; + bool isDFuncs; + bool isSrc; + bool v_opt; + int multiple; + char *str_compcom; + bool hex_visible; + int src_visible; + int vis_src; + int vis_dis; + int threshold_src; + int threshold_dis; + int threshold; + int vis_bits; +}; + +static int +real_main (int argc, char *argv[]) +{ + er_src *src = new er_src (argc, argv); + src->start (argc, argv); + delete src; + return 0; +} + +int +main (int argc, char *argv[]) +{ + return catch_out_of_memory (real_main, argc, argv); +} + +er_src::er_src (int argc, char *argv[]) +: DbeApplication (argc, argv) +{ + obj_type = OT_UNKNOWN; + out_fname = "<stdout>"; + out_file = stdout; + isDisasm = false; + isFuncs = false; + isDFuncs = false; + isSrc = false; + v_opt = false; + multiple = 0; + lo = NULL; +} + +static int +FuncNameCmp (const void *a, const void *b) +{ + Function *item1 = *((Function **) a); + Function *item2 = *((Function **) b); + return strcmp (item1->get_mangled_name (), item2->get_mangled_name ()); +} + +static int +FuncAddrCmp (const void *a, const void *b) +{ + Function *item1 = *((Function **) a); + Function *item2 = *((Function **) b); + return (item1->img_offset == item2->img_offset) ? + FuncNameCmp (a, b) : item1->img_offset > item2->img_offset ? 1 : -1; +} + +void +er_src::usage () +{ + +/* + Ruud - Isolate this line because it has an argument. Otherwise it would be at the + end of a long usage list. +*/ + printf ( GTXT ( + "Usage: gprofng display src [OPTION(S)] TARGET-OBJECT\n")); + + printf ( GTXT ( + "\n" + "Display the source code listing, or source code interleaved with disassembly code,\n" + "as extracted from the target object (an executable, shared object, object file, or\n" + "a Java .class file).\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + " -func list all the functions from the given object.\n" + "\n" + " -source item tag show the source code for item; the tag is used to\n" + " differentiate in case of multiple occurences with\n" + " the same name; the combination of \"all -1\" selects\n" + " all the functions in the object; the default is\n" + " \"-source all -1\".\n" + "\n" + " -disasm item tag show the source code, interleaved with the disassembled\n" + " instructions; the same definitions for item and tag apply.\n" + "\n" + " -outfile <filename> write results to file <filename>; a dash (-) writes to\n" + " stdout; this is also the default; note that this only\n" + " affects options included to the right of this option.\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gprofng(1), gp-archive(1), gp-collect-app(1), gp-display-html(1), gp-display-text(1)\n")); +/* + printf (GTXT ("Usage: %s [OPTION] a.out/.so/.o/.class\n\n"), whoami); + printf (GTXT (" -func List all the functions from the given object\n" + " -source, -src item tag Show the annotated source for the listed item\n" + " -disasm item tag Include the disassembly in the listing\n" + " -V Print the current release version of er_src\n" + " -cc, -scc, -dcc com_spec Define the compiler commentary classes to show\n" + " -outfile filename Open filename for output\n")); +*/ + exit (0); +} + +void +er_src::start (int argc, char *argv[]) +{ + dbevindex = dbeSession->createView (0, -1); + dbev = dbeSession->getView (dbevindex); + + // get options + check_args (argc, argv); + run_args (argc, argv); + if (out_file != stdout) + fclose (out_file); +} + +FILE * +er_src::set_outfile (char *cmd, FILE *&set_file) +{ + FILE *new_file; + if (!strcasecmp (cmd, "-")) + { + new_file = stdout; + out_fname = "<stdout>"; + } + else + { + char *cmdpath; + char *fname = strstr (cmd, "~/"); + // Handle ~ in file names + char *home = getenv ("HOME"); + if (fname != NULL && home != NULL) + cmdpath = dbe_sprintf ("%s/%s", home, fname + 2); + else if ((fname = strstr (cmd, "~")) != NULL && home != NULL) + cmdpath = dbe_sprintf ("/home/%s", fname + 1); + else + cmdpath = strdup (cmd); + new_file = fopen (cmdpath, "w"); + if (new_file == NULL) + { + fprintf (stderr, GTXT ("Unable to open file: %s"), cmdpath); + free (cmdpath); + return NULL; + } + out_fname = cmdpath; + } + if (set_file && (set_file != stdout)) + fclose (set_file); + + set_file = new_file; + return set_file; +} + +void +er_src::proc_cmd (CmdType cmd_type, bool first, char *arg1, + const char *arg2, const char *arg3) +{ + Cmd_status status; + Module *module; + Function *fitem; + int mindex, findex; + switch (cmd_type) + { + case SOURCE: + dbev->set_view_mode (VMODE_USER); + print_anno_file (arg1, arg2, arg3, false, + stdout, stdin, out_file, dbev, false); + break; + case DISASM: + dbev->set_view_mode (VMODE_MACHINE); + print_header (first, GTXT ("Annotated disassembly\n")); + print_anno_file (arg1, arg2, arg3, true, + stdout, stdin, out_file, dbev, false); + break; + case OUTFILE: + if (arg1) + set_outfile (arg1, out_file); + break; + case FUNCS: + print_header (false, GTXT ("Function list\n")); + fprintf (out_file, GTXT ("Functions sorted in lexicographic order\n")); + fprintf (out_file, GTXT ("\nLoad Object: %s\n\n"), lo->get_name ()); + if (lo->wsize == W32) + fprintf (out_file, GTXT (" Address Size Name\n\n")); + else + fprintf (out_file, GTXT (" Address Size Name\n\n")); + + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + module->functions->sort (FuncNameCmp); + const char *fmt = (lo->wsize == W32) ? + GTXT (" 0x%08llx %8lld %s\n") : + GTXT (" 0x%016llx %16lld %s\n"); + Vec_loop (Function*, module->functions, findex, fitem) + { + fprintf (out_file, fmt, + (ull_t) fitem->img_offset, + (ull_t) fitem->size, + fitem->get_name ()); + } + } + break; + case DUMPFUNC: + lo->functions->sort (FuncAddrCmp); + print_header (first, GTXT ("Dump functions\n")); + lo->dump_functions (out_file); + first = false; + break; + case SCOMPCOM: + status = dbev->proc_compcom (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s"), Command::get_err_string (status)); + break; + case DCOMPCOM: + status = dbev->proc_compcom (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s"), Command::get_err_string (status)); + break; + case COMPCOM: + status = dbev->proc_compcom (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s"), Command::get_err_string (status), arg1); + status = dbev->proc_compcom (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s"), Command::get_err_string (status), arg1); + break; + case HELP: + usage (); + break; + case VERSION_cmd: + if (out_file != stdout) +// Ruud + Application::print_version_info (); +/* + fprintf (out_file, "GNU %s version %s\n", get_basename (prog_name), VERSION); +*/ + break; + default: + fprintf (stderr, GTXT ("Invalid option")); + break; + } +} + +void +er_src::run_args (int argc, char *argv[]) +{ + CmdType cmd_type; + int arg_count, cparam; + char *arg; + char *arg1; + const char *arg2; + bool first = true; + bool space; + Module *module; + int mindex; + + for (int i = 1; i < argc; i++) + { + if (*argv[i] != '-') + { + if (!multiple) + { // er_src -V exe + space = false; + dbev->set_view_mode (VMODE_USER); + print_header (first, GTXT ("Annotated source\n")); + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + module->lang_code == Sp_lang_unknown) + continue; + if (space) + fprintf (out_file, "\n"); + print_anno_file (module->file_name, "1", NULL, false, + stdout, stdin, out_file, dbev, false); + space = true; + } + } + break; + } + if (strncmp (argv[i], NTXT ("--whoami="), 9) == 0) + { + whoami = argv[i] + 9; + continue; + } + switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam)) + { + case SOURCE: + case DISASM: + { + i += arg_count; + multiple++; + if (i >= argc || argv[i] == NULL || + (*(argv[i]) == '-' && atoi (argv[i]) != -1) || i + 1 == argc) + { + i--; + arg = argv[i]; + arg2 = "1"; + } + else + { + arg = argv[i - 1]; + if (*(argv[i]) == '-' && atoi (argv[i]) == -1 && + streq (arg, NTXT ("all"))) + { + space = false; + if (cmd_type == SOURCE) + print_header (first, GTXT ("Annotated source\n")); + else + print_header (first, GTXT ("Annotated disassembly\n")); + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + module->lang_code == Sp_lang_unknown) + continue; + if (space) + fprintf (out_file, "\n"); + proc_cmd (cmd_type, first, module->file_name, "1"); + space = true; + } + first = false; + break; + } + arg2 = argv[i]; + } + char *fcontext = NULL; + arg1 = parse_fname (arg, &fcontext); + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid function/file setting: %s\n"), arg1); + free (fcontext); + break; + } + proc_cmd (cmd_type, first, arg1, arg2, fcontext); + free (arg1); + free (fcontext); + first = false; + break; + } + case OUTFILE: + case FUNCS: + case DUMPFUNC: + case COMPCOM: + case SCOMPCOM: + case DCOMPCOM: + case VERSION_cmd: + case HELP: + proc_cmd (cmd_type, first, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL); + i += arg_count; + break; + default: + if (streq (argv[i] + 1, NTXT ("all")) || streq (argv[i] + 1, NTXT ("dall"))) + { + first = false; + multiple++; + if (streq (argv[i] + 1, NTXT ("all"))) + proc_cmd (FUNCS, first, NULL, NULL); + else + proc_cmd (DUMPFUNC, first, NULL, NULL); + space = false; + print_header (first, GTXT ("Annotated source\n")); + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + module->lang_code == Sp_lang_unknown) + continue; + if (space) + fprintf (out_file, "\n"); + proc_cmd (SOURCE, first, module->file_name, "1"); + space = true; + } + print_header (first, GTXT ("Annotated disassembly\n")); + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + module->lang_code == Sp_lang_unknown) + continue; + if (space) + fprintf (out_file, "\n"); + proc_cmd (DISASM, first, module->file_name, "1"); + space = true; + } + } + else + { + proc_cmd (cmd_type, first, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL); + i += arg_count; + break; + } + } + } +} + +int +er_src::check_args (int argc, char *argv[]) +{ + CmdType cmd_type = UNKNOWN_CMD; + int arg_count, cparam; + int i; + char *exe; + bool first = true; + if (argc == 1) + usage (); + + // If any comments from the .rc files, log them to stderr + Emsg * rcmsg = fetch_comments (); + while (rcmsg != NULL) + { + fprintf (stderr, "%s: %s\n", prog_name, rcmsg->get_msg ()); + rcmsg = rcmsg->next; + } + + // Parsing the command line + opterr = 0; + exe = NULL; + for (i = 1; i < argc; i++) + { + if (*argv[i] != '-') + { + exe = argv[i]; + if (i == 1) + { // er_src exe ? + if (!exe) + usage (); + if (argc == 3) // er_src exe file + usage (); + } + else if (v_opt && !multiple && !exe && !str_compcom) // just er_src -V + exit (0); + if (argc < i + 1 || argc > i + 3) + usage (); + i++; + if (argc > i) + usage (); + open (exe); + return i; + } + switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam)) + { + case WHOAMI: + whoami = argv[i] + 1 + cparam; + break; + case HELP: + i += arg_count; + multiple++; + usage (); + break; + case VERSION_cmd: + if (first) + { +// Ruud + Application::print_version_info (); +/* + printf ("GNU %s version %s\n", get_basename (prog_name), VERSION); +*/ + v_opt = true; + first = false; + } + break; + case SOURCE: + case DISASM: + i += arg_count; + multiple++; + isDisasm = true; + if (i >= argc || argv[i] == NULL || + (*(argv[i]) == '-' && atoi (argv[i]) != -1) || (i + 1 == argc)) + i--; + break; + case DUMPFUNC: + i += arg_count; + multiple++; + break; + case FUNCS: + i += arg_count; + multiple++; + break; + case OUTFILE: + case COMPCOM: + case SCOMPCOM: + case DCOMPCOM: + i += arg_count; + break; + default: + if (!(streq (argv[i] + 1, NTXT ("all")) || + streq (argv[i] + 1, NTXT ("dall")))) + { + fprintf (stderr, "Error: invalid option: `%s'\n", argv[i]); + exit (1); + } + } + } + if (!exe && !(argc == 2 && cmd_type == VERSION_cmd)) + usage (); + return i; +} + +void +er_src::checkJavaClass (char* exe) +{ + unsigned char cf_buf[4]; + unsigned int magic_number; + int fd = ::open (exe, O_RDONLY | O_LARGEFILE); + if (fd == -1) + return; + if (sizeof (cf_buf) == read_from_file (fd, cf_buf, sizeof (cf_buf))) + { + magic_number = cf_buf[0] << 24; + magic_number |= cf_buf[1] << 16; + magic_number |= cf_buf[2] << 8; + magic_number |= cf_buf[3]; + if (magic_number == 0xcafebabe) + obj_type = OT_JAVA_CLASS; + } + close (fd); +} + +void +er_src::print_header (bool first, const char* text) +{ + if (!first) + fprintf (out_file, "\n"); + if (multiple > 1) + { + fprintf (out_file, NTXT ("%s"), text); + fprintf (out_file, "---------------------------------------\n"); + } +} + +void +er_src::dump_annotated (char *name, char *sel, char *src, DbeView *dbevr, + bool is_dis, bool first) +{ + Module *module; + bool space; + int mindex; + print_header (first, (is_dis) ? ((is_java_class ()) ? + GTXT ("Annotated bytecode\n") : + GTXT ("Annotated disassembly\n")) : + GTXT ("Annotated source\n")); + if (!name) + { + space = false; + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + (!is_dis && module->lang_code == Sp_lang_unknown)) + continue; + if (space) + fprintf (out_file, "\n"); + print_anno_file (module->file_name, sel, src, is_dis, + stdout, stdin, out_file, dbevr, false); + space = true; + } + } + else + print_anno_file (name, sel, src, is_dis, stdout, stdin, out_file, dbevr, false); +} + +static bool +isFatal (bool isDisasm, LoadObject::Arch_status status) +{ + if (isDisasm) + { + switch (status) + { + // non-fatal errors for disassembly + case LoadObject::ARCHIVE_BAD_STABS: + case LoadObject::ARCHIVE_NO_STABS: + return false; + default: + return true; + } + } + return true; +} + +void +er_src::open (char *exe) +{ + LoadObject::Arch_status status; + char *errstr; + Module *module; + Vector<Histable*> *module_lst; + + // Construct the Segment structure + char *path = strdup (exe); + lo = dbeSession->createLoadObject (path); + if (NULL == lo->dbeFile->find_file (lo->dbeFile->get_name ())) + { + fprintf (stderr, GTXT ("%s: Error: unable to open file %s\n"), prog_name, lo->dbeFile->get_name ()); + exit (1); + } + checkJavaClass (exe); + + if (is_java_class ()) + { + lo->type = LoadObject::SEG_TEXT; + lo->set_platform (Java, Wnone); + lo->id = (uint64_t) - 1; // see AnalyzerSession::ask_which for details + module = dbeSession->createClassFile (dbe_strdup (exe)); + module->loadobject = lo; + lo->seg_modules->append (module); + module->dbeFile->set_location (exe); + if (module->readFile () != module->AE_OK) + { + Emsg *emsg = module->get_error (); + if (emsg) + { + fprintf (stderr, GTXT ("%s: Error: %s\n"), prog_name, emsg->get_msg ()); + return; + } + fprintf (stderr, GTXT ("%s: Error: Could not read class file `%s'\n"), prog_name, exe); + return; + } + status = lo->sync_read_stabs (); + if (status != LoadObject::ARCHIVE_SUCCESS) + { + if (status == LoadObject::ARCHIVE_ERR_OPEN) + { + fprintf (stderr, GTXT ("%s: Error: Could not read class file `%s'\n"), prog_name, exe); + return; + } + else + { + if (isDisasm) + if (status == LoadObject::ARCHIVE_NO_STABS) + { + fprintf (stderr, GTXT ("%s: Error: `%s' is interface; disassembly annotation not available\n"), prog_name, exe); + return; + } + } + } + } + else + { + status = lo->sync_read_stabs (); + if (status != LoadObject::ARCHIVE_SUCCESS) + { + errstr = lo->status_str (status); + if (errstr) + { + fprintf (stderr, "%s: %s\n", prog_name, errstr); + free (errstr); + } + if (isFatal (isDisasm, status)) + return; + } + obj_type = OT_EXE_ELF; + + // if .o file, then set file as the exe name + if (lo->is_relocatable ()) + { + // find the module, if we can + module_lst = new Vector<Histable*>; + module = dbeSession->map_NametoModule (path, module_lst, 0); + if (module == NULL) + // Create a module with the right name + module = dbeSession->createModule (lo, path); + } + } +} diff --git a/gprofng/src/gp-display-text.cc b/gprofng/src/gp-display-text.cc new file mode 100644 index 0000000..7183553 --- /dev/null +++ b/gprofng/src/gp-display-text.cc @@ -0,0 +1,2834 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <unistd.h> // isatty + +#include "gp-print.h" +#include "ipcio.h" +#include "Command.h" +#include "Dbe.h" +#include "DbeApplication.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Emsg.h" +#include "DbeView.h" +#include "DataObject.h" +#include "Function.h" +#include "Hist_data.h" +#include "PathTree.h" +#include "LoadObject.h" +#include "Function.h" +#include "FilterSet.h" +#include "Filter.h" +#include "MetricList.h" +#include "MemorySpace.h" +#include "Module.h" +#include "util.h" +#include "i18n.h" +#include "StringBuilder.h" +#include "debug.h" +#include "UserLabel.h" + +static char *exe_name; +static char **new_argv; + +void +reexec () +{ + if (dbeSession != NULL) + dbeSession->unlink_tmp_files (); + execv (exe_name, new_argv); +} + +/** + * Run application under enhance if the following requirements are satisfied: + * 1. Environment variable GPROFNG_ENHANCE is not set to "no" + * 2. Standard input is terminal + * 3. Standard output is terminal + * 4. /bin/enhance exists and can work on this system + */ +static void +reexec_enhance (int argc, char *argv[]) +{ + char *gp_enhance = getenv ("GPROFNG_ENHANCE"); + if (NULL != gp_enhance && 0 == strcasecmp (gp_enhance, "no")) + return; // Do not enhance + // Verify that input and output are tty + if (!isatty (fileno (stdin))) // stdin is not a terminal + return; // Do not enhance + if (!isatty (fileno (stdout))) // stdout is not a terminal + return; // Do not enhance + char *enhance_name = NTXT ("/bin/enhance"); + struct stat sbuf; + int res = stat (enhance_name, &sbuf); // Check if enhance exists + if (res == 0) + res = system (NTXT ("/bin/enhance /bin/true")); // Check if enhance can work + if (res != 0) + { + fflush (stdout); + printf (GTXT ("Warning: History and command editing is not supported on this system.\n")); + fflush (stdout); + return; + } + else + { + printf (GTXT ("Note: History and command editing is supported on this system.\n")); + fflush (stdout); + } + char **nargv = new char*[argc + 2]; + for (int i = 0; i < argc; i++) + nargv[i + 1] = argv[i]; + nargv[0] = enhance_name; + nargv[argc + 1] = NULL; + putenv (NTXT ("GPROFNG_ENHANCE=no")); // prevent recursion + execv (enhance_name, nargv); + // execv failed. Continue to run the program + delete[] nargv; +} + +int +main (int argc, char *argv[]) +{ + er_print *erprint; + int ind = 1; + if (argc > ind && *argv[ind] == '-') + { + int arg_count, cparam; + if (Command::get_command (argv[ind] + 1, arg_count, cparam) == WHOAMI) + ind = ind + 1 + arg_count; + } + if (argc > ind && argv[ind] != NULL && *argv[ind] != '-') + reexec_enhance (argc, argv); + + // Save argv for reexec()) + exe_name = argv[0]; + new_argv = argv; + + if (argc > ind && argv[ind] != NULL && strcmp (argv[ind], "-IPC") == 0) + { + putenv (NTXT ("LC_NUMERIC=C")); // Use non-localized numeric data in IPC packets + erprint = new er_print (argc, argv); + theDbeApplication->rdtMode = false; + ipc_mainLoop (argc, argv); + } + else + { + erprint = new er_print (argc, argv); + erprint->start (argc, argv); + } + + dbeSession->unlink_tmp_files (); + if (DUMP_CALL_STACK) + { + extern long total_calls_add_stack, total_stacks, total_nodes, call_stack_size[201]; + fprintf (stderr, NTXT ("total_calls_add_stack=%lld\ntotal_stacks=%lld\ntotal_nodes=%lld\n"), + (long long) total_calls_add_stack, (long long) total_stacks, (long long) total_nodes); + for (int i = 0; i < 201; i++) + if (call_stack_size[i] != 0) + fprintf (stderr, NTXT (" call_stack_size[%d] = %6lld\n"), i, + (long long) call_stack_size[i]); + } +#if defined(DEBUG) + delete erprint; +#endif + return 0; +} + +er_print::er_print (int argc, char *argv[]) +: DbeApplication (argc, argv) +{ + out_fname = GTXT ("<stdout>"); + inp_file = stdin; + out_file = stdout; + dis_file = stdout; + cov_string = NULL; + limit = 0; + cstack = new Vector<Histable*>(); + was_QQUIT = false; +} + +er_print::~er_print () +{ + free (cov_string); + delete cstack; + if (inp_file != stdin) + fclose (inp_file); +} + +void +er_print::start (int argc, char *argv[]) +{ + Vector<String> *res = theDbeApplication->initApplication (NULL, NULL, NULL); + res->destroy (); + delete res; + + // Create a view on the session + dbevindex = dbeSession->createView (0, -1); + dbev = dbeSession->getView (dbevindex); + limit = dbev->get_limit (); + (void) check_args (argc, argv); + int ngood = dbeSession->ngoodexps (); + if (ngood == 0) + { + fprintf (stderr, GTXT ("No valid experiments loaded; exiting\n")); + return; + } + dbeDetectLoadMachineModel (dbevindex); + run (argc, argv); +} + +bool +er_print::free_memory_before_exit () +{ + return was_QQUIT; +} + +void +er_print::usage () +{ + +/* + Ruud - Isolate this line because it has an argument. Otherwise it would be at the + end of the long option list. +*/ + printf ( GTXT ( + "Usage: gprofng display text [OPTION(S)] [COMMAND(S)] [-script <script_file>] EXPERIMENT(S)\n")); + + printf ( GTXT ( + "\n" + "Print a plain text version of the various displays supported by gprofng.\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + " -script <script-file> execute the commands stored in the script file;\n" + " this feature may be combined with commands specified\n" + " at the command line.\n" + "\n" + "Commands:\n" + "\n" + "This tool supports a rich set of commands to control the display of the\n" + "data; instead of, or in addition to, including these commands in a script\n" + "file, it is also allowed to include such commands at the command line;\n" + "in this case, the commands need to be prepended with the \"-\" symbol; the\n" + "commands are processed and interpreted left from right, so the order matters;\n" + "The gprofng manual documents the commands that are supported.\n" + "\n" + "If this tool is invoked without options, commands, or a script file, it starts\n" + "in interpreter mode. The user can then issue the commands interactively; the\n" + "session is terminated with the \"exit\" command in the interpreter.\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gprofng(1), gp-archive(1), gp-collect-app(1), gp-display-html(1), gp-display-src(1)\n")); +} + +int // returns count of experiments read +er_print::check_args (int argc, char *argv[]) +{ + CmdType cmd_type; + int arg_count; + int cparam; + int exp_no; + error_msg = NULL; + + Emsg *rcmsg = fetch_comments (); + while (rcmsg != NULL) + { + fprintf (stderr, NTXT ("%s: %s\n"), prog_name, rcmsg->get_msg ()); + rcmsg = rcmsg->next; + } + delete_comments (); + + // Set up the list of experiments to add after checking the args + Vector<Vector<char*>*> *exp_list = new Vector<Vector<char*>*>(); + + // Prescan the command line arguments, processing only a few + for (int i = 1; i < argc; i++) + { + if (*argv[i] != '-') + { + // we're at the end -- get the list of experiments + // Build the list of experiments, and set the searchpath + Vector<char*> *list = dbeSession->get_group_or_expt (argv[i]); + if (list->size () > 0) + { + for (int j = 0, list_sz = list->size (); j < list_sz; j++) + { + char *path = list->fetch (j); + if (strlen (path) == 0 || strcmp (path, NTXT ("\\")) == 0) + continue; + char *p = strrchr (path, '/'); + if (p) + { + // there's a directory in front of the name; add it to search path + *p = '\0'; + dbeSession->set_search_path (path, false); + } + } + list->destroy (); + list->append (dbe_strdup (argv[i])); + exp_list->append (list); + } + else + delete list; + continue; + } + + // Not at the end yet, treat the next argument as a command + switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam)) + { + case WHOAMI: + whoami = argv[i] + 1 + cparam; + break; + case HELP: + if (i + 1 + arg_count == argc) + { + usage(); + exit (0); + } + break; + case HHELP: + Command::print_help (whoami, true, false, stdout); + fprintf (stdout, "\n"); + indxo_list (false, stdout); + fprintf (stdout, "\n"); + mo_list (false, stdout); + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (stdout, GTXT ("\nSee gprofng(1) for more details\n")); + exit (0); + case ADD_EXP: + case DROP_EXP: + case OPEN_EXP: + printf (GTXT ("Error: command %s can not appear on the command line\n"), argv[i]); + exit (2); + case VERSION_cmd: + Application::print_version_info (); + exit (0); + case AMBIGUOUS_CMD: + fprintf (stderr, GTXT ("Error: Ambiguous command: %s\n"), argv[i]); + exit (2); + case UNKNOWN_CMD: + fprintf (stderr, GTXT ("Error: Invalid command: %s\n"), argv[i]); + exit (2); + // it's a plausible argument; see if we process now or later + case SOURCE: + case DISASM: + case CSINGLE: + case CPREPEND: + case CAPPEND: + case FSINGLE: + case SAMPLE_DETAIL: + case STATISTICS: + case HEADER: + //skip the arguments to that command + i += arg_count; + if (i >= argc || end_command (argv[i])) + i--; + break; + case PRINTMODE: + case INDXOBJDEF: + case ADDPATH: + case SETPATH: + case PATHMAP: + case OBJECT_SHOW: + case OBJECT_HIDE: + case OBJECT_API: + case OBJECTS_DEFAULT: + case EN_DESC: + // these are processed in the initial pass over the arguments + proc_cmd (cmd_type, cparam, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL, + (arg_count > 2) ? argv[i + 3] : NULL, + (arg_count > 3) ? argv[i + 4] : NULL); + i += arg_count; + break; + default: + // any others, we skip for now + i += arg_count; + break; + } + } + + // Make sure some experiments were specified + exp_no = exp_list->size (); + if (exp_no == 0) + { // no experiment name + fprintf (stderr, GTXT ("%s: Missing experiment directory (use the --help option to get a usage overview)\n"), whoami); + exit (1); + } + + // add the experiments to the session + char *errstr = dbeOpenExperimentList (0, exp_list, false); + for (long i = 0; i < exp_list->size (); i++) + { + Vector<char*>* p = exp_list->get (i); + Destroy (p); + } + delete exp_list; + if (errstr != NULL) + { + fprintf (stderr, NTXT ("%s"), errstr); + free (errstr); + } + + return exp_no; +} + +int +er_print::is_valid_seg_name (char *lo_name, int prev) +{ + // prev is the loadobject segment index that was last returned + // search starts following that loadobject + int index; + LoadObject *lo; + char *p_lo_name = lo_name; + char *name = NULL; + + // strip angle brackets from all but <Unknown> and <Total> + if (strcmp (lo_name, "<Unknown>") && strcmp (lo_name, "<Total>")) + { + if (*lo_name == '<') + { + name = dbe_strdup (lo_name + 1); + p_lo_name = name; + char *p = strchr (name, '>'); + if (p) + *p = '\0'; + } + } + + // get the load object list from the session + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + Vec_loop (LoadObject*, lobjs, index, lo) + { + if (prev > 0) + { + if (lo->seg_idx == prev) // this is where we left off + prev = -1; + continue; + } + + // does this one match? + if (cmp_seg_name (lo->get_pathname (), p_lo_name)) + { + delete lobjs; + free (name); + size_t len = strlen (lo_name); + if ((len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) || + (len > 6 && streq (lo_name + len - 6, NTXT (".class")))) + { + fprintf (stderr, GTXT ("Error: Java class `%s' is not selectable\n"), lo_name); + return -1; + } + return lo->seg_idx; + } + } + delete lobjs; + free (name); + return -1; +} + +int +er_print::cmp_seg_name (char *full_name, char *lo_name) +{ + char *cmp_name; + if (!strchr (lo_name, '/') && (cmp_name = strrchr (full_name, '/'))) + cmp_name++; // basename + else + cmp_name = full_name; // full path name + return !strcmp (lo_name, cmp_name); +} + +// processing object_select +// Note that this does not affect the strings in Settings, +// unlike object_show, object_hide, and object_api +int +er_print::process_object_select (char *names) +{ + int index; + LoadObject *lo; + int no_lobj = 0; + bool got_err = false; + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + if ((names == NULL) || !strcasecmp (names, Command::ALL_CMD)) + { // full coverage + Vec_loop (LoadObject*, lobjs, index, lo) + { + dbev->set_lo_expand (lo->seg_idx, LIBEX_SHOW); + } + } + else + { // parsing coverage + // first, hide functions from all loadobjects + // except the java ones + Vec_loop (LoadObject*, lobjs, index, lo) + { + char *lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if ((len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) || + (len > 6 && streq (lo_name + len - 6, NTXT (".class")))) + continue; + } + dbev->set_lo_expand (lo->seg_idx, LIBEX_HIDE); + } + + Vector <char *> *tokens = split_str (names, ','); + for (long j = 0, sz = VecSize (tokens); j < sz; j++) + { + // loop over the provided names + char *lo_name = tokens->get (j); + int seg_idx = -1; + seg_idx = is_valid_seg_name (lo_name, seg_idx); + while (seg_idx != -1) + { + dbev->set_lo_expand (seg_idx, LIBEX_SHOW); + no_lobj++; + seg_idx = is_valid_seg_name (lo_name, seg_idx); + } + if (no_lobj == 0) + { + got_err = true; + fprintf (stderr, GTXT ("Error: Unknown load object: `%s'\n"), lo_name); + } + free (lo_name); + } + delete tokens; + } + + if (!got_err) + { // good coverage string + free (cov_string); + cov_string = strdup (names); + } + else + { // bad, restore original coverage + no_lobj = -1; + process_object_select (cov_string); + } + delete lobjs; + return no_lobj; +} + +int +er_print::set_libexpand (char *cov, enum LibExpand expand) +{ + bool changed = dbev->set_libexpand (cov, expand); + if (changed == true) + dbev->update_lo_expands (); + return 0; +} + +int +er_print::set_libdefaults () +{ + dbev->set_libdefaults (); + return 0; +} + +bool +er_print::end_command (char *cmd) +{ + if (cmd == NULL || *cmd == '-') + return true; + size_t len = strlen (cmd); + if (cmd[len - 1] == '/') + len--; + if ((len > 3 && !strncmp (&cmd[len - 3], NTXT (".er"), 3)) || + (len > 4 && !strncmp (&cmd[len - 4], NTXT (".erg"), 4))) + return true; + return false; +} + +// Now actually start processing the arguments +void +er_print::run (int argc, char *argv[]) +{ + CmdType cmd_type; + int arg_count, cparam, i; + bool got = false; + char *arg1, *arg2; + for (i = 1; i < argc; i++) + { + if (*argv[i] != '-') // open experiment pointer files + continue; + switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam)) + { + case WHOAMI: + whoami = argv[i] + 1 + cparam; + break; + case SCRIPT: + got = true; + inp_file = fopen (argv[++i], "r"); + if (inp_file == NULL) + { + fprintf (stderr, GTXT ("Error: Script file cannot be opened: %s\n"), argv[i]); + exit (3); + } + proc_script (); + break; + case STDIN: + got = true; + inp_file = stdin; + proc_script (); + break; + case SOURCE: // with option arg_count == 2 + case DISASM: + got = true; + i += arg_count; + if ((i >= argc) || end_command (argv[i])) + { + i--; + arg1 = argv[i]; + arg2 = NTXT (""); + } + else + { + arg1 = argv[i - 1]; + arg2 = argv[i]; + } + proc_cmd (cmd_type, cparam, arg1, arg2, NULL, NULL, true); + break; + case CSINGLE: + case CPREPEND: + case CAPPEND: + case FSINGLE: + got = true; + i += arg_count; + if ((i >= argc) || end_command (argv[i])) + { + i--; + proc_cmd (cmd_type, cparam, argv[i], NTXT ("1")); + } + else + proc_cmd (cmd_type, cparam, argv[i - 1], argv[i]); + break; + case SAMPLE_DETAIL: // with option arg_count == 1 + case STATISTICS: + case HEADER: + got = true; + // now fall through to process the command + case COMPARE: + got = true; + i += arg_count; + if ((i >= argc) || end_command (argv[i])) + { + i--; + proc_cmd (cmd_type, cparam, NULL, NULL); + } + else + proc_cmd (cmd_type, cparam, argv[i], NULL); + break; + case PRINTMODE: + case INDXOBJDEF: + case ADDPATH: + case SETPATH: + case PATHMAP: + case OBJECT_SHOW: + case OBJECT_HIDE: + case OBJECT_API: + case OBJECTS_DEFAULT: + case EN_DESC: + got = true; + // these have been processed already + i += arg_count; + break; + case LIMIT: + got = true; + proc_cmd (cmd_type, cparam, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL); + i += arg_count; + break; + default: + got = true; + proc_cmd (cmd_type, cparam, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL); + i += arg_count; + break; + } + } + if (!got) // no command has been specified + proc_script (); +} + +#define MAXARGS 20 + +void +er_print::proc_script () +{ + CmdType cmd_type; + int arg_count, cparam; + char *cmd, *end_cmd; + char *script = NULL; + char *arglist[MAXARGS]; + char *line = NULL; + int lineno = 0; + while (!feof (inp_file)) + { + if (inp_file == stdin) + printf (NTXT ("(%s) "), get_basename (prog_name)); + free (script); + script = read_line (inp_file); + if (script == NULL) + continue; + free (line); + line = dbe_strdup (script); + lineno++; + for (int i = 0; i < MAXARGS; i++) + arglist[i] = NULL; + + // ensure it's terminated by a \n, and remove that character + strtok (script, NTXT ("\n")); + + // extract the command + cmd = strtok (script, NTXT (" \t")); + if (cmd == NULL) + continue; + if (*cmd == '#') + { + fprintf (stderr, NTXT ("%s"), line); + continue; + } + if (*cmd == '\n') + continue; + + char *remainder = strtok (NULL, NTXT ("\n")); + // now extract the arguments + int nargs = 0; + for (;;) + { + end_cmd = NULL; + if (nargs >= MAXARGS) + fprintf (stderr, GTXT ("Warning: more than %d arguments to %s command, line %d\n"), + MAXARGS, cmd, lineno); + char *nextarg = strtok (remainder, NTXT ("\n")); + if ((nextarg == NULL) || (*nextarg == '#')) + // either the end of the line, or a comment indicator + break; + if (nargs >= MAXARGS) + { + parse_qstring (nextarg, &end_cmd); + nargs++; + } + else + arglist[nargs++] = parse_qstring (nextarg, &end_cmd); + remainder = end_cmd; + if (remainder == NULL) + break; + // skip any blanks or tabs to get to next argument + while (*remainder == ' ' || *remainder == '\t') + remainder++; + } + + cmd_type = Command::get_command (cmd, arg_count, cparam); + + // check for extra arguments + if (cmd_type != UNKNOWN_CMD && cmd_type != INDXOBJDEF && nargs > arg_count) + fprintf (stderr, GTXT ("Warning: extra arguments to %s command, line %d\n"), + cmd, lineno); + switch (cmd_type) + { + case SOURCE: + case DISASM: + // ignore any third parameter + // if there was, we have written a warning + proc_cmd (cmd_type, cparam, arglist[0], arglist[1], NULL, NULL, + (inp_file != stdin)); + break; + case QUIT: + free (script); + free (line); + exit (0); + case QQUIT: + was_QQUIT = true; + free (script); + free (line); + return; + case STDIN: + break; + case COMMENT: + fprintf (dis_file, NTXT ("%s"), line); + break; + case AMBIGUOUS_CMD: + fprintf (stderr, GTXT ("Error: Ambiguous command: %s\n"), cmd); + break; + case UNKNOWN_CMD: + if (*cmd != '\n') + fprintf (stderr, GTXT ("Error: Invalid command: %s\n"), cmd); + break; + default: + proc_cmd (cmd_type, cparam, arglist[0], arglist[1]); + break; + } + } + // free up the input line + free (script); + free (line); +} + +void +er_print::proc_cmd (CmdType cmd_type, int cparam, + char *arg1, char *arg2, char *arg3, char *arg4, bool xdefault) +{ + er_print_common_display *cd; + FILE *ck_file, *save_file; + char *name; + int bgn_index, end_index, index; + Cmd_status status; + char *scratch, *scratch1; + switch (cmd_type) + { + case FUNCS: + print_func (Histable::FUNCTION, MODE_LIST, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_list (MET_NORMAL)); + break; + case FDETAIL: + print_func (Histable::FUNCTION, MODE_DETAIL, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_ref (MET_NORMAL)); + break; + case FSINGLE: + print_func (Histable::FUNCTION, MODE_DETAIL, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_ref (MET_NORMAL), + arg1, arg2); + break; + case HOTPCS: + print_func (Histable::INSTR, MODE_LIST, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_list (MET_NORMAL)); + break; + case PDETAIL: + print_func (Histable::INSTR, MODE_DETAIL, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_ref (MET_NORMAL)); + break; + case HOTLINES: + print_func (Histable::LINE, MODE_LIST, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_list (MET_NORMAL)); + break; + case LDETAIL: + print_func (Histable::LINE, MODE_DETAIL, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_ref (MET_NORMAL)); + break; + case OBJECTS: + print_objects (); + break; + case OVERVIEW_NEW: + print_overview (); + break; + case LOADOBJECT: + print_segments (); + break; + case GPROF: + print_func (Histable::FUNCTION, MODE_GPROF, + dbev->get_metric_list (MET_CALL), dbev->get_metric_list (MET_NORMAL)); + break; + case CALLTREE: + if (dbev->comparingExperiments ()) + { + fprintf (out_file, GTXT ("\nNot available when comparing experiments\n\n")); + break; + } + print_ctree (cmd_type); + break; + case CSINGLE: + case CPREPEND: + case CAPPEND: + case CRMFIRST: + case CRMLAST: + print_gprof (cmd_type, arg1, arg2); + break; + case EXP_LIST: + exp_list (); + break; + case DESCRIBE: + describe (); + break; + case SCOMPCOM: + status = dbev->proc_compcom (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case STHRESH: + status = dbev->proc_thresh (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case DCOMPCOM: + status = dbev->proc_compcom (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case COMPCOM: + status = dbev->proc_compcom (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + status = dbev->proc_compcom (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case DTHRESH: + status = dbev->proc_thresh (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case SOURCE: + case DISASM: + { + if (arg3 != NULL) + abort (); + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid function/file setting: \n")); + break; + } + char *fcontext = NULL; + char *arg = parse_fname (arg1, &fcontext); + if (arg == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid function/file setting: %s\n"), arg1); + free (fcontext); + break; + } + if (arg2 && (strlen (arg2) == 0)) + arg2 = NULL; + print_anno_file (arg, arg2, fcontext, cmd_type == DISASM, + dis_file, inp_file, out_file, dbev, xdefault); + free (arg); + free (fcontext); + break; + } + case METRIC_LIST: + proc_cmd (METRICS, cparam, NULL, NULL); + dbev->get_metric_ref (MET_NORMAL)->print_metric_list (dis_file, + GTXT ("Available metrics:\n"), false); + break; + case METRICS: + if (arg1) + { + char *ret = dbev->setMetrics (arg1, false); + if (ret != NULL) + { + fprintf (stderr, GTXT ("Error: %s\n"), ret); + proc_cmd (METRIC_LIST, cparam, NULL, NULL); + break; + } + } + scratch = dbev->get_metric_list (MET_NORMAL)->get_metrics (); + fprintf (dis_file, GTXT ("Current metrics: %s\n"), scratch); + free (scratch); + proc_cmd (SORT, cparam, NULL, NULL); + break; + case GMETRIC_LIST: + scratch = dbev->get_metric_list (MET_CALL)->get_metrics (); + fprintf (dis_file, GTXT ("Current caller-callee metrics: %s\n"), scratch); + free (scratch); + fprintf (dis_file, GTXT ("Current caller-callee sort Metric: %s\n"), + dbev->getSort (MET_DATA)); + break; + case INDX_METRIC_LIST: + scratch = dbev->get_metric_list (MET_INDX)->get_metrics (); + fprintf (dis_file, GTXT ("Current index-object metrics: %s\n"), scratch); + free (scratch); + scratch = dbev->getSort (MET_INDX); + fprintf (dis_file, GTXT ("Current index-object sort Metric: %s\n"), scratch); + free (scratch); + break; + case SORT: + if (arg1) + { + char *ret = dbev->setSort (arg1, MET_NORMAL, false); + if (ret != NULL) + { + fprintf (stderr, GTXT ("Error: %s\n"), ret); + proc_cmd (METRICS, cparam, NULL, NULL); + break; + } + dbev->setSort (arg1, MET_SRCDIS, false); + dbev->setSort (arg1, MET_CALL, false); + dbev->setSort (arg1, MET_DATA, false); + dbev->setSort (arg1, MET_INDX, false); + dbev->setSort (arg1, MET_CALL_AGR, false); + dbev->setSort (arg1, MET_IO, false); + dbev->setSort (arg1, MET_HEAP, false); + } + scratch = dbev->getSort (MET_NORMAL); + scratch1 = dbev->getSortCmd (MET_NORMAL); + fprintf (dis_file, + GTXT ("Current Sort Metric: %s ( %s )\n"), scratch, scratch1); + free (scratch1); + free (scratch); + break; + case OBJECT_SHOW: + if (arg1) + set_libexpand (arg1, LIBEX_SHOW); + obj_list (); + break; + case OBJECT_HIDE: + if (arg1) + set_libexpand (arg1, LIBEX_HIDE); + obj_list (); + break; + case OBJECT_API: + if (arg1) + set_libexpand (arg1, LIBEX_API); + obj_list (); + break; + case OBJECTS_DEFAULT: + set_libdefaults (); + obj_list (); + break; + case OBJECT_LIST: + obj_list (); + break; + case OBJECT_SELECT: + if (arg1) + { + if (process_object_select (arg1) != -1) + proc_cmd (OBJECT_LIST, cparam, NULL, NULL); + else + fprintf (stderr, GTXT ("Error: Type \"object_list\" for a list of all load objects.\n")); + } + else + fprintf (stderr, GTXT ("Error: No load object has been specified.\n")); + break; + case LOADOBJECT_LIST: + seg_list (); + break; + case LOADOBJECT_SELECT: + if (arg1) + { + if (process_object_select (arg1) != -1) + proc_cmd (LOADOBJECT_LIST, cparam, NULL, NULL); + else + fprintf (stderr, GTXT ("Error: Type \"segment_list\" for a list of all segments.\n")); + } + else + fprintf (stderr, GTXT ("Error: No segment has been specified.\n")); + break; + case SAMPLE_LIST: + filter_list (SAMPLE_LIST); + break; + case SAMPLE_SELECT: + if (arg1 && !dbev->set_pattern (SAMPLE_FILTER_IDX, arg1)) + fprintf (stderr, GTXT ("Error: Invalid filter pattern specification %s\n"), arg1); + proc_cmd (SAMPLE_LIST, cparam, NULL, NULL); + break; + case THREAD_LIST: + filter_list (THREAD_LIST); + break; + case THREAD_SELECT: + if (arg1 && !dbev->set_pattern (THREAD_FILTER_IDX, arg1)) + fprintf (stderr, GTXT ("Error: Invalid filter pattern specification %s\n"), arg1); + proc_cmd (THREAD_LIST, cparam, NULL, NULL); + break; + case LWP_LIST: + filter_list (LWP_LIST); + break; + case LWP_SELECT: + if (arg1 && !dbev->set_pattern (LWP_FILTER_IDX, arg1)) + fprintf (stderr, GTXT ("Error: Invalid filter pattern specification %s\n"), arg1); + proc_cmd (LWP_LIST, cparam, NULL, NULL); + break; + case CPU_LIST: + filter_list (CPU_LIST); + break; + case CPU_SELECT: + if (arg1 && !dbev->set_pattern (CPU_FILTER_IDX, arg1)) + fprintf (stderr, GTXT ("Error: Invalid filter pattern specification %s\n"), arg1); + proc_cmd (CPU_LIST, cparam, NULL, NULL); + break; + case FILTERS: + if (arg1 != NULL) + { + if (strcmp (arg1, NTXT ("True")) == 0) + scratch = dbev->set_filter (NULL); + else + scratch = dbev->set_filter (arg1); + if (scratch != NULL) + fprintf (stderr, GTXT ("Error: %s\n"), scratch); + } + scratch = dbev->get_filter (); + fprintf (dis_file, GTXT ("current filter setting: \"%s\"\n"), + scratch == NULL ? GTXT ("<none>") : scratch); + break; + case OUTFILE: + if (arg1) + { + set_outfile (arg1, out_file, false); + if (inp_file != stdin) + dis_file = out_file; + } + break; + case APPENDFILE: + if (arg1) + { + set_outfile (arg1, out_file, true); + if (inp_file != stdin) + dis_file = out_file; + } + break; + case LIMIT: + if (arg1) + { + limit = (int) strtol (arg1, (char **) NULL, 10); + char *res = dbeSetPrintLimit (dbevindex, limit); + if (res != NULL) + fprintf (stderr, NTXT ("%s\n"), res); + } + + limit = dbeGetPrintLimit (dbevindex); + fprintf (stderr, GTXT ("Print limit set to %d\n"), limit); + break; + case NAMEFMT: + if (arg1) + { + status = dbev->set_name_format (arg1); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + } + else + fprintf (stderr, GTXT ("Error: No format has been specified.\n")); + break; + case VIEWMODE: + { + if (arg1) + { + status = dbev->set_view_mode (arg1, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + } + const char *vname = "unknown"; + int vm = dbev->get_view_mode (); + switch (vm) + { + case VMODE_USER: + vname = "user"; + break; + case VMODE_EXPERT: + vname = "expert"; + break; + case VMODE_MACHINE: + vname = "machine"; + break; + } + fprintf (stderr, GTXT ("Viewmode set to %s\n"), vname); + } + break; + + // EN_DESC does not make sense after experiments are read, but it does make sense on the command line, + // processed before the experiments are read. + case EN_DESC: + if (arg1) + { + status = dbev->set_en_desc (arg1, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + } + else + fprintf (stderr, GTXT ("Error: No descendant processing has been specified.\n")); + break; + case SETPATH: + case ADDPATH: + if (arg1) + dbeSession->set_search_path (arg1, (cmd_type == SETPATH)); + fprintf (dis_file, GTXT ("search path:\n")); + Vec_loop (char*, dbeSession->get_search_path (), index, name) + { + fprintf (dis_file, NTXT ("\t%s\n"), name); + } + break; + case PATHMAP: + { + Vector<pathmap_t*> *pathMaps = dbeSession->get_pathmaps (); + if (arg1 != NULL) + { + if (arg2 == NULL) + { + fprintf (stderr, GTXT ("Error: No replacement path prefix has been specified.\n")); + break; + } + // add this mapping to the session + char *err = Settings::add_pathmap (pathMaps, arg1, arg2); + if (err != NULL) + { + fprintf (stderr, NTXT ("%s"), err); + free (err); + } + } + fprintf (dis_file, GTXT ("Path mappings: from -> to\n")); + for (int i = 0, sz = pathMaps->size (); i < sz; i++) + { + pathmap_t *thismap = pathMaps->get (i); + fprintf (dis_file, NTXT ("\t`%s' -> `%s'\n"), thismap->old_prefix, thismap->new_prefix); + } + } + break; + case SAMPLE_DETAIL: + if (get_exp_id (arg1, bgn_index, end_index) != -1) + { + cd = new er_print_experiment (dbev, bgn_index, end_index, false, + false, false, true, true); + print_cmd (cd); + delete cd; + } + break; + case STATISTICS: + if (get_exp_id (arg1, bgn_index, end_index) != -1) + { + cd = new er_print_experiment (dbev, bgn_index, end_index, false, + false, true, true, false); + print_cmd (cd); + delete cd; + } + break; + case PRINTMODE: + { + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("printmode is set to `%s'\n\n"), dbeGetPrintModeString (dbevindex)); + break; + } + char *s = dbeSetPrintMode (dbevindex, arg1); + if (s != NULL) + { + fprintf (stderr, NTXT ("%s\n"), s); + break; + } + fprintf (stderr, GTXT ("printmode is set to `%s'\n\n"), dbeGetPrintModeString (dbevindex)); + } + break; + case HEADER: + if (get_exp_id (arg1, bgn_index, end_index) != -1) + { + cd = new er_print_experiment (dbev, bgn_index, end_index, false, + true, false, false, false); + print_cmd (cd); + delete cd; + } + break; + case COMPARE: + if (arg1 == NULL) + { + fprintf (out_file, GTXT ("The argument to `compare' must be `on', `off', `delta', or `ratio'\n\n")); + break; + } + else + { + int cmp; + if (strcasecmp (arg1, NTXT ("OFF")) == 0 || strcmp (arg1, NTXT ("0")) == 0) + cmp = CMP_DISABLE; + else if (strcasecmp (arg1, NTXT ("ON")) == 0 || strcmp (arg1, NTXT ("1")) == 0) + cmp = CMP_ENABLE; + else if (strcasecmp (arg1, NTXT ("DELTA")) == 0) + cmp = CMP_DELTA; + else if (strcasecmp (arg1, NTXT ("RATIO")) == 0) + cmp = CMP_RATIO; + else + { + fprintf (out_file, GTXT ("The argument to `compare' must be `on', `off', `delta', or `ratio'\n\n")); + break; + } + int oldMode = dbev->get_compare_mode (); + dbev->set_compare_mode (cmp); + if (oldMode != cmp) + { + dbev->reset_data (false); + dbeSession->reset_data (); + } + } + break; + case LEAKS: + if (!dbeSession->is_leaklist_available ()) + { + fprintf (out_file, GTXT ("\nHeap trace information was not requested when recording experiments\n\n")); + break; + } + if (dbev->comparingExperiments ()) + { // XXXX show warning for compare + fprintf (out_file, GTXT ("\nNot available when comparing experiments\n\n")); + break; + } + cd = new er_print_leaklist (dbev, true, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case ALLOCS: + if (!dbeSession->is_leaklist_available ()) + { + fprintf (out_file, GTXT ("\nHeap trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_leaklist (dbev, false, true, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case HEAP: + if (!dbeSession->is_heapdata_available ()) + { + fprintf (out_file, GTXT ("Heap trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_heapactivity (dbev, Histable::HEAPCALLSTACK, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case HEAPSTAT: + if (!dbeSession->is_heapdata_available ()) + { + fprintf (out_file, GTXT ("Heap trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_heapactivity (dbev, Histable::HEAPCALLSTACK, true, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case IOACTIVITY: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("I/O trace information was not requested when recording experiments\n\n")); + break; + } + if (dbev->comparingExperiments ()) + { // XXXX show warning for compare + fprintf (out_file, GTXT ("\nNot available when comparing experiments\n\n")); + break; + } + cd = new er_print_ioactivity (dbev, Histable::IOACTFILE, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case IOVFD: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("I/O trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_ioactivity (dbev, Histable::IOACTVFD, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case IOCALLSTACK: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("I/O trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_ioactivity (dbev, Histable::IOCALLSTACK, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case IOSTAT: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("I/O trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_ioactivity (dbev, Histable::IOACTVFD, true, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case HELP: + Command::print_help(whoami, false, true, out_file); + break; + case VERSION_cmd: + Application::print_version_info (); + break; + case SCRIPT: + if (arg1) + { + ck_file = fopen (arg1, NTXT ("r")); + if (ck_file == NULL) + fprintf (stderr, GTXT ("Error: Script file cannot be opened: %s\n"), arg1); + else + { + save_file = inp_file; + inp_file = ck_file; + proc_script (); + inp_file = save_file; + } + } + else + fprintf (stderr, GTXT ("Error: No filename has been specified.\n")); + break; + case QUIT: + exit (0); + break; + + // commands relating to index Objects + case INDXOBJ: + if ((cparam == -1) && (arg1 == NULL)) + { + fprintf (stderr, GTXT ("Error: No index object name has been specified.\n")); + break; + } + // automatically load machine model if applicable + dbeDetectLoadMachineModel (dbevindex); + indxobj (arg1, cparam); + break; + case INDXOBJLIST: + // automatically load machine model if applicable + dbeDetectLoadMachineModel (dbevindex); + indxo_list (false, out_file); + break; + + // define a new IndexObject type + case INDXOBJDEF: + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("Error: No index object name has been specified.\n")); + break; + } + if (arg2 == NULL) + { + fprintf (stderr, GTXT ("Error: No index-expr has been specified.\n")); + break; + } + indxo_define (arg1, arg2, arg3, arg4); + break; + + // the commands following this are unsupported/hidden + case IFREQ: + if (!dbeSession->is_ifreq_available ()) + { + fprintf (out_file, GTXT ("\nInstruction frequency data was not requested when recording experiments\n\n")); + break; + } + ifreq (); + break; + case DUMPNODES: + dump_nodes (); + break; + case DUMPSTACKS: + dump_stacks (); + break; + case DUMPUNK: + dump_unk_pcs (); + break; + case DUMPFUNC: + dump_funcs (arg1); + break; + case DUMPDOBJS: + dump_dataobjects (arg1); + break; + case DUMPMAP: + dump_map (); + break; + case DUMPENTITIES: + dump_entities (); + break; + case DUMP_PROFILE: + dbev->dump_profile (out_file); + break; + case DUMP_SYNC: + dbev->dump_sync (out_file); + break; + case DUMP_HWC: + dbev->dump_hwc (out_file); + break; + case DUMP_HEAP: + if (!dbeSession->is_leaklist_available ()) + { + fprintf (out_file, GTXT ("\nHeap trace information was not requested when recording experiments\n\n")); + break; + } + dbev->dump_heap (out_file); + break; + case DUMP_IOTRACE: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("\nI/O trace information was not requested when recording experiments\n\n")); + break; + } + dbev->dump_iotrace (out_file); + break; + case DMEM: + if (arg1 == NULL) + fprintf (stderr, GTXT ("Error: No sample has been specified.\n")); + else + { + Experiment *exp = dbeSession->get_exp (0); + if (exp != NULL) + exp->DBG_memuse (arg1); + } + break; + case DUMP_GC: + if (!dbeSession->has_java ()) + { + fprintf (out_file, GTXT ("\nJava garbage collection information was not requested when recording experiments\n\n")); + break; + } + dbev->dump_gc_events (out_file); + break; + case DKILL: + { + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("Error: No process has been specified.\n")); + break; + } + if (arg2 == NULL) + { + fprintf (stderr, GTXT ("Error: No signal has been specified.\n")); + break; + } + pid_t p = (pid_t) atoi (arg1); + int signum = atoi (arg2); + char *ret = dbeSendSignal (p, signum); + if (ret != NULL) + fprintf (stderr, GTXT ("Error: %s"), ret); + } + break; + case PROCSTATS: + dump_stats (); + break; + case ADD_EXP: + case OPEN_EXP: + if (arg1 == NULL) + fprintf (stderr, GTXT ("Error: No experiment name has been specified.\n")); + else + { + Vector<Vector<char*>*> *groups = new Vector<Vector<char*>*>(1); + Vector<char*> *list = new Vector<char*>(1); + list->append (arg1); + groups->append (list); + char *res = dbeOpenExperimentList (dbevindex, groups, cmd_type == OPEN_EXP); + if (cmd_type == OPEN_EXP) + fprintf (stderr, GTXT ("Previously loaded experiment have been dropped.\n")); + if (res != NULL) + fprintf (stderr, NTXT ("%s"), res); + else + fprintf (stderr, GTXT ("Experiment %s has been loaded\n"), arg1); + free (res); + delete list; + delete groups; + } + break; + case DROP_EXP: + { + if (arg1 == NULL) + fprintf (stderr, GTXT ("Error: No experiment name has been specified.\n")); + else + { + int exp_index = dbeSession->find_experiment (arg1); + if (exp_index < 0) + fprintf (stderr, GTXT ("Error: experiment %s has not been opened.\n"), arg1); + else + { + Vector<int> *expid = new Vector<int> (1); + expid->append (exp_index); + char *res = dbeDropExperiment (dbevindex, expid); + if (res != NULL) + fprintf (stderr, NTXT ("%s"), res); + else + fprintf (stderr, GTXT ("Experiment %s has been dropped\n"), arg1); + delete expid; + free (res); + } + } + } + break; + case HHELP: + // automatically load machine model if applicable + dbeDetectLoadMachineModel (dbevindex); + Command::print_help (whoami, false, false, out_file); + fprintf (out_file, NTXT ("\n")); + indxo_list (false, out_file); + fprintf (out_file, NTXT ("\n")); + mo_list (false, out_file); + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (out_file, GTXT ("\nSee gprofng(1) for more details\n")); + break; + case QQUIT: + was_QQUIT = true; + return; + default: + fprintf (stderr, GTXT ("Error: Invalid option\n")); + break; + } + + // check for any processing error messages + dump_proc_warnings (); + fflush (out_file); +} + +#define MAX_NUM_HEADER 4 + +void +er_print::disp_list (int num_header, int size, int align[], char *header[], + char **lists[]) +{ + size_t maxlen[MAX_NUM_HEADER]; + char fmt[MAX_NUM_HEADER][64]; + if (num_header > MAX_NUM_HEADER) + abort (); + for (int i = 0; i < num_header; i++) + { + maxlen[i] = strlen (header[i]); + for (int j = 0; j < size; j++) + { + size_t len = strlen (lists[i][j]); + if (maxlen[i] < len) + maxlen[i] = len; + } + + // get format string + if ((align[i] == -1) && (i == num_header - 1)) + snprintf (fmt[i], sizeof (fmt[i]), NTXT ("%%s ")); + else + snprintf (fmt[i], sizeof (fmt[i]), NTXT ("%%%ds "), (int) (align[i] * maxlen[i])); + + // write header + fprintf (out_file, fmt[i], header[i]); + } + putc ('\n', out_file); + + // write separator "===" + size_t np = 0; + for (int i = 0; (i < num_header) && (np < 132); i++) + { + size_t nc = maxlen[i]; + if (nc + np > 132) + nc = 132 - np; + for (size_t j = 0; j < nc; j++) + putc ('=', out_file); + putc (' ', out_file); + np += nc + 1; + } + putc ('\n', out_file); + + // write lists + for (int j = 0; j < size; j++) + { + for (int i = 0; i < num_header; i++) + fprintf (out_file, fmt[i], lists[i][j]); + putc ('\n', out_file); + } +} + +void +er_print::exp_list () +{ + int size, index; + int align[MAX_NUM_HEADER]; + char *header[MAX_NUM_HEADER]; + char **lists[MAX_NUM_HEADER]; + + align[0] = 1; // right-justify + align[1] = 1; // right-justify + align[2] = 1; // right-justify + align[3] = -1; // left-justify + header[0] = GTXT ("ID"); + header[1] = GTXT ("Sel"); + header[2] = GTXT ("PID"); + header[3] = GTXT ("Experiment"); + + size = dbeSession->nexps (); + lists[0] = new char*[size]; + lists[1] = new char*[size]; + lists[2] = new char*[size]; + lists[3] = new char*[size]; + for (index = 0; index < size; index++) + { + lists[0][index] = dbe_sprintf (NTXT ("%d"), index + 1); + lists[1][index] = strdup (dbev->get_exp_enable (index) ? GTXT ("yes") : GTXT ("no")); + lists[2][index] = dbe_sprintf (NTXT ("%d"), dbeSession->get_exp (index)->getPID ()); + lists[3][index] = strdup (dbeSession->get_exp (index)->get_expt_name ()); + } + disp_list (4, size, align, header, lists); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < size; j++) + free (lists[i][j]); + delete[] lists[i]; + } +} + +void +er_print::describe () +{ + Vector<void*> *res = dbeGetFilterKeywords (dbev->vindex); + if (res == NULL) + return; + Vector <char*> *kwCategoryI18N = (Vector<char*>*) res->fetch (1); + Vector <char*> *kwKeyword = (Vector<char*>*) res->fetch (3); + Vector <char*> *kwFormula = (Vector<char*>*) res->fetch (4); + Vector <char*> *kwDescrip = (Vector<char*>*) res->fetch (5); + Vector <void*> *kwEnumDescs = (Vector<void*>*) res->fetch (6); + String sectionFormat = NTXT ("\n------ %s ------\n"); + String categoryFormat = NTXT ("\n%s\n"); + String keywordFormat = NTXT (" %-20s %s\n"); + String empty = NTXT (""); + String previousCategory = empty; + + for (int i = 0; i < kwKeyword->size (); i++) + { + if (kwKeyword->fetch (i) == NULL) + { + fprintf (dis_file, sectionFormat, kwCategoryI18N->fetch (i)); + continue; + } + String cat = kwCategoryI18N->fetch (i); + if (dbe_strcmp (previousCategory, cat) != 0) + fprintf (dis_file, categoryFormat, cat); + previousCategory = cat; + Vector <String> *enumDescs = (Vector <String> *) kwEnumDescs->fetch (i); + String keyword = kwKeyword->fetch (i); + if (kwDescrip->fetch (i) != NULL) + { + fprintf (dis_file, keywordFormat, keyword, kwDescrip->fetch (i)); + keyword = empty; + } + if (kwFormula->fetch (i) != NULL) + { + fprintf (dis_file, keywordFormat, keyword, kwFormula->fetch (i)); + keyword = empty; + continue; + } + int numEnums = enumDescs != NULL ? enumDescs->size () : 0; + for (int jj = 0; jj < numEnums; jj++) + { + fprintf (dis_file, keywordFormat, keyword, enumDescs->fetch (jj)); + keyword = empty; + } + } + destroy (res); +} + +void +er_print::obj_list () +{ + LoadObject *lo; + int index; + int align[MAX_NUM_HEADER]; + char *header[MAX_NUM_HEADER]; + char **lists[MAX_NUM_HEADER]; + Vector<LoadObject*> *text_segments = dbeSession->get_text_segments (); + if (text_segments->size () == 0) + { + fprintf (dis_file, GTXT ("There are no load objects in this experiment\n")); + return; + } + align[0] = -1; // left-justify + align[1] = -1; // left-justify + align[2] = -1; // left-justify + align[3] = -1; // left-justify + header[0] = GTXT ("Sel"); + header[1] = GTXT ("Load Object"); + header[2] = GTXT ("Index"); + header[3] = GTXT ("Path"); + + int size = text_segments->size (); + lists[0] = new char*[size]; + lists[1] = new char*[size]; + lists[2] = new char*[size]; + lists[3] = new char*[size]; + + char *lo_name; + int new_index = 0; + Vec_loop (LoadObject*, text_segments, index, lo) + { + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + continue; + } + LibExpand expand = dbev->get_lo_expand (lo->seg_idx); + switch (expand) + { + case LIBEX_SHOW: + lists[0][new_index] = dbe_strdup (GTXT ("show")); + break; + case LIBEX_HIDE: + lists[0][new_index] = dbe_strdup (GTXT ("hide")); + break; + case LIBEX_API: + lists[0][new_index] = dbe_strdup (GTXT ("API-only")); + break; + } + lists[1][new_index] = dbe_strdup (lo_name); + lists[2][new_index] = dbe_sprintf (NTXT ("%d"), lo->seg_idx); + lists[3][new_index] = dbe_strdup (lo->get_pathname ()); + new_index++; + } + disp_list (4, new_index, align, header, lists); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < new_index; j++) + free (lists[i][j]); + delete[] lists[i]; + } + delete text_segments; +} + +void +er_print::seg_list () +{ + LoadObject *lo; + int index; + int align[MAX_NUM_HEADER]; + char *header[MAX_NUM_HEADER]; + char **lists[MAX_NUM_HEADER]; + + // XXX seg_list only prints text segments; should extend to all + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + if (lobjs->size () == 0) + { + fprintf (dis_file, GTXT ("There are no segments in this experiment\n")); + return; + } + align[0] = -1; // left-justify + align[1] = 1; // right-justify + align[2] = -1; // left-justify + header[0] = GTXT ("Sel"); + header[1] = GTXT ("Size"); + header[2] = GTXT ("Segment"); + + int size = lobjs->size (); + lists[0] = new char*[size]; + lists[1] = new char*[size]; + lists[2] = new char*[size]; + + char *lo_name; + int new_index = 0; + Vec_loop (LoadObject*, lobjs, index, lo) + { + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + continue; + } + bool expand = dbev->get_lo_expand (lo->seg_idx); + lists[0][new_index] = strdup (expand ? GTXT ("yes") : GTXT ("no")); + lists[1][new_index] = dbe_sprintf (NTXT ("%lld"), (ll_t) lo->get_size ()); + lists[2][new_index] = strdup (lo->get_pathname ()); + new_index++; + } + + disp_list (3, new_index, align, header, lists); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < new_index; j++) + free (lists[i][j]); + delete[] lists[i]; + } + delete lobjs; +} + +void +er_print::filter_list (CmdType cmd_type) +{ + FilterNumeric *select; + int index; + int align[MAX_NUM_HEADER]; + char *header[MAX_NUM_HEADER]; + char **lists[MAX_NUM_HEADER]; + char *pattern; + + // first ensure that the data has been read + MetricList *mlist = dbev->get_metric_list (MET_INDX); + Hist_data *data = dbev->get_hist_data (mlist, Histable::INDEXOBJ, 0, Hist_data::ALL); + delete data; + + align[0] = 1; // right-justify + align[1] = -1; // left-justify + align[2] = 1; // right-justify + align[3] = 1; // right-justify + header[0] = GTXT ("Exp"); + header[1] = GTXT ("Sel"); + header[2] = GTXT ("Total"); + header[3] = GTXT ("Status"); + + int size = dbeSession->nexps (); + lists[0] = new char*[size]; + lists[1] = new char*[size]; + lists[2] = new char*[size]; + lists[3] = new char*[size]; + int new_index = 0; + for (index = 0; index < size; index++) + { + switch (cmd_type) + { + case SAMPLE_LIST: + select = dbev->get_FilterNumeric (index, SAMPLE_FILTER_IDX); + break; + case THREAD_LIST: + select = dbev->get_FilterNumeric (index, THREAD_FILTER_IDX); + break; + case LWP_LIST: + select = dbev->get_FilterNumeric (index, LWP_FILTER_IDX); + break; + case CPU_LIST: + select = dbev->get_FilterNumeric (index, CPU_FILTER_IDX); + break; + default: + abort (); // internal error + } + if (select == NULL) + continue; + lists[0][new_index] = dbe_sprintf (NTXT ("%d"), index + 1); + pattern = dbev->get_exp_enable (index) ? select->get_pattern () : NULL; + lists[1][new_index] = strdup (pattern && *pattern ? pattern : GTXT ("none")); + lists[2][new_index] = dbe_sprintf (NTXT ("%lld"), (ll_t) select->nelem ()); + lists[3][new_index] = select->get_status (); + new_index++; + } + disp_list (3, size, align, header, lists); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < new_index; j++) + free (lists[i][j]); + delete[] lists[i]; + } +} + +int +er_print::check_exp_id (int exp_id, char *sel) +{ + if (exp_id < 0 || exp_id >= dbeSession->nexps ()) + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\nType \"exp_list\" for a list of all experiments.\n"), + sel); + return -1; + } + return exp_id; +} + +int +er_print::get_exp_id (char *sel, int &bgn_index, int &end_index) +{ + int id, exp_id; + if (sel == NULL || strcmp (sel, NTXT ("all")) == 0) + { + // loop over all experiments + bgn_index = 0; + end_index = dbeSession->nexps () - 1; + } + else + { + id = (int) strtol (sel, (char **) NULL, 10) - 1; + exp_id = check_exp_id (id, sel); + if (exp_id == -1) + return -1; + bgn_index = end_index = exp_id; + } + return 0; +} + +void +er_print::print_objects () +{ + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + char *msg = pr_load_objects (lobjs, NTXT ("")); + delete lobjs; + fprintf (out_file, NTXT ("%s\n"), msg); + free (msg); +} + +void +er_print::print_overview () +{ + //fprintf(out_file, NTXT("%s\n"), GTXT("Not implemented yet."));//YXXX + Vector<char*> *status = dbeGetOverviewText (dbevindex); + StringBuilder sb; + sb.append (GTXT ("Experiment(s):\n\n")); + for (int i = 0; i < status->size (); i++) + sb.appendf (NTXT ("%s\n"), status->fetch (i)); + sb.append (GTXT ("Metrics:\n")); + sb.toFile (out_file); + + Vector<void*> *data = dbeGetRefMetricTree (dbevindex, false); + Vector<char *> *metric_cmds = new Vector<char *>(); + Vector<char *> *non_metric_cmds = new Vector<char *>(); + print_overview_nodes (data, 0, metric_cmds, non_metric_cmds); + Vector<void*> *values = dbeGetRefMetricTreeValues (0, metric_cmds, non_metric_cmds); + print_overview_tree (data, 0, values, metric_cmds, non_metric_cmds); + + StringBuilder sb2; + sb2.append (GTXT ("\nNotes: '*' indicates hot metrics, '[X]' indicates currently enabled metrics.\n")); + sb2.append (GTXT (" The metrics command can be used to change selections. The metric_list command lists all available metrics.\n")); + sb2.toFile (out_file); +} + +void +er_print::print_overview_nodes (Vector<void*> * data, int level, Vector<char *> *metric_cmds, Vector<char *> *non_metric_cmds) +{ + Vector<void*> *fields = (Vector<void*> *) data->fetch (0); + Vector<void*> *children = (Vector<void*> *) data->fetch (1); + char *name = ((Vector<char*> *)fields->fetch (0))->fetch (0); + int vstyles_capable = ((Vector<int>*) fields->fetch (5))->fetch (0); //bitmask e.g.VAL_TIMEVAL + bool has_value = ((Vector<bool>*) fields->fetch (10))->fetch (0); + bool selectable = (vstyles_capable != 0) ? true : false; + if (selectable) + metric_cmds->append (name); + else if (has_value) + non_metric_cmds->append (name); + + level++; + for (int i = 0; i < children->size (); i++) + print_overview_nodes ((Vector<void*> *)(children->fetch (i)), level, metric_cmds, non_metric_cmds); +} + +void +er_print::print_overview_tree (Vector<void*> * data, int level, Vector<void*> * values, Vector<char *> *metric_cmds, Vector<char *> *non_metric_cmds) +{ + Vector<void*> * fields = (Vector<void*> *) data->fetch (0); + Vector<void*> * children = (Vector<void*> *) data->fetch (1); + char *name = ((Vector<char*> *)fields->fetch (0))->fetch (0); + char *username = ((Vector<char*> *)fields->fetch (1))->fetch (0); + int flavors = ((Vector<int>*) fields->fetch (3))->fetch (0); //bitmask e.g. EXCLUSIVE + int vstyles_capable = ((Vector<int>*) fields->fetch (5))->fetch (0); //bitmask e.g.VAL_TIMEVAL + // bool aggregation = ((Vector<bool>*) fields->fetch(9))->fetch(0); + // bool has_value = ((Vector<bool>*) fields->fetch(10))->fetch(0); + char *unit = ((Vector<char*> *) fields->fetch (11))->fetch (0); + + StringBuilder sb; + for (int i = 0; i < level * 2; i++) + sb.append (NTXT (" ")); // NOI18N + + bool selectable = (vstyles_capable != 0) ? true : false; + if (selectable) + { + bool isSelected = dbev->get_metric_list (MET_NORMAL)->find_metric_by_name (name) == NULL ? false : true; + if (isSelected) + sb.append (NTXT ("[X]")); + else + sb.append (NTXT ("[ ]")); + } + if ((unit != NULL && dbe_strcmp (unit, UNIT_SECONDS) == 0) + || (unit == NULL && vstyles_capable & VAL_TIMEVAL)) + unit = GTXT ("Seconds"); + + bool isHiddenInOverview = ((flavors & BaseMetric::STATIC) != 0); + if (name != NULL && dbe_strcmp (name, L1_STATIC) == 0) + isHiddenInOverview = true; + if (!dbeSession->has_java () && name != NULL && dbe_strcmp (name, L1_GCDURATION) == 0) + isHiddenInOverview = true; + if (isHiddenInOverview) + return; + + sb.append (username == NULL ? NTXT ("") : username); // NOI18N + int show = 0; + if (name == NULL) + show = 0; + else if (strstr (name, NTXT ("PROFDATA_TYPE_")) == NULL) + show = 1; + + if (show) + { + sb.append (username == NULL ? NTXT ("") : NTXT (" - ")); // NOI18N + sb.append (name == NULL ? NTXT ("") : name); // NOI18N + } + + // "Bugs 16624403 and 19539622" (leave this string intact for searches) + // add an extra condition for now + // once we have proper fixes, eliminate test on Bug16624402_extra_condition + int Bug16624402_extra_condition = 1; + if (username) + { + if (strcmp (username, NTXT ("Block Covered %")) == 0) Bug16624402_extra_condition = 0; + if (strcmp (username, NTXT ("Instr Covered %")) == 0) Bug16624402_extra_condition = 0; + } + if (Bug16624402_extra_condition > 0 && values->size () > 0) + { + Vector<void*> * valueColumns = (Vector<void*> *)values->fetch (0); + Vector<void*> * highlightColumns = (Vector<void*> *)values->fetch (1); + int jj = 0; + int found = 0; + for (jj = 0; jj < valueColumns->size (); jj++) + { + const char *value_name = ""; + if (jj < metric_cmds->size ()) + value_name = metric_cmds->fetch (jj); + else + value_name = non_metric_cmds->fetch (jj - metric_cmds->size ()); + if (dbe_strcmp (value_name, name) != 0) + continue; + else + { + found = 1; + break; + } + } + if (found) + { + Vector<void*> * valueVec = (Vector<void*> *)valueColumns->fetch (jj); + Vector<bool> * highlights = (Vector<bool> *)highlightColumns->fetch (jj); + for (int kk = 0; kk < valueVec->size (); kk++) + { + char * value_str; + int show_value = 0; + switch (valueVec->type ()) + { + case VEC_INTEGER: + value_str = dbe_sprintf (NTXT ("%ld"), (long) (((Vector<int> *)valueVec)->fetch (kk))); + show_value = 1; + break; + case VEC_DOUBLE: + value_str = dbe_sprintf (NTXT ("%.3f"), (double) (((Vector<double> *)valueVec)->fetch (kk))); + show_value = 1; + break; + case VEC_LLONG: + value_str = dbe_sprintf (NTXT ("%lld"), (long long) (((Vector<long> *)valueVec)->fetch (kk))); + show_value = 1; + break; + case VEC_STRING: + value_str = NTXT (""); + break; + default: + value_str = NTXT (""); + } + if (show_value) + { + if (kk == 0) + { + sb.append (unit == NULL ? NTXT ("") : NTXT (" (")); + sb.append (unit == NULL ? NTXT ("") : unit); + sb.append (unit == NULL ? NTXT ("") : NTXT (")")); + sb.append (NTXT (":")); + } + bool highlight = highlights->fetch (kk); + const char * hilite = highlight ? NTXT ("*") : NTXT (""); + sb.append (NTXT (" [")); + sb.append (hilite); + sb.append (value_str); + sb.append (NTXT ("]")); + } + } + } + } + sb.append (NTXT ("\n")); + sb.toFile (out_file); + level++; + for (int i = 0; i < children->size (); i++) + print_overview_tree ((Vector<void*> *)(children->fetch (i)), level, values, metric_cmds, non_metric_cmds); +} + +void +er_print::print_segments () +{ + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + char *msg = pr_load_objects (lobjs, NTXT ("")); + delete lobjs; + fprintf (dis_file, NTXT ("Not implemented yet!\n")); + free (msg); +} + +void +er_print::print_dobj (Print_mode mode, MetricList *mlist1, + char *dobj_name, char *sel) +{ + Hist_data *hist_data = NULL; + char *errstr; + er_print_common_display *cd; + int list_limit = limit; + Histable *sobj = NULL; + Dprintf (DEBUG_DATAOBJ, NTXT ("er_print::print_dobj(mode=%d,dobj=%s,sel=%s)\n"), + mode, (dobj_name == NULL) ? NTXT ("0") : dobj_name, (sel == NULL) ? NTXT ("0") : sel); + char *name = dbev->getSort (MET_DATA); + switch (mode) + { + case MODE_LIST: + hist_data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::ALL); + break; + case MODE_DETAIL: + // if specified, find the dataobject from the name + if (dobj_name && strcmp (dobj_name, NTXT ("<All>"))) + { + if (!dbeSession->find_obj (dis_file, inp_file, sobj, dobj_name, + sel, Histable::DOBJECT, (inp_file != stdin))) + return; + if (sobj == NULL) + { // dataobject/segment not found + hist_data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::DETAIL); + if (!dbeSession->find_obj (dis_file, inp_file, sobj, dobj_name, + sel, Histable::DOBJECT, (inp_file != stdin))) + return; + if (sobj == NULL) + { // dataobject/segment not found + fprintf (stderr, GTXT ("Error: No dataobject with given name `%s' found.\n"), + dobj_name); + return; + } + } + + list_limit = 1; + } + if (!hist_data) + hist_data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::DETAIL); + break; + case MODE_ANNOTATED: + hist_data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::LAYOUT); + break; + default: // MODE_GPROF is not relevant for DataObjects + abort (); + } + + if (hist_data->get_status () != Hist_data::SUCCESS) + { + // XXXX is this error message adequate? + errstr = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + if (errstr) + { + fprintf (stderr, GTXT ("Error: %s\n"), errstr); + free (errstr); + } + delete hist_data; + return; + } + cd = (er_print_common_display *) new er_print_histogram (dbev, hist_data, + hist_data->get_metric_list (), mode, list_limit, name, sobj, false, false); + free (name); + print_cmd (cd); + + delete hist_data; + delete cd; +} + +void +er_print::print_func (Histable::Type type, Print_mode mode, MetricList *mlist1, + MetricList *mlist2, char *func_name, char *sel) +{ + Hist_data *hist_data; + Hist_data::HistItem *hitem; + int index; + char *errstr; + int list_limit = limit; + Histable *sobj = NULL; + MetricList *mlist; + StringBuilder sb; + char *sname = dbev->getSort (MET_NORMAL); + sb.append (sname); + free (sname); + + switch (mode) + { + case MODE_DETAIL: + { + // The first metric list, mlist1, is only used to pick out the sort + // mlist2 is the one used to generate the data + char *prevsort = NULL; + // if specified, find the function from the function name + if (func_name && strcmp (func_name, NTXT ("<All>"))) + { + if ((!dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::FUNCTION, (inp_file != stdin)) || (sobj == NULL)) && + !dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::LOADOBJECT, (inp_file != stdin))) + return; + if (sobj == NULL) + { // function/segment object not found + fprintf (stderr, GTXT ("Error: No function with given name `%s' found.\n"), + func_name); + return; + } + list_limit = 1; + } + else + { + // find the sort metric from the reference list + prevsort = mlist2->get_sort_cmd (); + + // find the current sort metric from the current list + char *cursort = mlist1->get_sort_cmd (); + + // find the corresponding metric in the reference list + (void) mlist2->set_sort (cursort, false); + free (cursort); + // if it fails, nothing is needed + } + hist_data = dbev->get_hist_data (mlist2, type, 0, Hist_data::ALL); + + // restore + if (sobj == NULL) + { + if (prevsort == NULL) + abort (); + (void) mlist2->set_sort (prevsort, false); + } + mlist = mlist2; + free (prevsort); + break; + } + case MODE_GPROF: + // if specified, find the function from the function name + if (func_name && strcmp (func_name, NTXT ("<All>"))) + { + if (!dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::FUNCTION, (inp_file != stdin))) + return; + if (sobj == NULL) + { // function/segment object not found + fprintf (stderr, GTXT ("Error: No function with given name `%s' found.\n"), + func_name); + return; + } + list_limit = 1; + sb.setLength (0); + } + sb.append (GTXT ("\nCallers and callees sorted by metric: ")); + sname = dbev->getSort (MET_CALL); + sb.append (sname); + free (sname); + + // Use mlist2 to generate the sort order. + // mlist1 is used to generate the data. + hist_data = dbev->get_hist_data (mlist2, type, 0, Hist_data::ALL); + mlist = mlist1; + break; + default: + hist_data = dbev->get_hist_data (mlist1, type, 0, Hist_data::ALL); + mlist = mlist1; + } + + if (hist_data->get_status () != Hist_data::SUCCESS) + { + errstr = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + if (errstr) + { + fprintf (stderr, GTXT ("Error: %s\n"), errstr); + free (errstr); + } + delete hist_data; + return; + } + + if (type == Histable::FUNCTION) + { + for (index = 0; index < hist_data->size (); index++) + { + hitem = hist_data->fetch (index); + if (hitem->obj->get_type () == Histable::FUNCTION) + // fetch the name, since that will force a format conversion + ((Function *) hitem->obj)->get_name (); + } + } + + char *name = sb.toString (); + er_print_histogram *cd = new er_print_histogram (dbev, hist_data, + mlist, mode, list_limit, name, sobj, false, false); + print_cmd (cd); + delete hist_data; + free (name); + delete cd; +} + +void +er_print::print_gprof (CmdType cmd_type, char *func_name, char *sel) +{ + Histable *sobj = NULL; + if (func_name != NULL) + { + if ((!dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::FUNCTION, (inp_file != stdin)) + || sobj == NULL) + && !dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::LOADOBJECT, (inp_file != stdin))) + return; + if (sobj == NULL) + { // function/segment object not found + fprintf (stderr, GTXT ("Error: No function with given name `%s' found.\n"), + func_name); + return; + } + } + if (cmd_type == CPREPEND) + { + if (sobj == NULL) + { + fprintf (stderr, GTXT ("Error: No function name has been specified.\n")); + return; + } + cstack->insert (0, sobj); + } + else if (cmd_type == CAPPEND) + { + if (sobj == NULL) + { + fprintf (stderr, GTXT ("Error: No function name has been specified.\n")); + return; + } + cstack->append (sobj); + } + else if (cmd_type == CSINGLE) + { + if (sobj != NULL) + { + cstack->reset (); + cstack->append (sobj); + } + else if (cstack->size () == 0) + { + fprintf (stderr, GTXT ("Error: No function name has been specified.\n")); + return; + } + } + else if (cmd_type == CRMFIRST) + { + if (cstack->size () <= 1) + { + fprintf (stderr, GTXT ("Warning: there is only one function in the stack segment; cannot remove it.\n")); + return; + } + cstack->remove (0); + } + else if (cmd_type == CRMLAST) + { + if (cstack->size () <= 1) + { + fprintf (stderr, GTXT ("Warning: there is only one function in the stack segment; cannot remove it.\n")); + return; + } + cstack->remove (cstack->size () - 1); + } + + er_print_gprof *cd = new er_print_gprof (dbev, cstack); + print_cmd (cd); + delete cd; +} + +/* + * Method print_ctree() prints Functions Call Tree. + */ +void +er_print::print_ctree (CmdType cmd_type) +{ + if (cmd_type != CALLTREE) + { + fprintf (stderr, GTXT ("Error: Invalid command type: %d\n"), cmd_type); + return; + } + + Histable *sobj = dbeSession->get_Total_Function (); + Vector<Histable*> *ctree_cstack = new Vector<Histable*>(); + ctree_cstack->reset (); + er_print_ctree *cd = new er_print_ctree (dbev, ctree_cstack, sobj, limit); + print_cmd (cd); + delete ctree_cstack; + delete cd; +} + +void +er_print::memobj (char *name, int cparam) +{ + int type; + if (name != NULL) + { + // find the memory object index for the name + MemObjType_t *mot = MemorySpace::findMemSpaceByName (name); + if (mot == NULL) + { + // unknown type, report the error + fprintf (stderr, GTXT ("Error: Unknown Memory Object type: %s\n"), name); + return; + } + type = mot->type; + } + else + { + MemObjType_t *mot = MemorySpace::findMemSpaceByIndex (cparam); + if (mot == NULL) + { + // unknown type, report the error + fprintf (stderr, GTXT ("Error: Unknown Memory Object type: %s\n"), name); + return; + } + type = cparam; + } + dbePrintData (0, DSP_MEMOBJ, type, NULL, NULL, out_file); +} + +void +er_print::mo_define (char *moname, char *mo_index_exp, char *machmodel, char *short_desc, char *long_desc) +{ + char *ret = MemorySpace::mobj_define (moname, mo_index_exp, machmodel, short_desc, long_desc); + if (ret != NULL) + fprintf (stderr, GTXT ("mobj_define for %s failed: %s\n"), moname, ret); +} + +void +er_print::mo_list (bool showtab, FILE *outf) +{ + Vector<bool> *mtab = NULL; + Vector<void*>*res = MemorySpace::getMemObjects (); + if (showtab) + mtab = dbev->get_MemTabState (); + if (res == NULL) + // Since we checked already, this is an internal error + abort (); + + // unpack the return + // Vector<char*> *index = (Vector<int> *)res->fetch(0); // not used + Vector<char*> *mo_names = (Vector<char*> *)res->fetch (1); + // Vector<char*> *mnemonic = (Vector<char> *)res->fetch(2); // not used + Vector<char*> *mo_expr = (Vector<char*> *)res->fetch (3); + Vector<char*> *mo_mach_m = (Vector<char*> *)res->fetch (4); + // Vector<char*> *tmpOrder = (Vector<int> *)res->fetch(5); // not used + + int size = mo_names->size (); + if (size == 0) + { + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (outf, GTXT (" No Memory Object Types Defined\n")); + } + else + { + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (outf, GTXT (" Memory Object Types Available:\n")); + else + fprintf (outf, GTXT ("*Memory Object Types*\n")); + for (int i = 0; i < size; i++) + { + if (mtab) + fprintf (outf, NTXT (" %c %s\n"), mtab->fetch (i) ? 'T' : 'F', + mo_names->fetch (i)); + else + { + if (mo_mach_m->fetch (i) != NULL) + fprintf (outf, NTXT (" %s\t\t\"%s\"\t\t(machinemodel: %s)\n"), + mo_names->fetch (i), mo_expr->fetch (i), mo_mach_m->fetch (i)); + else + fprintf (outf, NTXT (" %s\t\t\"%s\"\n"), + mo_names->fetch (i), mo_expr->fetch (i)); + } + } + } + delete mo_names; + delete mo_expr; + delete mo_mach_m; + delete res; +} + +void +er_print::indxobj (char *name, int cparam) +{ + int type; + if (name != NULL) + { + // find the index object index for the name + type = dbeSession->findIndexSpaceByName (name); + if (type < 0) + { + // unknown type, report the error + fprintf (stderr, GTXT ("Error: Unknown Index Object type: %s\n"), name); + return; + } + } + else + { + char *indxname = dbeSession->getIndexSpaceName (cparam); + if (indxname == NULL) + { + // unknown type, report the error + fprintf (stderr, GTXT ("Error: Unknown Index Object type: %d\n"), cparam); + return; + } + type = cparam; + } + dbePrintData (0, DSP_INDXOBJ, type, NULL, NULL, out_file); +} + +void +er_print::indxo_define (char *ioname, char *io_index_exp, char *sdesc, char *ldesc) +{ + char *ret = dbeDefineIndxObj (ioname, io_index_exp, sdesc, ldesc); + if (ret != NULL) + fprintf (stderr, GTXT ("indxobj_define for %s failed: %s\n"), ioname, ret); +} + +void +er_print::indxo_list (bool showtab, FILE *outf) +{ + Vector<bool> *indxtab = NULL; + char *name; + char *i18n_name; + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (outf, GTXT (" Index Object Types Available:\n")); + else + fprintf (outf, GTXT ("*Index Object Types*\n")); + Vector<void*>*res = dbeGetIndxObjDescriptions (0); + if (showtab) + indxtab = dbev->get_IndxTabState (); + if (res == NULL) // If none is defined + return; + Vector<char*> *indxo_names = (Vector<char*> *)res->fetch (1); + Vector<char*> *indxo_i18nnames = (Vector<char*> *)res->fetch (3); + Vector<char*> *indxo_exprlist = (Vector<char*> *)res->fetch (5); + int size = indxo_names->size (); + for (int i = 0; i < size; i++) + { + name = indxo_names->fetch (i); + i18n_name = indxo_i18nnames->fetch (i); + if (indxtab) + { + if ((i18n_name != NULL) && (strcmp (i18n_name, name) != 0)) + fprintf (outf, NTXT (" %c %s (%s)\n"), indxtab->fetch (i) ? 'T' : 'F', + i18n_name, name); + else + fprintf (outf, NTXT (" %c %s\n"), indxtab->fetch (i) ? 'T' : 'F', name); + } + else + { + if (i18n_name != NULL && strcmp (i18n_name, indxo_names->fetch (i)) != 0) + fprintf (outf, NTXT (" %s (%s)"), i18n_name, name); + else + fprintf (outf, NTXT (" %s"), name); + } + char *exprs = indxo_exprlist->fetch (i); + if (exprs != NULL) + fprintf (outf, NTXT (" \t%s\n"), exprs); + else + fprintf (outf, NTXT ("\n")); + } + delete indxo_names; + if (showtab) + delete res; +} + +void +er_print::ifreq () +{ + dbev->ifreq (out_file); +} + +void +er_print::dump_nodes () +{ + dbev->dump_nodes (out_file); +} + +void +er_print::dump_stacks () +{ + dbeSession->dump_stacks (out_file); +} + +void +er_print::dump_unk_pcs () +{ + // Dump the nodes associated with the <Unknown> function + dbev->get_path_tree ()->dumpNodes (out_file, dbeSession->get_Unknown_Function ()); + + // Dump the nodes associated with the <no Java callstack recorded> function + Vector<Function *> *matches = dbeSession->match_func_names ("<no Java callstack recorded>", dbev->get_name_format ()); + if (matches == NULL || matches->size () == 0) + fprintf (out_file, GTXT ("No %s functions found\n"), "<no Java callstack recorded>"); + else + { + Function *fitem; + int index; + Vec_loop (Function*, matches, index, fitem) + { + dbev->get_path_tree ()->dumpNodes (out_file, fitem); + } + delete matches; + } +} + +void +er_print::dump_funcs (char *arg1) +{ + if (arg1 == NULL || strlen (arg1) == 0) + dbeSession->dump_segments (out_file); + else + { + Vector<Function *> *matches = dbeSession->match_func_names (arg1, dbev->get_name_format ()); + if (matches == NULL) + { + fprintf (stderr, GTXT ("Invalid argument `%s' -- not a regular expression\n"), arg1); + return; + } + fprintf (out_file, GTXT ("%d Function's match `%s'\n"), (int) matches->size (), arg1); + Function *fitem; + int index; + Vec_loop (Function*, matches, index, fitem) + { + fprintf (out_file, NTXT (" %5lld -- %s (%s) [%s]\n"), + (ll_t) fitem->id, fitem->get_name (), + (fitem->module ? fitem->module->file_name : NTXT ("<unknown>")), + ((fitem->module && fitem->module->loadobject) ? + get_basename (fitem->module->loadobject->get_name ()) : NTXT ("<unknown>"))); + } + delete matches; + } +} + +void +er_print::dump_dataobjects (char *arg1) +{ + // Force computation of data objects, to update master table; discard it + MetricList *mlist1 = dbev->get_metric_list (MET_DATA); + Hist_data *data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::ALL); + delete data; + + if (arg1 == NULL || strlen (arg1) == 0) + dbeSession->dump_dataobjects (out_file); + else + { + Vector<DataObject *> *matches = dbeSession->match_dobj_names (arg1); + if (matches == NULL) + { + fprintf (stderr, GTXT ("Invalid argument `%s' -- not a regular expression\n"), arg1); + return; + } + fprintf (out_file, GTXT ("%d DataObject's match `%s'\n"), (int) matches->size (), arg1); + DataObject *ditem; + int index; + Vec_loop (DataObject*, matches, index, ditem) + { + fprintf (out_file, NTXT (" %5lld -- %s\n"), (ll_t) ditem->id, ditem->get_name ()); + } + delete matches; + } +} + +void +er_print::dump_map () +{ + dbeSession->dump_map (out_file); +} + +void +er_print::dump_entities () +{ + int ent_prop_ids[] = {PROP_THRID, PROP_LWPID, PROP_CPUID, PROP_EXPID, -1}; + + // loop over experiments + for (int exp_id = 0; exp_id < dbeSession->nexps (); exp_id++) + { + Experiment *exp = dbeSession->get_exp (exp_id); + fprintf (out_file, GTXT ("Experiment %d (%s)\n"), + exp_id, exp->get_expt_name ()); + + for (int kk = 0; ent_prop_ids[kk] != -1; kk++) + { + int ent_prop_id = ent_prop_ids[kk]; + Vector<void*> *elist = dbeGetEntities (0, exp_id, ent_prop_id); + if (!elist) + continue; + Vector<int> *entity_vals = (Vector<int> *) elist->fetch (0); + Vector<char*> *jthr_names = (Vector<char*> *)elist->fetch (1); + Vector<char*> *jthr_g_names = (Vector<char*> *)elist->fetch (2); + Vector<char*> *jthr_p_names = (Vector<char*> *)elist->fetch (3); + Vector<char*> *entity_name = (Vector<char*> *)elist->fetch (4); + int nent = entity_vals->size (); + char *entName = entity_name->fetch (0); + if (!entName) + entName = NTXT ("<unknown>"); + fprintf (out_file, GTXT (" %s\n"), entName); + for (int i = 0; i < nent; i++) + fprintf (out_file, GTXT (" %s=%d: %s, %s, %s\n"), + entName, entity_vals->fetch (i), + jthr_names->fetch (i) != NULL ? jthr_names->fetch (i) : NTXT ("N/A"), + jthr_g_names->fetch (i) != NULL ? jthr_g_names->fetch (i) : NTXT ("N/A"), + jthr_p_names->fetch (i) != NULL ? jthr_names->fetch (i) : NTXT ("N/A")); + destroy (elist); + } + } +} + +void +er_print::dump_stats () +{ + Emsg *m = dbev->get_path_tree ()->fetch_stats (); + while (m != NULL) + { + fprintf (out_file, NTXT ("%s\n"), m->get_msg ()); + m = m->next; + } + dbev->get_path_tree ()->delete_stats (); +} + +void +er_print::dump_proc_warnings () +{ + PathTree *p = dbev->get_path_tree (); + if (p == NULL) + return; + Emsg *m = p->fetch_warnings (); + while (m != NULL) + { + fprintf (out_file, NTXT ("%s\n"), m->get_msg ()); + m = m->next; + } + dbev->get_path_tree ()->delete_warnings (); +} + +void +er_print::print_cmd (er_print_common_display *cd) +{ + cd->set_out_file (out_file); + cd->data_dump (); +} + +FILE * +er_print::set_outfile (char *cmd, FILE *&set_file, bool append) +{ + FILE *new_file; + char *home; + if (!strcasecmp (cmd, NTXT ("-"))) + { + new_file = stdout; + out_fname = NTXT ("<stdout>"); + } + else if (!strcasecmp (cmd, NTXT ("--"))) + { + new_file = stderr; + out_fname = NTXT ("<stderr>"); + } + else + { + char *fname; + char *path = NULL; + // Handle ~ in file names + home = getenv (NTXT ("HOME")); + if ((fname = strstr (cmd, NTXT ("~/"))) != NULL && home != NULL) + path = dbe_sprintf (NTXT ("%s/%s"), home, fname + 2); + else if ((fname = strstr (cmd, NTXT ("~"))) != NULL && home != NULL) + path = dbe_sprintf (NTXT ("/home/%s"), fname + 1); + else + path = strdup (cmd); + new_file = fopen (path, append ? NTXT ("a") : NTXT ("w")); + if (new_file == NULL) + { + fprintf (stderr, GTXT ("Error: Unable to open file: %s\n"), cmd); + free (path); + return NULL; + } + out_fname = path; + } + if (set_file && set_file != stdout) + fclose (set_file); + set_file = new_file; + return set_file; +} diff --git a/gprofng/src/gp-print.h b/gprofng/src/gp-print.h new file mode 100644 index 0000000..80c922f --- /dev/null +++ b/gprofng/src/gp-print.h @@ -0,0 +1,118 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _GP_PRINT_H +#define _ER_PRINT_H + +#include "Command.h" +#include "DbeApplication.h" +#include "Histable.h" +#include "Print.h" + +void ipc_mainLoop (int argc, char *argv[]); + +class DbeView; +template <class ITEM> class Vector; + +// er_print object +class er_print : public DbeApplication +{ +public: + + er_print (int argc, char *argv[]); + virtual ~er_print (); + void start (int argc, char *argv[]); + bool free_memory_before_exit (); + +private: + + char *error_msg; + DbeView *dbev; + char *out_fname; + FILE *inp_file; + FILE *dis_file; + FILE *out_file; + int dbevindex; + char *cov_string; + int limit; + Vector<Histable*> *cstack; + bool was_QQUIT; + + // override methods in base class + int check_args (int argc, char *argv[]); + void usage (); + + int is_valid_seg_name (char *seg_name, int prev); + int cmp_seg_name (char *full_name, char *seg_name); + int process_object_select (char *cov); + int set_libexpand (char *cov, enum LibExpand expand); + int set_libdefaults (); + + bool end_command (char *cmd); + void run (int argc, char *argv[]); + void proc_script (); + void proc_cmd (CmdType cmd_type, int cparam, char *arg1, char *arg2, + char *arg3 = NULL, char *arg4 = NULL, bool xdefault = true); + void disp_list (int no_header, int size, int align[], + char *header[], char **lists[]); + void exp_list (); + void describe (); + void obj_list (); + void seg_list (); + void print_objects (); + void print_overview (); + void print_overview_nodes (Vector<void*> *data, int level, + Vector<char *> *metric_cmds, Vector<char *> *non_metric_cmds); + void print_overview_tree (Vector<void*> *data, int level, Vector<void*> *values, + Vector<char *> *metric_cmds, Vector<char *> *non_metric_cmds); + void print_segments (); + void filter_list (CmdType cmd_type); + int check_exp_id (int exp_id, char *sel); + int get_exp_id (char *sel, int &bgn_index, int &end_index); + void print_func (Histable::Type type, Print_mode mode, + MetricList *mlist1, MetricList *mlist2, + char *func_name = NULL, char *sel = NULL); + void print_gprof (CmdType cmd_type, char *func_name, char *sel); + void print_ctree (CmdType cmd_type); + void print_dobj (Print_mode type, MetricList *mlist1, + char *dobj_name = NULL, char *sel = NULL); + void memobj (char *, int); + void mo_list (bool showtab, FILE *outf); + void mo_define (char *, char *, char *, char *, char *); + void indxobj (char *, int); + void indxo_list (bool showtab, FILE *outf); + void indxo_define (char *, char *, char *, char *); + void ifreq (); + void dump_nodes (); + void dump_stacks (); + void dump_unk_pcs (); + void dump_funcs (char *); + void dump_dataobjects (char *); + void dump_map (); + void dump_entities (); + void dump_stats (); + void dump_proc_warnings (); + void send_signal (); + void print_cmd (er_print_common_display *); + FILE *set_outfile (char *cmd, FILE *&set_file, bool append); + void gen_mapfile (char *seg_name, char *cmd); +}; + +#endif /* _ER_PRINT_H */ diff --git a/gprofng/src/gprofng.cc b/gprofng/src/gprofng.cc new file mode 100644 index 0000000..1bf5679 --- /dev/null +++ b/gprofng/src/gprofng.cc @@ -0,0 +1,301 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> + +#include "Application.h" +#include "i18n.h" +#include "util.h" + +static int verbose = 0; + +class Gprofng : Application +{ +public: + Gprofng (int _argc, char *_argv[]); + ~Gprofng (); + void start(); + void usage(); + +private: + void exec_cmd(char *tool_name, int argc, char **argv); + int argc; + char **argv; +}; + +int +main (int argc, char *argv[]) +{ + Gprofng *gprofng = new Gprofng (argc, argv); + gprofng->start(); + delete gprofng; + return 0; +} + +Gprofng::Gprofng (int _argc, char *_argv[]) : Application(_argc, _argv, NULL) +{ + argc = _argc; + argv = _argv; +} + +Gprofng::~Gprofng () { } + +void +Gprofng::usage () +{ + /* + * Isolate the first line because it has an argument. + * Otherwise it would be at the end of this long list. + */ + printf ( GTXT ( + "Usage: %s [OPTION(S)] COMMAND [KEYWORD] [ARGUMENTS]\n"), whoami); + + printf ( GTXT ( + "\n" + "This is the driver for the GPROFNG tools suite to gather and analyze performance data.\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --check verify if the hardware and software environment is supported.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + "Commands:\n" + "\n" + "The driver supports various commands. These are listed below.\n" + "\n" + "It is also possible to invoke the lower level commands directly, but since these \n" + "are subject to change, in particular the options, we recommend to use the driver.\n" + "\n" + "The man pages for the commands below can be viewed using the command name with\n" + "\"gprofng\" replaced by \"gp\" and the spaces replaced by a dash (\"-\"). For\n" + "example the man page name for \"gprofng collect app\" is \"gp-collect-app\".\n" + "\n" + "The following combination of commands and keywords are supported:\n" + "\n" + "Collect performance data\n" + "\n" + " gprofng collect app collect application performance data.\n" + "\n" + "Display the performance results\n" + "\n" + " gprofng display text display the performance data in ASCII format.\n" + " gprofng display html generate an HTML file from one or more experiments.\n" +/* + " gprofng display gui invoke the GUI to graphically analyze the results.\n" +*/ + " gprofng display src display source or disassembly with compiler annotations.\n" + "\n" + "Miscellaneous commands\n" + "\n" + " gprofng archive include binaries and source code in an experiment directory.\n" + "\n" + "Environment:\n" + "\n" + "The following environment variables are supported:\n" + "\n" + " GPROFNG_MAX_CALL_STACK_DEPTH set the depth of the call stack (default is 256).\n" + "\n" + " GPROFNG_USE_JAVA_OPTIONS may be set when profiling a C/C++ application\n" + " that uses dlopen() to execute Java code.\n" + "\n" + " GPROFNG_SSH_REMOTE_DISPLAY use this variable to define the ssh command\n" + " executed by the remote display tool.\n" + "\n" + " GPROFNG_SKIP_VALIDATION set this variable to disable checking hardware,\n" + " system, and Java versions.\n" + "\n" + " GPROFNG_ALLOW_CORE_DUMP set this variable to allow a core file to be\n" + " generated; otherwise an error report is created on /tmp.\n" + "\n" + " GPROFNG_ARCHIVE use this variable to define the settings for automatic\n" + " archiving upon experiment recording completion.\n" + "\n" + " GPROFNG_ARCHIVE_COMMON_DIR set this variable to the location of the common archive.\n" + "\n" + " GPROFNG_JAVA_MAX_CALL_STACK_DEPTH set the depth of the Java call stack; the default\n" + " is 256; set to 0 to disable capturing of call stacks.\n" + "\n" + " GPROFNG_JAVA_NATIVE_MAX_CALL_STACK_DEPTH set the depth of the Java native call stack;\n" + " the default is 256; set to 0 to disable capturing\n" + " of call stacks (JNI and assembly call stacks\n" + " are not captured).\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gp-archive(1), gp-collect-app(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n")); + +/* + printf ( GTXT ( + "Usage: %s [--verbose] [--version] [--help] <tool-name> [<keyword>] <args>\n" + "\n%s\n" + " archive Archive binaries and sources\n" + " collect [app] Collect performance data\n" + " display [text] Print an ASCII report\n" + " display gui Graphical tool for analyzing an experiment\n" + " display html Generate an HTML file from an experiment\n" + " display src Print source or dissasembly\n"), + whoami, getenv ("_BUILDING_MANPAGE") + ? "*Available subcommands*" + : "Available Subcommands"); +*/ +} + +void +Gprofng::exec_cmd (char *tool_name, int argc, char **argv) +{ + static const struct + { + const char *tool_name; + const char *keyword; + const char *app_name; + } app_names [] = { + { "archive", NULL, "gp-archive"}, + { "collect", "app", "gp-collect-app"}, + { "collect", "kernel", "gp-collect-kernel"}, + { "display", "text", "gp-display-text"}, + { "display", "gui", "gp-display-gui"}, + { "display", "html", "gp-display-html"}, + { "display", "src", "gp-display-src"}, + { NULL, NULL} + }; + + const char *keyword = argc > 1 ? argv[1] : ""; + int first = -1; + int find_tool_name = -1; + for (int i = 0; app_names[i].tool_name; i++) + if (!strcmp (tool_name, app_names[i].tool_name)) + { + if (app_names[i].keyword == NULL) + { + first = i; + break; + } + if (!strcmp (keyword, app_names[i].keyword)) + { + first = i; + argc--; + argv++; + break; + } + if (find_tool_name == -1) + find_tool_name = i; + } + + if (first == -1) + { + if (find_tool_name == -1) + fprintf (stderr, GTXT ("%s: error: keyword '%s' is not supported\n"), + get_basename (get_name ()), tool_name); + else if (*keyword == 0) + fprintf (stderr, GTXT ("%s %s: error: no qualifier\n"), + get_basename (get_name ()), tool_name); + else + fprintf (stderr, GTXT ("%s %s: error: qualifier '%s' is not supported\n"), + get_basename (get_name ()), tool_name, keyword); + exit (1); + } + + const char *aname = app_names[first].app_name;; + + char **arr = (char **) malloc ((argc + 3) * sizeof (char *)); + int n = 0; + char *pname = get_name (); + arr[n++] = dbe_sprintf ("%.*s%s", (int) (get_basename (pname) - pname), + pname, aname); + if (app_names[first].keyword) + arr[n++] = dbe_sprintf ("--whoami=%s %s %s", whoami, tool_name, + app_names[first].keyword); + else + arr[n++] = dbe_sprintf ("--whoami=%s %s", whoami, tool_name); + for (int i = 1; i < argc; i++) + arr[n++] = argv[i]; + arr[n] = NULL; + if (verbose) + { + printf ("gprofng::exec\n"); + for (int i = 0; arr[i]; i++) + printf ("%5d: %s\n", i, arr[i]); + printf("\n"); + } + execv (arr[0], arr); + + // If execv returns, it must have failed. + fprintf (stderr, GTXT ("%s failed: %s\n"), arr[0], STR (strerror (errno))); + exit(1); +} + +void +Gprofng::start () +{ + if (argc == 1) + { + usage (); + exit (0); + } + for (int i = 1; i < argc; i++) + { + char *s = argv[i]; + if (*s != '-') + { + exec_cmd(s, argc - i, argv + i); + return; + } + else if (!strcmp (s, "--help")) + { + usage (); + exit (0); + } + else if (!strcmp (s, "--version") || !strcmp (s, "-v")) + { + Application::print_version_info (); + exit (0); + } + else if (!strcmp (s, "--verbose")) + verbose = 1; + else if (!strcmp (s, "--check")) + { + fprintf (stderr, GTXT ("%s: error: --check is not implemented yet\n"), + get_basename (get_name ())); + exit (1); + } + else + { + fprintf (stderr, GTXT ("%s: error: unknown option %s\n"), + get_basename (get_name ()), s); + exit(1); + } + } + fprintf (stderr, GTXT ("%s: error: expected argument after options\n"), + get_basename (get_name ())); +} diff --git a/gprofng/src/gprofng.h2m b/gprofng/src/gprofng.h2m new file mode 100644 index 0000000..8786768 --- /dev/null +++ b/gprofng/src/gprofng.h2m @@ -0,0 +1,4 @@ +[SEE ALSO] + +.B +\fBgprofng-archive\fR(1), \fBgprofng-collect\fR(1), \fBgprofng-display-text\fR(1), \fBgprofng-display-src\fR(1), \fBgprofng-display-html\fR(1), \fBgprofng-display-gui\fR(1) diff --git a/gprofng/src/gprofng.rc b/gprofng/src/gprofng.rc new file mode 100644 index 0000000..3870220 --- /dev/null +++ b/gprofng/src/gprofng.rc @@ -0,0 +1,132 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. +# +# Specify which classes of compiler commentary will be shown +# with annotated source. +scc all + +# Specify which classes of compiler commentary will be shown +# with annotated disassembly +dcc all:src + +# Set the default function-list metrics +# for heap data, show inclusive leaks and bytes leaked; not allocations +dmetrics i.heapleakbytes:e!heapleakbytes +dmetrics i.heapleakcnt:e!heapleakcnt +dmetrics i.heapallocbytes:e!heapallocbytes +dmetrics i.heapalloccnt:e!heapalloccnt: + +# Clock profiling data +# Note: use same display order of LMS_* in: er.rc, TimelineVariable.java, +# Ovw_data.h, BaseMetricTreeNode.cc and Experiment.cc metric registration +dmetrics i!total:e!.total +# Show total cpu time +dmetrics ei.totalcpu +dmetrics i!.user:e!.user +dmetrics i!system:e!.system +dmetrics i!trap:e!.trap +dmetrics i!lock:e!.lock +dmetrics i!datapfault:e!.datapfault +dmetrics i!textpfault:e!.textpfault +dmetrics i!kernelpfault:e!.kernelpfault +dmetrics i!stop:e!.stop +dmetrics i!wait:e!.wait +dmetrics i!sleep:e!.sleep + +# for kernel clock profiling data, show inclusive and exclusive KCPU +dmetrics ei.kcpu +###dmetrics ie.kcpu + +# for count data, show exclusive metrics only +dmetrics i!bit:e.bit + +# for er_generic data, show exclusive metrics only +dmetrics i!icount:e.icount + +# Hide implementation hack. Functionmark column only serves +# to force zero-count functions to be displayed. +dmetrics e!bit_FM + +# for kernel profiles, show inclusive and exclusive kucycles and kcycles +# (kucycles and kcycles are for 12.3 and older experiments, Obsolete TBR) +dmetrics ei.kucycles:ei.kcycles +###dmetrics ie.kucycles:ie.kcycles + +# for derived HWC metrics, show exclusive only +dmetrics i!IPC:e!.IPC +dmetrics i!CPI:e!.CPI +dmetrics i!K_IPC:e!.K_IPC +dmetrics i!K_CPI:e!.K_CPI + +# for HWC, show exclusive only +dmetrics i!hwc:e.hwc + +# for synctrace, show inclusive only +dmetrics i.sync:e!sync +dmetrics i.syncn:e!syncn + +# Set the default function-list metrics for OMP profiling +dmetrics i.ompwork:e!ompwork +dmetrics i.ompwait:e!ompwait +dmetrics i!.masterthread:e!.masterthread + +#set the default function-list metrics for deadlock detection +dmetrics i!deadlocks:e.deadlocks + +# io data +dmetrics i.ioreadtime:e!ioreadtime +dmetrics i.iowritetime:e!iowritetime +dmetrics i.ioothertime:e!ioothertime +dmetrics i.ioerrortime:e!ioerrortime +dmetrics i!.ioreadcnt:e!ioreadcnt +dmetrics i!.ioreadbytes:e!ioreadbytes +dmetrics i!.iowritecnt:e!iowritecnt +dmetrics i!.iowritebytes:e!iowritebytes +dmetrics i!.ioothercnt:e!ioothercnt +dmetrics i!.ioerrorcnt:e!ioerrorcnt + +# for any other unnamed metrics, don't show them +dmetrics ie!.any + +# don't show size or address; show name +dmetrics !size:!address:name + +# Select the default function-list sorting metric +dsort ei.any:name +###dsort ie.any:name + +# Set function name style +name long + +# Set View mode to user +viewmode user + +# Set compare mode +compare off + +# Set enabling descendants to on +en_desc on + +# Set path where the gprofng libraries are installed +preload_libdirs ../lib:../lib32:../lib64 + +# Add search path for annotated source and disasm +addpath $expts:. + +# Add controls for specific load objects +# object_hide <Unknown> + +# version "@(#)er.rc 1.62 11/10/31" diff --git a/gprofng/src/i18n.cc b/gprofng/src/i18n.cc new file mode 100644 index 0000000..32c9960 --- /dev/null +++ b/gprofng/src/i18n.cc @@ -0,0 +1,30 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include "i18n.h" + +extern "C" +void +init_locale (char *Path) //set up for internationalization +{ + bindtextdomain (PACKAGE_NAME, LOCALEDIR); + textdomain (PACKAGE_NAME); +} diff --git a/gprofng/src/i18n.h b/gprofng/src/i18n.h new file mode 100644 index 0000000..d02ec0e --- /dev/null +++ b/gprofng/src/i18n.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _I18N_H +#define _I18N_H + +#include <libintl.h> + +#define GTXT(x) gettext(x) /* x - string literal to be i18n-ed */ +#define PTXT(x) gettext(x) /* x - expression to be i18n-ed */ +#define STXT(x) ((char *) (x)) /* x - static string literal to be i18n-ed */ +#define NTXT(x) ((char *) (x)) /* x - string literal not to be i18n-ed */ + +#ifdef __cplusplus +extern "C" +{ +#endif + void init_locale (char *Path); +#ifdef __cplusplus +} +#endif + +#endif /* _I18N_H */ diff --git a/gprofng/src/info.h b/gprofng/src/info.h new file mode 100644 index 0000000..5b05833 --- /dev/null +++ b/gprofng/src/info.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _INFO_H +#define _INFO_H + +/* Header file for .info section format */ +#include <inttypes.h> + +/* The format of the .info section from a single object file is: + * Fixed-length info_header + * Variable length string padded to a multiple of 4 bytes, giving + * the name of the source file from which this contribution comes. + * Zero or more entries. + * + * In an executable, there will be multiple occurrences of the above. + * The size of the info section will be a multiple of 4 bytes. + */ + +struct info_header +{ + char endian; /* 0 for big, 1 for little */ + char magic[3]; /* The string "SUN" */ + uint32_t cnt; /* number of entries for this section */ + uint16_t len; /* The length of the header, including the string */ + uint16_t version; /* The version number of this block */ + uint16_t phase; /* The compiler phase that produced this info */ + uint16_t spare; +}; + +#define PHASE_UNKNOWN 0 +#define PHASE_F77 1 +#define PHASE_CC 2 +#define PHASE_CPLUS 3 +#define PHASE_F95 4 +#define PHASE_IROPT 5 +#define PHASE_MAX 255 +#define F95_COPYINOUT ((PHASE_F95 << 24) | 1) + +/* An entry consists of a fixed-size struct entry, possibly followed by + * a variable length data structure whose format is determined by the + * type of an entry. The size of an entry is a multiple of 4 bytes. + */ + +struct entry_header +{ + uint32_t type; /* The type of this entry. High 8 bits is the phase. + * Low 24 bits is the type. */ + uint16_t len; /* length of this entry */ + uint16_t col; /* Column number in source line */ + uint32_t msgnum; /* Message number. High 8 bits is the phase. + * Low 24 bits is the type. */ + uint32_t line; /* Line number in source file */ +}; + +#endif diff --git a/gprofng/src/ipc.cc b/gprofng/src/ipc.cc new file mode 100644 index 0000000..932423c --- /dev/null +++ b/gprofng/src/ipc.cc @@ -0,0 +1,2829 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <signal.h> +#include <stdarg.h> +#include <fcntl.h> // creat +#include <unistd.h> // sleep +#include <pthread.h> // pthread_exit +#include <sys/wait.h> // wait +#include <locale.h> + +#include "DbeApplication.h" +#include "Histable.h" +#include "ipcio.h" +#include "Dbe.h" +#include "DbeSession.h" +#include "DbeThread.h" +#include "DbeView.h" + +int ipc_flags = 0; +IPCTraceLevel ipc_trace_level = TRACE_LVL_0; +int ipc_single_threaded_mode = 0; +const char *IPC_PROTOCOL_UNKNOWN = "IPC_PROTOCOL_UNKNOWN"; +const char *IPC_PROTOCOL_CURR = IPC_PROTOCOL_STR; +char const *ipc_protocol = NULL; + +DbeThreadPool *ipcThreadPool; + +extern int currentRequestID; +extern int currentChannelID; +extern BufferPool *responseBufferPool; +extern bool cancelNeeded (int); +extern void reexec (); + +/* Simple implementation of support for cancel of open experiment. Since we have only one cancellable + operation supported at this moment, we are using just a global variable. + As we support more and more cancellable ops we need a more sophisticated data struture such + as a mt-safe array to keep track of all cancellable requests/channels and update the supporting + routines - setCancellableChannel, cancelNeeded (in ipcio.cc) setCancelRequestedCh */ +int cancellableChannelID = 0xFFFFFFFF; +int cancelRequestedChannelID; + +static const char *table_name (int); + +#define VSIZE(v) ((long long) ((v) ? (v)->size() : 0)) + +inline const char* +bool2str (bool v) +{ + return v ? "true" : "false"; +} + +inline char* +str2str (String v) +{ + return (char*) (v ? v : "NULL"); +} + +inline char* +str2s (String v) +{ + return (char*) (v ? v : ""); +} + +inline DbeView * +getView (int index) +{ + return dbeSession->getView (index); +} + +extern "C" +{ + typedef void (*SignalHandler)(int); +} + +/* + * Fatal error handlers + */ +extern "C" void fatalErrorHadler (int sig, siginfo_t *info, void *context); +extern "C" void sigSEGV_handler (int sig, siginfo_t *info, void *context); +extern "C" void sigABRT_handler (int sig, siginfo_t *info, void *context); +static char fatalErrorBuffer1[1024 * 8]; +static char fatalErrorBuffer2[1024 * 8]; +static int fatalErrorCode = 1; +static int fatalErrorCounter = 0; +static void *fatalErrorContext = 0; +static siginfo_t *fatalErrorInfo = 0; +static char *fatalErrorDynamicMemory = NULL; + +extern "C" void +fatalErrorHadler (int sig, siginfo_t *info, void *context) +{ + if (fatalErrorCounter > 0) + { // Need synchronization here + //sleep(10); // Wait 10 seconds to make sure previous processing is done + return; // exit(fatalErrorCode); // Already in processing + } + fatalErrorCounter = 1; + fatalErrorCode = sig; + fatalErrorContext = context; + fatalErrorInfo = info; + // Free reserved memory + if (fatalErrorDynamicMemory != NULL) + { + free (fatalErrorDynamicMemory); + fatalErrorDynamicMemory = NULL; + } + // Get process ID + pid_t pid = getpid (); + // Create dump file + snprintf (fatalErrorBuffer1, sizeof (fatalErrorBuffer1), "/tmp/analyzer.%lld", + (long long) pid); + mkdir (fatalErrorBuffer1, 0700); + snprintf (fatalErrorBuffer1, sizeof (fatalErrorBuffer1), + "/tmp/analyzer.%lld/crash.sig%d.%lld", (long long) pid, sig, + (long long) pid); + // Dump stack trace in background using pstack + snprintf (fatalErrorBuffer2, sizeof (fatalErrorBuffer2), + "/usr/bin/pstack %lld > %s.pstack", (long long) pid, fatalErrorBuffer1); + system (fatalErrorBuffer2); + int fd = creat (fatalErrorBuffer1, 0600); + if (fd >= 0) + { + // Write error message + snprintf (fatalErrorBuffer2, sizeof (fatalErrorBuffer2), + "A fatal error has been detected by er_print: Signal %lld\n", + (long long) sig); + write (fd, fatalErrorBuffer2, strlen (fatalErrorBuffer2)); +// snprintf (fatalErrorBuffer2, sizeof (fatalErrorBuffer2), +// "If you would like to submit a bug report, please use your support contract.\n")); +// write(fd, fatalErrorBuffer2, strlen(fatalErrorBuffer2)); + snprintf (fatalErrorBuffer2, sizeof (fatalErrorBuffer2), + "Protocol Version: %d\n", IPC_VERSION_NUMBER); + write (fd, fatalErrorBuffer2, strlen (fatalErrorBuffer2)); + close (fd); + // Send postmortem error message to the GUI + // snprintf(fatalErrorBuffer1, sizeof (fatalErrorBuffer1), + // "%s: %s: /tmp/analyzer.%lld", + // "Unexpected signal in er_print", + // "Crash dump", + // (long long) pid); + // res = write(2, fatalErrorBuffer2, strlen(fatalErrorBuffer1)); + } + wait (0); // wait for pstack + //sleep(10); // Wait 10 seconds to make sure processing of fatal error is done + // Exit with correct status + exit (fatalErrorCode); +} + +// SIGABRT Handler +extern "C" void +sigABRT_handler (int sig, siginfo_t *info, void *context) +{ + fatalErrorHadler (sig, info, context); + pthread_exit (&fatalErrorCode); +} + +// SIGSEGV Handler +extern "C" void +sigSEGV_handler (int sig, siginfo_t *info, void *context) +{ + //if (fatalErrorCounter > 0) sleep(1); // Wait 1 second + fatalErrorHadler (sig, info, context); + pthread_exit (&fatalErrorCode); +} + +// SIGTERM Handler +extern "C" void sigterm_handler (int sig, siginfo_t *info, void *context); +struct sigaction old_sigterm_handler; + +volatile int term_flag; +int error_flag; + +extern "C" void +sigterm_handler (int, siginfo_t *, void *) +{ + if (fatalErrorCounter > 0) + { + //sleep(10); // Wait 10 seconds to make sure processing of fatal error is done + //return; // Fatal error processing will exit it + pthread_exit (&fatalErrorCode); + } + term_flag = 1; +} + +#define ipc_log ipc_default_log +#define ipc_request_trace ipc_request_log +#define ipc_response_trace ipc_response_log +static const char *ipc_log_name = NULL; +static const char *ipc_request_log_name = NULL; +static const char *ipc_response_log_name = NULL; +FILE *requestLogFileP = stderr; +FILE *responseLogFileP = stderr; +hrtime_t begin_time; +long long delta_time = 0; +int ipc_delay_microsec = 0; + +void +ipc_default_log (const char *fmt, ...) +{ + if (!ipc_log_name || !ipc_flags) + return; + if (ipc_trace_level >= TRACE_LVL_3) + { + hrtime_t cur_time = gethrtime (); + unsigned long long time_stamp = (cur_time - begin_time) / 1000000 + delta_time; + fprintf (stderr, "%7llu: ", time_stamp); + } + va_list vp; + va_start (vp, fmt); + vfprintf (stderr, fmt, vp); + va_end (vp); + fflush (stderr); +} + +extern "C" void sigint_handler (int sig, siginfo_t *info, void *context); +struct sigaction old_sigint_handler; + +extern "C" void +sigint_handler (int, siginfo_t *, void *) +{ + ipc_log ("SIGINT signal happens\n"); +} + +void +ipc_request_log (IPCTraceLevel trace_level, const char *fmt, ...) +{ + if (!ipc_request_log_name || !ipc_flags || trace_level > ipc_trace_level) + return; + fprintf (responseLogFileP, "thr: %llu ", (unsigned long long) pthread_self ()); + if (ipc_trace_level >= TRACE_LVL_3) + { + hrtime_t cur_time = gethrtime (); + unsigned long long time_stamp = (cur_time - begin_time) / 1000000 + delta_time; + fprintf (requestLogFileP, "%7llu: ", time_stamp); + } + va_list vp; + va_start (vp, fmt); + vfprintf (requestLogFileP, fmt, vp); + va_end (vp); + fflush (requestLogFileP); +} + +void +ipc_response_log (IPCTraceLevel trace_level, const char *fmt, ...) +{ + if (!ipc_response_log_name || !ipc_flags || trace_level > ipc_trace_level) + return; + fprintf (responseLogFileP, "thr: %llu ", (unsigned long long) pthread_self ()); + if (ipc_trace_level >= TRACE_LVL_3) + { + hrtime_t cur_time = gethrtime (); + unsigned long long time_stamp = (cur_time - begin_time) / 1000000 + delta_time; + fprintf (responseLogFileP, "%7llu: ", time_stamp); + } + va_list vp; + va_start (vp, fmt); + vfprintf (responseLogFileP, fmt, vp); + va_end (vp); + fflush (responseLogFileP); +} + +#ifdef IPC_LOG +void +ipc_dump (char *s, Vector<bool> *v) +{ + if (v == NULL) + { + ipc_log (" Vector<bool> %s is NULL\n", str2s (s)); + return; + } + ipc_log (" Vector<bool> %s size=%lld\n", str2s (s), VSIZE (v)); + for (int i = 0; i < v->size (); i++) + ipc_log (" [%d]: %s\n", i, bool2str (v->fetch (i))); +} + +void +ipc_dump (char *s, Vector<String> *v) +{ + if (v == NULL) + { + ipc_log (" Vector<bool> %s is NULL\n", str2s (s)); + return; + } + ipc_log (" Vector<String> %s size=%lld\n", str2s (s), VSIZE (v)); + for (int i = 0; i < v->size (); i++) + { + String str = v->fetch (i); + ipc_log (" [%d]: '%s'\n", i, str2str (str)); + } +} + +void +ipc_dump (char *s, Vector<Obj> *v) +{ + if (v == NULL) + { + ipc_log (" Vector<Obj> %s is NULL\n", str2s (s)); + return; + } + ipc_log (" Vector<void *> %s size=%lld\n", str2s (s), VSIZE (v)); + for (int i = 0; i < v->size (); i++) + ipc_log (" [%d]: 0x%08llx\n", i, (long long) (v->fetch (i))); +} + +#else +#define ipc_dump(s, v) +#endif + +static MetricList * +readMetricListV2 (int dbevindex, IPCrequest* req) +{ + MetricType mtype = (MetricType) readInt (req); + Vector<int> *type = (Vector<int>*)readArray (req); + Vector<int> *subtype = (Vector<int>*)readArray (req); + Vector<bool> *sort = (Vector<bool>*)readArray (req); + Vector<int> *vis = (Vector<int>*)readArray (req); + Vector<char*> *cmd = (Vector<char*>*)readArray (req); + Vector<char*> *expr_spec = (Vector<char*>*)readArray (req); + Vector<char*> *legends = (Vector<char*>*)readArray (req); + MetricList *mlist = dbeGetMetricListV2 (dbevindex, mtype, type, subtype, sort, + vis, cmd, expr_spec, legends); + return mlist; +} + +static void +setCancellableChannel (int chID) +{ + cancellableChannelID = chID; +} + +/* Add more entries here for other cancellable operations */ +static void +checkCancellableOp (char *inp, IPCrequest* req) +{ + if (!strcmp (inp, "setFilterStr")) + setCancellableChannel (currentChannelID); + else if (!strcmp (inp, "openExperimentList")) + setCancellableChannel (currentChannelID); + else if (!strcmp (inp, "getFiles") || !strcmp (inp, "getFileAttributes")) + { + setCancellableChannel (currentChannelID); + req->setCancelImmediate (); + } +} + +/* This is what used to be the core of ipc_mainLoop before asynch ipc. + Read the details of the request from the request buffer: name, args etc + do the work by calling the appropriate dbe routine(s) and write the + response to a response buffer and queue it up in the response queue */ + +int +ipc_doWork (void *arg) +{ + IPCrequest *req = (IPCrequest *) arg; + currentRequestID = req->getRequestID (); + currentChannelID = req->getChannelID (); + req->setStatus (IN_PROGRESS); + String inp = readString (req); + if (inp == NULL) + { + ipc_log ("NULL ipc command received, exiting\n"); + return 0; + } + ipc_log ("ipc: %s Req %x Ch %x\n", inp, currentRequestID, currentChannelID); + checkCancellableOp (inp, req); + if (!strcmp (inp, "initApplication")) + { + bool nbm = readBoolean (req); + String arg1 = readString (req); + String arg2 = readString (req); + Vector<String> *arg3 = (Vector<String>*)readArray (req); + ipc_log (" nbm: %s, arg1: '%s', arg2: '%s'\n", bool2str (nbm), str2str (arg1), str2str (arg2)); + ipc_dump ("arg3", arg3); + // set the session to be interactive + dbeSession->set_interactive (true); + if (nbm) + theApplication->set_name ("analyzer-NBM"); + else + theApplication->set_name ("analyzer"); + + // XXX Why does it reset the install directory???? Or a licensing directory??? + // Vector<String> *res = theDbeApplication->initApplication (arg1, arg2, &setProgress); + Vector<String> *res = theDbeApplication->initApplication (NULL, NULL, &setProgress); + writeArray (res, req); + free (arg1); + free (arg2); + destroy (arg3); + destroy (res); + } + else if (!strcmp (inp, "syncTime")) + { + long long anl_time = readLong (req); + hrtime_t cur_time = gethrtime (); + long long time_stamp = (cur_time - begin_time) / 1000000; + delta_time = anl_time - time_stamp; + ipc_log (" syncTime %llu %llu \n", anl_time, delta_time); + writeString (NULL, req); + } + else if (!strcmp (inp, "getInitMessages")) + { + Vector<String> *res = dbeGetInitMessages (); + ipc_log (" returned = %lld msgs\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "reExec")) + { + ipc_log (" started reexec()\n"); + reexec (); + } + else if (!strcmp (inp, "dbeCreateDirectories")) + { + String arg1 = readString (req); // path + ipc_log (" arg = %s\n", arg1); + String res = dbeCreateDirectories (arg1); + writeString (res, req); + free (arg1); + free (res); + } + else if (!strcmp (inp, "dbeDeleteFile")) + { + String arg1 = readString (req); // path + ipc_log (" arg = %s\n", arg1); + String res = dbeDeleteFile (arg1); + writeString (res, req); + free (arg1); + free (res); + } + else if (!strcmp (inp, "dbeReadFile")) + { + String arg1 = readString (req); + ipc_log (" arg = %s\n", arg1); // path + Vector<String> *res = dbeReadFile (arg1); + writeArray (res, req); + free (arg1); + destroy (res); + } + else if (!strcmp (inp, "dbeWriteFile")) + { + String arg1 = readString (req); // path + String arg2 = readString (req); // contents + ipc_log (" arg1 = %s arg2 = %s\n", arg1, arg2); + int res = dbeWriteFile (arg1, arg2); + writeInt (res, req); + free (arg1); + } + else if (!strcmp (inp, "getExpPreview")) + { + // XXX add another argument == DbeView index + String arg1 = readString (req); + ipc_log (" arg = %s\n", arg1); + Vector<String> *res = dbeGetExpPreview (0, arg1); + writeArray (res, req); + free (arg1); + destroy (res); + } + else if (!strcmp (inp, "getFileAttributes")) + { + String arg1 = readString (req); // filename + String arg2 = readString (req); // format + ipc_log (" arg1 = %s arg2 = %s\n", arg1, arg2); + String res = dbeGetFileAttributes (arg1, arg2); + writeString (res, req); + free (arg1); + free (arg2); + free (res); + } + else if (!strcmp (inp, "getFiles")) + { + String arg1 = readString (req); // dirname + String arg2 = readString (req); // format + ipc_log (" arg1 = %s arg2 = %s\n", arg1, arg2); + String res = dbeGetFiles (arg1, arg2); + writeString (res, req); + free (arg1); + free (arg2); + free (res); + } + else if (!strcmp (inp, "getOSFamily")) + writeString ("Linux", req); + else if (!strcmp (inp, "getRunningProcesses")) + { + String arg1 = readString (req); // format + ipc_log (" arg = %s\n", arg1); + String res = dbeGetRunningProcesses (arg1); + writeString (res, req); + free (arg1); + free (res); + } + else if (!strcmp (inp, "getCurrentDirectory")) + { + char buf [2048]; + String res = getcwd (buf, (size_t) sizeof (buf)); // Get current directory + writeString (res, req); + } + else if (!strcmp (inp, "getHomeDirectory")) + { + String res = getenv ("HOME"); // Get HOME directory + writeString (res, req); + } + else if (!strcmp (inp, "setCurrentDirectory")) + { + String arg1 = readString (req); // dirname + ipc_log (" arg = %s\n", arg1); + int res = chdir (arg1); // Change directory + writeInt (res, req); + free (arg1); + } + else if (!strcmp (inp, "getLocale")) + { + String res = setlocale (LC_ALL, ""); // Get locale + writeString (res, req); + } + else if (!strcmp (inp, "setLocale")) + { + String arg1 = readString (req); // locale + ipc_log (" arg = %s\n", arg1); + String res = setlocale (LC_ALL, arg1); // Set locale + writeString (res, req); + free (arg1); + } + else if (!strcmp (inp, "readRCFile")) + { + int arg1 = readInt (req); + String arg2 = readString (req); // file name + ipc_log (" arg1=%d, arg2=%s\n", arg1, arg2); + String res = dbeReadRCFile (arg1, arg2); // Read RC File + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "dbeGetExpParams")) + { + // XXX add another argument == DbeView index + String arg1 = readString (req); + ipc_log (" arg = %s\n", arg1); + String res = dbeGetExpParams (0, arg1); + writeString (res, req); + free (arg1); + free (res); + } + else if (!strcmp (inp, "getExperimentsGroups")) + { + Vector<Vector<char*>*> *groups = dbeGetExperimensGroups (); + writeArray (groups, req); + destroy (groups); + } + else if (!strcmp (inp, "setExperimentsGroups")) + { + Vector<Vector<char*>*> *groups = (Vector<Vector<char*>*> *)readArray (req); + ipc_log (" groups.size = %lld\n", VSIZE (groups)); + char *msg_str = dbeSetExperimentsGroups (groups); + writeString (msg_str, req); + free (msg_str); + destroy (groups); + } + else if (!strcmp (inp, "dropExperiment")) + { + int arg1 = readInt (req); + Vector<int> *arg2 = (Vector<int>*)readArray (req); + ipc_log (" arg = %d, exps = %lld\n", arg1, VSIZE (arg2)); + char *res = dbeDropExperiment (arg1, arg2); + writeString (res, req); + free (res); + delete arg2; + } + else if (!strcmp (inp, "getUserExpId")) + { + Vector<int> *arg = (Vector<int>*)readArray (req); + ipc_log (" expIds = %lld\n", VSIZE (arg)); + Vector<int> *res = dbeGetUserExpId (arg); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getFounderExpId")) + { + Vector<int> *arg = (Vector<int>*)readArray (req); + ipc_log (" expIds = %lld\n", VSIZE (arg)); + Vector<int> *res = dbeGetFounderExpId (arg); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getExpGroupId")) + { + Vector<int> *arg = (Vector<int>*)readArray (req); + ipc_log (" expIds = %lld\n", VSIZE (arg)); + Vector<int> *res = dbeGetExpGroupId (arg); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getExpsProperty")) + { + String arg = readString (req); + Vector<String> *res = dbeGetExpsProperty (arg); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExpName")) + { + // XXX add argument == DbeView index + Vector<String> *res = dbeGetExpName (0); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExpState")) + { + // XXX add argument == DbeView index + Vector<int> *res = dbeGetExpState (0); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getExpEnable")) + { + int arg1 = readInt (req); + ipc_log (" arg1 = %d\n", arg1); + Vector<bool> *res = dbeGetExpEnable (arg1); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setExpEnable")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool>*)readArray (req); + ipc_log (" arg1=%d\n", arg1); + ipc_dump ("arg2", arg2); + bool b = dbeSetExpEnable (arg1, arg2); + writeBoolean (b, req); + ipc_log (" dbeSetExpEnable returns %s\n", bool2str (b)); + delete arg2; + } + else if (!strcmp (inp, "getExpInfo")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<String> *res = dbeGetExpInfo (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getViewModeEnable")) + { + bool res = dbeGetViewModeEnable (); + writeBoolean (res, req); + } + else if (!strcmp (inp, "getJavaEnable")) + { + bool res = dbeGetJavaEnable (); + writeBoolean (res, req); + } + else if (!strcmp (inp, "updateNotes")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + String arg4 = readString (req); + bool arg5 = readBoolean (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + int i = dbeUpdateNotes (arg1, arg2, arg3, arg4, arg5); + writeInt (i, req); + } + else if (!strcmp (inp, "getLoadObjectList")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<void *> *res = dbeGetLoadObjectList (arg1); + if (res == NULL) + ipc_log (" returning = NULL for LoadObjectList\n"); + else + { + Vector<char*> *s = (Vector<char*> *) res->fetch (0); + ipc_log (" returning = %lld vectors for %lld LoadObjects\n", + VSIZE (res), VSIZE (s)); + } + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getLoadObjectName")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<String> *res = dbeGetLoadObjectName (arg1); + ipc_log (" returning = %lld strings\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getTabListInfo")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<void*> *res = dbeGetTabListInfo (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getSearchPath")) + { + // XXX add argument == DbeView index + ipc_log (" no args\n"); + Vector<String> *res = dbeGetSearchPath (0); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setSearchPath")) + { + // XXX add another argument == DbeView index + Vector<String> *res = (Vector<String>*)readArray (req); + ipc_log (" %lld strings\n", VSIZE (res)); + dbeSetSearchPath (0, res); + writeString (NULL, req); + destroy (res); + } + else if (!strcmp (inp, "getPathmaps")) + { + Vector<void*> *res = dbeGetPathmaps (0); + ipc_log (" returns = %lld objects, number of pathmaps = %lld\n", + VSIZE (res), VSIZE ((Vector<int>*)res->fetch (0))); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setPathmaps")) + { + Vector<String> *from = (Vector<String>*)readArray (req); + Vector<String> *to = (Vector<String>*)readArray (req); + char *res = dbeSetPathmaps (from, to); + writeString (res, req); + free (res); + if (from) + { + from->destroy (); + delete from; + } + if (to) + { + to->destroy (); + delete to; + } + } + else if (!strcmp (inp, "addPathmap")) + { + // XXX add another argument == DbeView index + String arg1 = readString (req); + String arg2 = readString (req); + ipc_log (" args = '%s', '%s'\n", arg1 ? arg1 : "NULL", arg2 ? arg2 : "NULL"); + char *res = dbeAddPathmap (0, arg1, arg2); + ipc_log (" returns = '%s'\n", (res != NULL ? res : "NULL")); + writeString (res, req); + free (arg1); + free (arg2); + } + else if (!strcmp (inp, "getMsg")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + String res = dbeGetMsg (arg1, arg2); + ipc_log (" returns = '%s'\n", (res != NULL ? res : "<NULL>")); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "initView")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" new view = %d; clone of view %d\n", arg1, arg2); + dbeInitView (arg1, arg2); + writeString (NULL, req); + } + else if (!strcmp (inp, "disposeWindow")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + dbeDeleteView (arg1); + writeString (NULL, req); + } +#if 0 + else if (!strcmp (inp, "createMapfile")) + { + int arg1 = readInt (); + String arg2 = readString (); + int arg3 = readInt (); + ipc_log (" args = %d, %s, %d\n", arg1, arg2, arg3); + String res = dbeCreateMapfile (arg1, arg2, arg3); + writeString (res); + free (arg2); + free (res); + } +#endif + else if (!strcmp (inp, "setCompareModeV2")) + { + int dbevindex = readInt (req); + int cmp_mode = readInt (req); + getView (dbevindex)->set_compare_mode (cmp_mode); + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, currentRequestID, currentChannelID); + } + else if (!strcmp (inp, "getCompareModeV2")) + { + int dbevindex = readInt (req); + int res = CMP_DISABLE; + if (dbeSession->expGroups && dbeSession->expGroups->size () > 1) + res = getView (dbevindex)->get_compare_mode (); + ipc_log (" %s: %d returns %d\n", inp, dbevindex, res); + writeInt (res, req); + } + else if (!strcmp (inp, "getRefMetricsV2")) + { + Vector<void*> *res = dbeGetRefMetricsV2 (); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setCurMetricsV2")) + { + int dbevindex = readInt (req); + int cmp_mode = readInt (req); + MetricList *mlist = readMetricListV2 (dbevindex, req); + getView (dbevindex)->reset_metric_list (mlist, cmp_mode); + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, currentRequestID, currentChannelID); + } + else if (!strcmp (inp, "getCurMetricsV2")) + { + int arg1 = readInt (req); + MetricType arg2 = (MetricType) readInt (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + Vector<void*> *res = dbeGetCurMetricsV2 (arg1, arg2); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getRefMetricTree")) + { + int dbevindex = readInt (req); + bool include_unregistered = readBoolean (req); + ipc_log (" args = %d, %d\n", dbevindex, include_unregistered); + Vector<void*> *res = dbeGetRefMetricTree (dbevindex, include_unregistered); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getRefMetricTreeValues")) + { + int dbevindex = readInt (req); + Vector<String> *metcmds = (Vector<String>*)readArray (req); + Vector<String> *nonmetcmds = (Vector<String>*)readArray (req); + ipc_log (" args = %d, metcmds->size()=%lld, nonmetcmds->size()=%lld\n", + dbevindex, VSIZE (metcmds), VSIZE (nonmetcmds)); + ipc_dump ("metcmds", metcmds); + ipc_dump ("nonmetcmds", nonmetcmds); + Vector<void*> *res = dbeGetRefMetricTreeValues (dbevindex, metcmds, nonmetcmds); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld objects, length = %lld\n", + VSIZE (res), VSIZE (((Vector<int>*)res->fetch (0)))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getOverviewText")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<char*> *res = dbeGetOverviewText (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setSort")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + MetricType arg3 = (MetricType) readInt (req); + bool arg4 = readBoolean (req); + ipc_log (" args = %d, %d, %d, %c\n", arg1, arg2, arg3, (arg4 ? 'T' : 'F')); + dbeSetSort (arg1, arg2, arg3, arg4); + writeString (NULL, req); + } + + else if (!strcmp (inp, "getAnoValue")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<int> *res = dbeGetAnoValue (arg1); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setAnoValue")) + { + int arg1 = readInt (req); + ipc_log (" args = %d, array\n", arg1); + Vector<int> *arg2 = (Vector<int>*)readArray (req); + dbeSetAnoValue (arg1, arg2); + writeString (NULL, req); + delete arg2; + } + else if (!strcmp (inp, "getNameFormat")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int b = dbeGetNameFormat (arg1); + writeInt (b, req); + } + else if (!strcmp (inp, "getSoName")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + bool b = dbeGetSoName (arg1); + writeBoolean (b, req); + } + else if (!strcmp (inp, "setNameFormat")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + bool arg3 = readBoolean (req); + ipc_log (" args = %d, %d, %d\n", arg1, arg2, arg3); + dbeSetNameFormat (arg1, arg2, arg3); + writeString (NULL, req); + } + else if (!strcmp (inp, "getViewMode")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int i = dbeGetViewMode (arg1); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "setViewMode")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + dbeSetViewMode (arg1, arg2); + writeString (NULL, req); + } + else if (!strcmp (inp, "getTLValue")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetTLValue (arg1); + ipc_log (" returns = %lld void*'s\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setTLValue")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + String tldata_cmd = readString (req); + int entity_prop_id = readInt (req); + int align = readInt (req); + int depth = readInt (req); + dbeSetTLValue (arg1, tldata_cmd, entity_prop_id, align, depth); + writeString (NULL, req); + free (tldata_cmd); + } + else if (!strcmp (inp, "getExpFounderDescendants")) + { + Vector<void*> *res = dbeGetExpFounderDescendants (); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExpSelection")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetExpSelection (arg1); + writeArray (res, req); + destroy (res); + } + + else if (!strcmp (inp, "setFilterStr")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + ipc_log (" args = %d, %s\n", arg1, arg2); + String res = dbeSetFilterStr (arg1, arg2); + ipc_log (" returns = '%s'\n", res ? res : "NULL"); + writeString (res, req); + free (arg2); + free (res); + } + + else if (!strcmp (inp, "getFilterStr")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + String res = dbeGetFilterStr (arg1); + ipc_log (" returns = '%s'\n", res ? res : "NULL"); + writeString (res, req); + free (res); + } + + else if (!strcmp (inp, "validateFilterExpression")) + { + String arg1 = readString (req); + int res = dbeValidateFilterExpression (arg1); + ipc_log (" validateFilterExpression('%s') returned %d\n", str2str (arg1), res); + free (arg1); + writeInt (res, req); + } + + else if (!strcmp (inp, "getFilterKeywords")) + { + int dbevindex = readInt (req); + Vector<void*>*res = dbeGetFilterKeywords (dbevindex); + writeArray (res, req); + destroy (res); + } + + else if (!strcmp (inp, "getFilters")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args: view = %d, experiment = %d\n", arg1, arg2); + Vector<void*>*res = dbeGetFilters (arg1, arg2); + ipc_log (" -- returned %lld Filters\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + + else if (!strcmp (inp, "updateFilters")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool>*)readArray (req); + Vector<String> *arg3 = (Vector<String>*)readArray (req); + ipc_log ("arg1=%d arg2->size()=%lld arg3->size()=%lld\n", + arg1, VSIZE (arg2), VSIZE (arg3)); + ipc_dump ("arg2", arg2); + ipc_dump ("arg3", arg3); + bool b = dbeUpdateFilters (arg1, arg2, arg3); + writeBoolean (b, req); + ipc_log (" returns %s\n", (b == true ? "true" : "false")); + delete arg2; + delete arg3; + } + else if (!strcmp (inp, "getLoadObjectState")) + { + int arg1 = readInt (req); + ipc_log (" args = %d \n", arg1); + Vector<int> *res = dbeGetLoadObjectState (arg1); + ipc_log (" returning = %lld int's\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setLoadObjectState")) + { + int arg1 = readInt (req); + Vector<int> *arg2 = (Vector<int>*)readArray (req); + ipc_log (" args = %d, %lld objects\n", arg1, VSIZE (arg2)); + dbeSetLoadObjectState (arg1, arg2); + writeString (NULL, req); + delete arg2; + } + else if (!strcmp (inp, "setLoadObjectDefaults")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + dbeSetLoadObjectDefaults (arg1); + writeString (NULL, req); + } + else if (!strcmp (inp, "getMemTabSelectionState")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<bool> *res = dbeGetMemTabSelectionState (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setMemTabSelectionState")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool> *)readArray (req); + ipc_log (" args = %d\n arg2 = %lld objects\n", arg1, VSIZE (arg2)); + dbeSetMemTabSelectionState (arg1, arg2); + writeString (NULL, req); + destroy (arg2); + } + else if (!strcmp (inp, "getIndxTabSelectionState")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<bool> *res = dbeGetIndxTabSelectionState (arg1); + ipc_log (" -- returned %lld-vector [bool]\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setIndxTabSelectionState")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool> *)readArray (req); + ipc_log (" args = %d\n arg2 = %lld objects\n", arg1, VSIZE (arg2)); + dbeSetIndxTabSelectionState (arg1, arg2); + writeString (NULL, req); + destroy (arg2); + } + else if (!strcmp (inp, "getTabSelectionState")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<bool> *res = dbeGetTabSelectionState (arg1); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setTabSelectionState")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool>*)readArray (req); + ipc_log (" args = %d\n arg2 = %lld objects\n", arg1, VSIZE (arg2)); + dbeSetTabSelectionState (arg1, arg2); + writeString (NULL, req); + delete arg2; + } + else if (!strcmp (inp, "getMemObjects")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetMemObjects (arg1); + +#ifdef IPC_LOG + if (res == NULL) + ipc_log (" -- returned NULL\n"); + else + { + Vector<int> *mo_types = (Vector<int> *)res->fetch (0); + ipc_log (" -- returned %lld-vector [ %lld-vectors]\n", + VSIZE (res), VSIZE (mo_types)); + } +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "loadMachineModel")) + { + String arg1 = readString (req); +#ifdef IPC_LOG + ipc_log (" arg = `%s'\n", arg1); +#endif + String sts = dbeLoadMachineModel (arg1); +#ifdef IPC_LOG + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); +#endif + writeString (sts, req); + free (arg1); + } + else if (!strcmp (inp, "getMachineModel")) + { + String sts = dbeGetMachineModel (); +#ifdef IPC_LOG + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); +#endif + writeString (sts, req); + } + else if (!strcmp (inp, "getCPUVerMachineModel")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<char*> *res = dbeGetCPUVerMachineModel (arg1); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "listMachineModels")) + { + Vector<String> *res = dbeListMachineModels (); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld strings\n", VSIZE (res)); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "defineMemObj")) + { + String arg1 = readString (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); +#ifdef IPC_LOG + ipc_log (" args = %s, %s, %s, %s\n", arg1, arg2, arg3 == NULL ? "NULL" : arg3, arg4 == NULL ? "NULL" : arg4); +#endif + String sts = dbeDefineMemObj (arg1, arg2, NULL, arg3, arg4); +#ifdef IPC_LOG + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); +#endif + writeString (sts, req); + free (arg1); + free (arg2); + free (arg3); + free (arg4); + } + else if (!strcmp (inp, "deleteMemObj")) + { + String arg1 = readString (req); +#ifdef IPC_LOG + ipc_log (" args = %s\n", arg1); +#endif + String sts = dbeDeleteMemObj (arg1); +#ifdef IPC_LOG + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); +#endif + writeString (sts, req); + free (arg1); + } + else if (!strcmp (inp, "getIndxObjDescriptions")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetIndxObjDescriptions (arg1); +#ifdef IPC_LOG + if (res == NULL) + ipc_log (" -- returned NULL\n"); + else + { + Vector<int> *indxo_types = (Vector<int> *)res->fetch (0); + ipc_log (" -- returned %lld-vector [ %lld-vectors]\n", + VSIZE (res), VSIZE (indxo_types)); + } +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCustomIndxObjects")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetCustomIndxObjects (arg1); +#ifdef IPC_LOG + if (res == NULL) + ipc_log (" -- returned NULL\n"); + else + { + Vector<char *> *indxo_names = (Vector<char *> *)res->fetch (0); + ipc_log (" -- returned %lld-vector [ %lld-vectors]\n", + VSIZE (res), VSIZE (indxo_names)); + } +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "defineIndxObj")) + { + String arg1 = readString (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); + ipc_log (" args = %s, %s, %s, %s\n", arg1, arg2, arg3 == NULL ? "NULL" : arg3, arg4 == NULL ? "NULL" : arg4); + String sts = dbeDefineIndxObj (arg1, arg2, arg3, arg4); + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); + writeString (sts, req); + free (arg1); + free (arg2); + free (arg3); + free (arg4); + } + else if (!strcmp (inp, "setSelObj")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %ld, %s, %d\n", arg1, (long) arg2, table_name (arg3), arg4); + dbeSetSelObj (arg1, arg2, arg3, arg4); + writeString (NULL, req); + } + else if (!strcmp (inp, "setSelObjV2")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + ipc_log (" args = %d, %ld\n", arg1, arg2); + dbeSetSelObjV2 (arg1, arg2); + writeString (NULL, req); + } + else if (!strcmp (inp, "getSelObj")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, table_name (arg2), arg3); + Obj i = dbeGetSelObj (arg1, arg2, arg3); + ipc_log (" returns = %ld (0x%08lx)\n", (long) i, (long) i); + writeObject (i, req); + } + else if (!strcmp (inp, "getSelObjV2")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + ipc_log (" arg1 = %d agr2 = %s\n", arg1, arg2 ? arg2 : "NULL"); + Obj res = dbeGetSelObjV2 (arg1, arg2); + ipc_log (" returns = %lld\n", (long long) res); + writeObject (res, req); + free (arg2); + } + else if (!strcmp (inp, "getSelObjIO")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + int arg3 = readInt (req); + ipc_log (" arg1 = %d, arg2 = %lld, arg3 = %d\n", arg1, (long long) arg2, arg3); + Vector<uint64_t> *res = dbeGetSelObjIO (arg1, arg2, arg3); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getSelObjsIO")) + { + int arg1 = readInt (req); + Vector<uint64_t> *arg2 = (Vector<uint64_t>*)readArray (req); + int arg3 = readInt (req); + ipc_log (" arg1 = %d, arg2 size = %lld, arg3 = %d\n", + arg1, VSIZE (arg2), arg3); + Vector<uint64_t> *res = dbeGetSelObjsIO (arg1, arg2, arg3); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getSelObjHeapTimestamp")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + ipc_log (" arg1 = %d, arg2 = %llu\n", arg1, (unsigned long long) arg2); + uint64_t st = dbeGetSelObjHeapTimestamp (arg1, arg2); + ipc_log (" returns = %llu\n", (unsigned long long) st); + writeLong (st, req); + } + else if (!strcmp (inp, "getSelObjHeapUserExpId")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + ipc_log (" arg1 = %d, arg2 = %llu\n", arg1, (unsigned long long) arg2); + int userExpId = dbeGetSelObjHeapUserExpId (arg1, arg2); + ipc_log (" returns = %d\n", userExpId); + writeInt (userExpId, req); + } + else if (!strcmp (inp, "getSelIndex")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, 0x%08lx, %s, %d\n", arg1, (long) arg2, table_name (arg3), arg4); + int i = dbeGetSelIndex (arg1, arg2, arg3, arg4); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "printData")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + String arg4 = readString (req); + String arg5 = readString (req); + ipc_log (" args = %d, %s, %d, `%s', `%s'\n", + arg1, table_name (arg2), arg3, + (arg4 == NULL ? "NULL" : arg4), + (arg5 == NULL ? "NULL" : arg5)); + String res = dbePrintData (arg1, arg2, arg3, arg4, arg5, NULL); + writeString (res, req); + free (arg4); + free (arg5); + free (res); + } + else if (!strcmp (inp, "getPrintLimit")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int i = dbeGetPrintLimit (arg1); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "setPrintLimit")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + String res = dbeSetPrintLimit (arg1, arg2); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "getPrintMode")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int i = dbeGetPrintMode (arg1); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "setPrintMode")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + ipc_log (" args = %d, %s\n", arg1, arg2); + String res = dbeSetPrintMode (arg1, arg2); + writeString (res, req); + free (arg2); + free (res); + } + else if (!strcmp (inp, "getPrintDelim")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + char i = dbeGetPrintDelim (arg1); + ipc_log (" returns = %c\n", i); + writeInt ((int) i, req); + } + else if (!strcmp (inp, "getHotMarks")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %s (%d) \n", arg1, table_name (arg2), arg2); + Vector<void*> *res = dbeGetHotMarks (arg1, arg2); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getHotMarksInc")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %s (%d) \n", arg1, table_name (arg2), arg2); + Vector<void*> *res = dbeGetHotMarksInc (arg1, arg2); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getSummaryHotMarks")) + { + int arg1 = readInt (req); + Vector<Obj> *arg2 = (Vector<Obj>*)readArray (req); + int arg3 = readInt (req); + ipc_log (" args = %d, 0x%llx, %s (%d)\n", arg1, (long long) arg2, table_name (arg3), arg3); + Vector<void*> *res = dbeGetSummaryHotMarks (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncId")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %s, %d, %d\n", arg1, table_name (arg2), arg3, arg4); + Vector<uint64_t> *res = dbeGetFuncId (arg1, arg2, arg3, arg4); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getFuncCalleeInfo")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + Vector<int> *arg3 = (Vector<int>*)readArray (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %s, %lld, %d\n", arg1, table_name (arg2), VSIZE (arg3), arg4); + Vector<void*> *res = dbeGetFuncCalleeInfo (arg1, arg2, arg3, arg4); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncCallerInfo")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + Vector<int> *arg3 = (Vector<int>*)readArray (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %s, %lld, %d\n", arg1, table_name (arg2), VSIZE (arg3), arg4); + Vector<void*> *res = dbeGetFuncCallerInfo (arg1, arg2, arg3, arg4); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setFuncData")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %ld, %s, %d\n", arg1, (long) arg2, table_name (arg3), arg4); + int i = dbeSetFuncData (arg1, arg2, arg3, arg4); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "setFuncDataV2")) + { + int dbevindex = readInt (req); + Obj sel_obj = readObject (req); + int type = readInt (req); + int subtype = readInt (req); + Vector<long long> *longs = new Vector<long long>(2); + Vector<char *> *strings = new Vector<char *>(2); + + longs->append (dbeSetFuncData (dbevindex, sel_obj, type, subtype)); + strings->append (dbeGetMsg (dbevindex, ERROR_MSG)); + String sf_name = NULL; + long long sf_id = 0; + switch (type) + { + case DSP_SOURCE: + case DSP_DISASM: + { + Histable *obj = (Histable *) sel_obj; + if (obj) + { + Histable *sf = obj->convertto (Histable::SOURCEFILE); + if (sf) + { + sf_id = sf->id; + sf_name = dbe_strdup (sf->get_name ()); + } + } + break; + } + } + longs->append (sf_id); + strings->append (sf_name); + ipc_log (" setFuncData(%d, %ld, %s, %d) returns (%lld, %lld)\n (%s, %s)\n", + dbevindex, (long) sel_obj, table_name (type), subtype, longs->get (0), longs->get (1), + STR (strings->get (0)), STR (strings->get (1))); + + Vector<void *> *res = new Vector<void *>(2); + res->append (longs); + res->append (strings); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncList")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, table_name (arg2), arg3); + Vector<void*> *res = dbeGetFuncList (arg1, arg2, arg3); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld objects, length = %lld\n", + VSIZE (res), VSIZE ((Vector<int>*)res->fetch (0))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncListV2")) + { + int dbevindex = readInt (req); + int mtype = readInt (req); + Obj sel_obj = readObject (req); + int type = readInt (req); + int subtype = readInt (req); + Vector<void*> *res = dbeGetFuncListV2 (dbevindex, mtype, sel_obj, type, subtype); + ipc_log (" args = %d 0x%x %ld, %s, %d returns = %d objects, length = %d\n", + dbevindex, mtype, (long) sel_obj, table_name (type), subtype, + (int) (res ? res->size () : 0), + (int) (res ? ((Vector<int>*)res->fetch (0))->size () : 0)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncListMini")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, table_name (arg2), arg3); + Vector<void*> *res = dbeGetFuncListMini (arg1, arg2, arg3); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld objects, length = %lld\n", + VSIZE (res), VSIZE ((Vector<int>*)res->fetch (0))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "dbeGetTotals")) + { + int dbevindex = readInt (req); + int dsptype = readInt (req); + int subtype = readInt (req); + Vector<void *> *res = dbeGetTotals (dbevindex, dsptype, subtype); + ipc_log (" dbeGetTotals(%d, %d, %d) returns %lld objects\n", + dbevindex, dsptype, subtype, VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getComparableObjsV2")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + int arg3 = readInt (req); + Vector<Obj> *res = dbeGetComparableObjsV2 (arg1, arg2, arg3); + ipc_log (" args = %d 0x%lx %d\n", arg1, (long) arg2, arg3); + ipc_dump ("getComparableObjsV2:res", res); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "dbeConvertSelObj")) + { + Obj obj = readObject (req); + int type = readInt (req); + Obj res = dbeConvertSelObj (obj, type); + ipc_log (" args = %lld %d res=%lld \n", (long long) obj, type, + (long long) res); + writeObject (res, req); + } + else if (!strcmp (inp, "getTableDataV2")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); + String arg5 = readString (req); + Vector<uint64_t> *arg6 = (Vector<uint64_t>*)readArray (req); + ipc_log (" args = %d, %s, %s, %s, %s, %lld\n", arg1, STR (arg2), + STR (arg3), STR (arg4), STR (arg5), VSIZE (arg6)); + Vector<void*> *res = dbeGetTableDataV2 (arg1, arg2, arg3, arg4, arg5, arg6); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld objects, length = %lld\n", + VSIZE (res), VSIZE ((Vector<int>*)res->fetch (0))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + //destroy( arg6 ); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeNumLevels")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int res = dbeGetCallTreeNumLevels (arg1); +#ifdef IPC_LOG + ipc_log (" returns = %d\n", res); +#endif + writeInt (res, req); + } + else if (!strcmp (inp, "getCallTreeLevel")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, arg2, arg3); + Vector<void*> *res = dbeGetCallTreeLevel (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeChildren")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + Vector<int> *arg3 = (Vector<int> *) readArray (req); /*NodeIdx array*/ + ipc_log (" args = %d, %s, vec_size=%lld\n", arg1, arg2, (long long) (arg3 ? arg3->size () : 0)); + Vector<void*> *res = dbeGetCallTreeChildren (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeLevels")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + ipc_log (" args = %d, %s\n", arg1, arg2); + Vector<void*> *res = dbeGetCallTreeLevels (arg1, arg2); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeLevelFuncs")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %d, %d\n", arg1, arg2, arg3); + Vector<void*> *res = dbeGetCallTreeLevelFuncs (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeFuncs")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetCallTreeFuncs (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getGroupIds")) + { + int arg1 = readInt (req); + Vector<int> *res = dbeGetGroupIds (arg1); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getNames")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + Obj arg3 = readObject (req); +#ifdef IPC_LOG + ipc_log (" args = %d, %s 0x%lx\n", arg1, table_name (arg2), (long) arg3); +#endif + Vector<String> *res = dbeGetNames (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getTotalMax")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, table_name (arg2), arg3); + Vector<void*> *res = dbeGetTotalMax (arg1, arg2, arg3); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld vectors, length %lld\n", + VSIZE (res), VSIZE ((Vector<void*>*)res->fetch (0))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "composeFilterClause")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + Vector<int> *arg4 = (Vector<int>*)readArray (req); + ipc_log (" args = %d, %s, %d, %lld selections\n", + arg1, table_name (arg2), arg3, VSIZE (arg4)); + String s = dbeComposeFilterClause (arg1, arg2, arg3, arg4); + ipc_log (" returns %s\n", (s == NULL ? "<NULL>" : s)); + writeString (s, req); + } + else if (!strcmp (inp, "getStatisOverviewList")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<Object> *res = dbeGetStatisOverviewList (arg1); + ipc_log (" dbeStatisGetOverviewList returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getStatisList")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<Object> *res = dbeGetStatisList (arg1); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getSummary")) + { + int arg1 = readInt (req); + Vector<Obj> *arg2 = (Vector<Obj>*)readArray (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, 0x%llx, %s (%d), %d\n", arg1, (long long) arg2, table_name (arg3), arg3, arg4); + Vector<Object> *res = dbeGetSummary (arg1, arg2, arg3, arg4); + ipc_log (" dbeGetSummary returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getSummaryV2")) + { + int dbevindex = readInt (req); + Vector<Obj> *sel_objs = (Vector<Obj>*)readArray (req); + int type = readInt (req); + int subtype = readInt (req); + Vector<void*> *res = dbeGetSummaryV2 (dbevindex, sel_objs, type, subtype); + ipc_log (" args = %d, [%lld], %s (%d), %d res=[%lld] 0x%llx \n", + dbevindex, VSIZE (sel_objs), table_name (type), type, subtype, + VSIZE (res), (unsigned long long) res); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExpName1")) + { + // XXX add an argument = DbeView index + String arg1 = readString (req); + ipc_log (" arg = `%s'\n", arg1 ? arg1 : "NULL"); + String res = dbeGetExpName (0, arg1); + writeString (res, req); + ipc_log (" returns `%s'\n", res ? res : "NULL"); + free (arg1); + free (res); + } + else if (!strcmp (inp, "getHwcHelp")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + Vector<String> *res = dbeGetHwcHelp (0, forKernel); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getHwcSets")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + Vector<Vector<char*>*> *res = dbeGetHwcSets (0, forKernel); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getHwcsAll")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + Vector<void*> *res = dbeGetHwcsAll (0, forKernel); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getHwcAttrList")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + Vector<char*> *res = dbeGetHwcAttrList (0, forKernel); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getHwcMaxConcurrent")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + int res = dbeGetHwcMaxConcurrent (0, forKernel); + writeInt (res, req); + } + else if (!strcmp (inp, "getIfreqData")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<char*> *res = dbeGetIfreqData (arg1); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getNewLeakListInfo")) + { + int arg1 = readInt (req); + bool arg2 = readBoolean (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + Vector<void*> *res = dbeGetLeakListInfo (arg1, arg2); + ipc_log (" returns %lld void*'s\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getObject")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + Obj arg3 = readObject (req); + Obj i = dbeGetObject (arg1, arg2, arg3); + writeObject (i, req); + } + else if (!strcmp (inp, "getExpVerboseName")) + { + Vector<int> *arg = (Vector<int>*)readArray (req); + ipc_log (" expIds = %lld\n", VSIZE (arg)); + Vector<String> *res = dbeGetExpVerboseName (arg); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getName")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + String res = dbeGetName (0, arg1); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "getStartTime")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + long long l = dbeGetStartTime (0, arg1); + ipc_log (" returns = %llu\n", l); + writeLong (l, req); + } + else if (!strcmp (inp, "getRelativeStartTime")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + long long l = dbeGetRelativeStartTime (0, arg1); + ipc_log (" returns = %llu\n", l); + writeLong (l, req); + } + else if (!strcmp (inp, "getEndTime")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + long long l = dbeGetEndTime (0, arg1); + ipc_log (" returns = %llu\n", l); + writeLong (l, req); + } + else if (!strcmp (inp, "getClock")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + int i = dbeGetClock (0, arg1); + writeInt (i, req); + } + /* + else if ( !strcmp( inp, "getFounderExpId" ) ) { + // XXX add an argument = DbeView index + int arg1 = readInt(req); + int i = dbeGetFounderExpId(0, arg1 ); + writeInt( i, req ); + } + */ + else if (!strcmp (inp, "getEntityProps")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetEntityProps (arg1); + writeArray (res, req); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getEntities")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %d, %d\n", arg1, arg2, arg3); + Vector<void*> *res = dbeGetEntities (arg1, arg2, arg3); + writeArray (res, req); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getEntitiesV2")) + { + int arg1 = readInt (req); + Vector<int> *arg2 = (Vector<int>*)readArray (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %lld, %d\n", arg1, VSIZE (arg2), arg3); + Vector<void*> *res = dbeGetEntitiesV2 (arg1, arg2, arg3); + writeArray (res, req); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getTLDetails")) + {//TBR + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + long long arg5 = readLong (req); + ipc_log (" dbevindex= %d, exp_id = %d, data_id = %d, " + "entity_prop_id = %d, event_id = %lld\n", + arg1, arg2, arg3, arg4, arg5); + Vector<void*> *res = dbeGetTLDetails (arg1, arg2, arg3, arg4, arg5); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getStackNames")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + ipc_log (" args = %d, %ld\n", arg1, (long) arg2); + Vector<String> *res = dbeGetStackNames (arg1, arg2); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getStackFunctions")) + { + // XXX add an argument = DbeView index + Obj arg1 = readObject (req); + ipc_log (" args = %ld\n", (long) arg1); + Vector<Obj> *res = dbeGetStackFunctions (0, arg1); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getStacksFunctions")) + { + // XXX add an argument = DbeView index + Vector<Obj> *arg1 = (Vector<Obj>*)readArray (req); + ipc_log (" argc = %ld\n", (long) arg1->size ()); + Vector<void*> *res = dbeGetStacksFunctions (0, arg1); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getStackPCs")) + { + // XXX add an argument = DbeView index + Obj arg1 = readObject (req); + ipc_log (" args = %ld\n", (long) arg1); + Vector<Obj> *res = dbeGetStackPCs (0, arg1); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getIOStatistics")) + { + int dbevindex = readInt (req); + Vector<Vector<char*>*> *res = dbeGetIOStatistics (dbevindex); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getHeapStatistics")) + { + int dbevindex = readInt (req); + Vector<Vector<char*>*> *res = dbeGetHeapStatistics (dbevindex); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getSamples")) + { + int dbev_id = readInt (req); + int exp_id = readInt (req); + int64_t lo = readLong (req); + int64_t hi = readLong (req); + ipc_log (" dbevindex= %d, exp_id = %d, lo_idx:%lld, hi_idx:%lld\n", + dbev_id, exp_id, (long long) lo, (long long) hi); + Vector<void*> *res = dbeGetSamples (dbev_id, exp_id, lo, hi); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getGCEvents")) + { + int dbev_id = readInt (req); + int exp_id = readInt (req); + int64_t lo = readLong (req); + int64_t hi = readLong (req); + ipc_log (" dbevindex= %d, exp_id = %d, lo_idx:%lld, hi_idx:%lld\n", + dbev_id, exp_id, (long long) lo, (long long) hi); + Vector<void*> *res = dbeGetGCEvents (dbev_id, exp_id, lo, hi); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncNames")) + { + int arg1 = readInt (req); + Vector<Obj> *arg2 = (Vector<Obj>*)readArray (req); + ipc_log (" arg1 = %d, arg2 absent, size = %lld\n", arg1, VSIZE (arg2)); + Vector<String> *res = dbeGetFuncNames (arg1, arg2); + writeArray (res, req); + delete arg2; + destroy (res); + } + else if (!strcmp (inp, "getFuncIds")) + { + int arg1 = readInt (req); + Vector<Obj> *arg2 = (Vector<Obj>*)readArray (req); + ipc_log (" arg1 = %d, arg2 absent, size = %lld\n", arg1, VSIZE (arg2)); + Vector<uint64_t> *res = dbeGetFuncIds (arg1, arg2); + writeArray (res, req); + delete arg2; + destroy (res); + } + else if (!strcmp (inp, "getObjNamesV2")) + { + int arg1 = readInt (req); + Vector<uint64_t> *arg2 = (Vector<uint64_t>*)readArray (req); + ipc_log (" arg1 = %d, arg2 absent, size = %lld\n", arg1, VSIZE (arg2)); + Vector<String> *res = dbeGetObjNamesV2 (arg1, arg2); + writeArray (res, req); + delete arg2; + destroy (res); + } + else if (!strcmp (inp, "getFuncName")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + ipc_log (" arg1 = %d, arg2 = %lld\n", arg1, (long long) arg2); + String res = dbeGetFuncName (arg1, arg2); + ipc_log (" returning = %s\n", res ? res : "NULL"); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "getObjNameV2")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + ipc_log (" arg1 = %d, arg2 = %llu\n", arg1, (unsigned long long) arg2); + String res = dbeGetObjNameV2 (arg1, arg2); + ipc_log (" returning = %s\n", res ? res : "NULL"); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "getDataspaceTypeDesc")) + { + // XXX add an argument = DbeView index + Obj arg1 = readObject (req); + ipc_log (" arg1 absent, index = %ld\n", (long) arg1); + String res = dbeGetDataspaceTypeDesc (0, arg1); + ipc_log (" returning = %s\n", res ? res : "NULL"); + writeString (res, req); + free (res); + } + /* + * New Interface with Timeline + */ +#if 0 //YXXX TBR + else if (!strcmp (inp, "dbeInit")) + dbeInit (); + else if (!strcmp (inp, "getDefaultExperimentName")) + { + String res = dbeGetDefaultExperimentName (); + ipc_log (" returning = %s\n", res); + writeString (res); + free (res); + } + else if (!strcmp (inp, "getExperimentState")) + { + String res = dbeGetExperimentState (); + ipc_log (" returning = %s\n", res); + writeString (res); + free (res); + } + else if (!strcmp (inp, "getExpStartTime")) + { + long long l = dbeGetExpStartTime (); + ipc_log (" returns = %llu\n", l); + writeLong (l); + } + else if (!strcmp (inp, "getExpEndTime")) + { + long long l = dbeGetExpEndTime (); + ipc_log (" returns = %llu\n", l); + writeLong (l); + } +#endif + else if (!strcmp (inp, "getDataDescriptorsV2")) + {//TBR? TBD + int exp_id = readInt (req); + ipc_log (" exp_id = %d\n", exp_id); + Vector<void*> *res = dbeGetDataDescriptorsV2 (exp_id); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getDataPropertiesV2")) + {//TBR? TBD + int exp_id = readInt (req); + int arg2 = readInt (req); + ipc_log (" exp_id = %d, data_idx = %d\n", exp_id, arg2); + Vector<void*> *res = dbeGetDataPropertiesV2 (exp_id, arg2); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExperimentTimeInfo")) + { + Vector<int> *exp_ids = (Vector<int>*)readArray (req); + ipc_log (" cnt = %lld\n", VSIZE (exp_ids)); + Vector<void*> *res = dbeGetExperimentTimeInfo (exp_ids); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExperimentDataDescriptors")) + { + Vector<int> *exp_ids = (Vector<int>*)readArray (req); + ipc_log (" cnt = %lld\n", VSIZE (exp_ids)); + Vector<void*> *res = dbeGetExperimentDataDescriptors (exp_ids); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } +#if 0 //YXXX TBR? + else if (!strcmp (inp, "getExprValues")) + {//TBR? TBD + int arg1 = readInt (); + String arg2 = readString (); + ipc_log (" data_idx = %d expr = %s\n", arg1, arg2 ? arg2 : "NULL"); + Vector<long long> *res = dbeGetExprValues (arg1, arg2); + ipc_log (" returns = %d objects\n", res ? res->size () : 0); + writeArray (res); + delete res; + free (arg2); + } +#endif + else if (!strcmp (inp, "hasTLData")) + { + int dbevindex = readInt (req); + Vector<int> *exp_ids = (Vector<int>*)readArray (req); + Vector<int> *data_ids = (Vector<int>*)readArray (req); + Vector<int> *eprop_ids = (Vector<int>*)readArray (req); + Vector<int> *eprop_vals = (Vector<int>*)readArray (req); + Vector<int> *auxs = (Vector<int>*)readArray (req); + ipc_log (" dbev_id = %d, cnt = %lld\n", dbevindex, VSIZE (exp_ids)); + Vector<bool> *res = dbeHasTLData (dbevindex, + exp_ids, data_ids, eprop_ids, eprop_vals, auxs); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getTLData")) + { + int dbevindex = readInt (req); + int exp_id = readInt (req); + int tldata_type = readInt (req); + int entity_prop_id = readInt (req); + int entity_prop_val = readInt (req); + int aux = readInt (req); + long long arg5 = readLong (req); + long long arg6 = readLong (req); + int arg7 = readInt (req); + bool getReps = readBoolean (req); + Vector<String> *secondaryProps = (Vector<String>*)readArray (req); + + ipc_log (" args = %d:%d; tldata_type=%d entity_prop_id=%d ent=%d aux=%d" + "\n tstart=%lld delta=%lld ndeltas=%d getReps=%d nProps=%lld\n", + dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, + arg5, arg6, arg7, (int) getReps, VSIZE (secondaryProps)); + Vector<void*> *res = dbeGetTLData (dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, + arg5, arg6, arg7, getReps, secondaryProps); +#ifdef IPC_LOG + if (res) + { + Vector<Obj> *reps = (Vector<Obj>*)res->fetch (0); + Vector<Obj> *props = (Vector<Obj>*)res->fetch (1); + if (reps) + { + Vector <long long> *fids = (Vector <long long> *)reps->fetch (2); + int sz = fids ? fids->size () : 0; + ipc_log (" returning TL reps (dDscrs); nreps=%d:", sz); + int i; + for (i = 0; i < sz && i < 7; i++) + ipc_log (" %lld", fids->fetch (i)); + if (i < sz) + ipc_log (" ... %lld", fids->fetch (sz - 1)); + ipc_log ("\n"); + } + if (props) + { + int nprops = props->size (); + ipc_log (" returning values for %d properties:\n", nprops); + assert (secondaryProps->size () == nprops); + } + } + else + ipc_log (" returning NULL\n"); +#endif + writeArray (res, req); + destroy (res); + destroy (secondaryProps); + } + else if (!strcmp (inp, "getTLEventCenterTime")) + { + int dbevindex = readInt (req); + int exp_id = readInt (req); + int tldata_type = readInt (req); + int entity_prop_id = readInt (req); + int entity_prop_val = readInt (req); + int aux = readInt (req); + long long event_id = readLong (req); + long long move_count = readLong (req); + ipc_log (" args = %d:%d; tldata_type = %d entity_prop_id = %d " + "ent = %d aux = %d idx = %lld move=%lld\n", + dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, event_id, move_count); + Vector<long long> * res = dbeGetTLEventCenterTime (dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, event_id, move_count); + ipc_log (" returning idx = %lld, time = %lld\n", + res ? res->fetch (0) : -1, res ? res->fetch (1) : -1); + writeArray (res, req); + } + else if (!strcmp (inp, "getTLEventIdxNearTime")) + { + int dbevindex = readInt (req); + int exp_id = readInt (req); + int tldata_type = readInt (req); + int entity_prop_id = readInt (req); + int entity_prop_val = readInt (req); + int aux = readInt (req); + int searchDirection = readInt (req); + long long value = readLong (req); + ipc_log (" args = %d:%d; tldata_type = %d entity_prop_id = %d " + "ent = %d aux = %d direction = %d value = %lld(0x%llx)\n", + dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, + searchDirection, value, value); + long long res = dbeGetTLEventIdxNearTime (dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, + searchDirection, value); + ipc_log (" returning = %lld\n", res); + writeLong (res, req); + } + else if (!strcmp (inp, "getAggregatedValue")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); + long long arg5 = readLong (req); + long long arg6 = readLong (req); + int arg7 = readInt (req); + String arg8 = readString (req); + String arg9 = readString (req); + ipc_log (" data_idx = %d lfilter = \"%s\" fexpr = \"%s\" " + "time = \"%s\" tstart = %lld delta = %lld " + "num = %d key = \"%s\" aggr = \"%s\"\n", + arg1, arg2 ? arg2 : "NULL", arg3 ? arg3 : "NULL", + arg4 ? arg4 : "NULL", arg5, arg6, + arg7, arg8 ? arg8 : "NULL", arg9 ? arg9 : "NULL"); + Vector<long long> *res = dbeGetAggregatedValue (arg1, arg2, arg3, + arg4, arg5, arg6, arg7, arg8, arg9); +#ifdef IPC_LOG + if (res) + { + int sz = res->size (); + ipc_log (" returning = %d values:", sz); + if (sz > 10) + sz = 10; + for (int i = 0; i < sz; i++) + ipc_log (" %lld", res->fetch (i)); + ipc_log ("\n"); + } + else + ipc_log (" returning NULL\n"); +#endif + writeArray (res, req); + delete res; + free (arg2); + free (arg3); + free (arg4); + free (arg8); + free (arg9); + } +#if 0//YXXX TBR + else if (!strcmp (inp, "getExprValue")) + { + int exp_id = readInt (); + int arg1 = readInt (); + int arg2 = readInt (); + String arg3 = readString (); + ipc_log (" exp_id %d, data_id = %d, event_id = %d, expr = %s\n", + exp_id, arg1, arg2, arg3 ? arg3 : "NULL"); + String res = dbeGetExprValue (exp_id, arg1, arg2, arg3); + ipc_log (" returning = %s\n", res ? res : ""); + writeString (res); + free (res); + free (arg3); + } + else if (!strcmp (inp, "getListValues")) + { + Obj arg1 = readObject (); + ipc_log (" stack = %lu\n", (long) arg1); + Vector<Obj> *res = dbeGetListValues (arg1); + ipc_log (" returns = %d objects\n", res ? res->size () : 0); + writeArray (res); + destroy (res); + } + else if (!strcmp (inp, "getListNames")) + { + Obj arg1 = readObject (); + ipc_log (" stack = %lu\n", (long) arg1); + Vector<String> *res = dbeGetListNames (arg1); + ipc_log (" returns = %d objects\n", res ? res->size () : 0); + writeArray (res); + destroy (res); + } +#endif + else if (!strcmp (inp, "getLineInfo")) + { + Obj arg1 = readObject (req); + ipc_log (" pc = %lu\n", (long) arg1); + Vector<String> *res = dbeGetLineInfo (arg1); + ipc_log (" returning File name: '%s'\n", res ? res->fetch (0) : ""); + ipc_log (" returning Lineno: '%s'\n", res ? res->fetch (1) : ""); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setAlias")) + { + String arg1 = readString (req); + String arg2 = readString (req); + String arg3 = readString (req); + ipc_log (" name=\"%s\" uname=\"%s\" expr=\"%s\"\n", + arg1 ? arg1 : "", arg2 ? arg2 : "", arg3 ? arg3 : ""); + int res = dbeSetAlias (arg1, arg2, arg3); + ipc_log (" returning = %d\n", res); + writeInt (res, req); + } + else if (!strcmp (inp, "getAlias")) + { + String arg1 = readString (req); + ipc_log (" name=\"%s\"\n", arg1 ? arg1 : ""); + Vector<char*> *res = dbeGetAlias (arg1); + ipc_log (" returning uname: '%s'\n", res && res->fetch (0) ? res->fetch (0) : ""); + ipc_log (" returning expr: '%s'\n", res && res->fetch (1) ? res->fetch (0) : ""); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getXYPlotData")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); + String arg5 = readString (req); + String arg6 = readString (req); + String arg7 = readString (req); + String arg8 = readString (req); + String arg9 = readString (req); + ipc_log (" data_idx = %d lfilter = \"%s\" arg = \"%s\" " + "func1 = \"%s\" aggr1 = \"%s\" " + "func2 = \"%s\" aggr2 = \"%s\" " + "func3 = \"%s\" aggr3 = \"%s\" \n", + arg1, arg2 ? arg2 : "NULL", arg3 ? arg3 : "NULL", + arg4 ? arg4 : "NULL", arg5 ? arg5 : "NULL", arg6 ? arg6 : "NULL", + arg7 ? arg7 : "NULL", arg8 ? arg8 : "NULL", arg9 ? arg9 : "NULL"); + Vector<Vector<long long>*> *res = dbeGetXYPlotData (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + +#ifdef IPC_LOG + if (res) + { + long nvals = res->size (); + for (long i = 0; i < nvals; ++i) + { + Vector<long long> *vals = res->fetch (i); + long long sz = VSIZE (vals); + ipc_log (" returning = %lld values:", sz); + if (sz > 10) + sz = 10; + for (long j = 0; j < sz; j++) + ipc_log (" %lld", vals->fetch (j)); + ipc_log ("\n"); + } + } + else + ipc_log (" returning NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (strcmp (inp, "dbe_archive") == 0) + { + Vector<long long> *ids = (Vector<long long> *) readArray (req); + Vector<const char*> *locations = (Vector<const char*> *) readArray (req); + dbe_archive (ids, locations); + delete ids; + destroy (locations); + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, currentRequestID, currentChannelID); + } + else if (strcmp (inp, "dbeSetLocations") == 0) + { + Vector<const char*> *fnames = (Vector<const char*> *) readArray (req); + Vector<const char*> *locations = (Vector<const char*> *) readArray (req); + dbeSetLocations (fnames, locations); + destroy (fnames); + destroy (locations); + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, currentRequestID, currentChannelID); + } + else if (strcmp (inp, "dbeResolvedWith_setpath") == 0) + { + char *path = readString (req); + Vector<void *> *res = dbeResolvedWith_setpath (path); + free (path); + writeArray (res, req); + destroy (res); + } + else if (strcmp (inp, "dbeResolvedWith_pathmap") == 0) + { + char *old_prefix = readString (req); + char *new_prefix = readString (req); + Vector<void *> *res = dbeResolvedWith_pathmap (old_prefix, new_prefix); + free (old_prefix); + free (new_prefix); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCollectorControlValue")) + { + /* int dbevindex =*/ readInt (req); + char *control = readString (req); + ipc_log (" args = %s\n", control); + char *ret = dbeGetCollectorControlValue (control); + ipc_log (" returning %s\n", STR (ret)); + writeString (ret, req); + } + else if (!strcmp (inp, "setCollectorControlValue")) + { + /* int dbevindex =*/ readInt (req); + char *control = readString (req); + char *value = readString (req); +#ifdef IPC_LOG + ipc_log (" args = %s %s\n", control, value); +#endif + char *ret = dbeSetCollectorControlValue (control, value); +#ifdef IPC_LOG + if (ret) + ipc_log (" returning %s\n", ret); + else + ipc_log (" returning NULL\n"); +#endif + writeString (ret, req); + } + else if (!strcmp (inp, "unsetCollectorControlValue")) + { + /* int dbevindex =*/ readInt (req); + char *control = readString (req); + ipc_log (" args = %s\n", control); + char *ret = dbeUnsetCollectorControlValue (control); + ipc_log (" returning %s\n", STR (ret)); + writeString (ret, req); + } + else if (!strcmp (inp, "getSignalValue")) + { + String arg1 = readString (req); + ipc_log (" arg1=\"%s\"\n", arg1 ? arg1 : ""); + int res = dbeGetSignalValue (arg1); + ipc_log (" returning = %d\n", res); + writeInt (res, req); + } + else if (!strcmp (inp, "sendSignal")) + { + long long p = readLong (req); + int signum = readInt (req); + ipc_log (" args = %llu, %d\n", (long long) p, signum); + char * ret = dbeSendSignal ((pid_t) p, signum); +#ifdef IPC_LOG + if (ret) + ipc_log (" returning %s\n", ret); + else + ipc_log (" returning NULL\n"); +#endif + writeString (ret, req); + } + else if (!strcmp (inp, "checkConnection")) + { + String arg1 = readString (req); + ipc_log (" arg = `%s'\n", arg1 ? arg1 : "NULL"); + String res = dbeCheckConnection (arg1); + writeString (res, req); + ipc_log (" returns `%s'\n", res ? res : "NULL"); + free (arg1); + free (res); + } + else if (!strcmp (inp, "QUIT")) + { +#ifdef IPC_LOG + ipc_log (" %s\n", inp); +#endif + exit (0); + } + else + { + ipc_log ("Unrecognized input cmd \"%s\"; Aborting.\n", inp); + return 1; + } + ipc_log (" processing IPC command %s complete\n", inp); + free (inp); + fflush (stdout); + if (req->getStatus () != CANCELLED_IMMEDIATE) + // wake up the main working thread, let it take care of delete + req->setStatus (COMPLETED); + delete req; + return 0; +} + +void +check_env_args (int argc, char *argv[]) +{ + int indx = 2; // Skip "-IPC" + const char *MINUS_E = "-E"; + const char *SP_ER_PRINT_TRACE_LEVEL = "SP_ER_PRINT_TRACE_LEVEL"; + const char *SP_IPC_PROTOCOL = "SP_IPC_PROTOCOL"; + const char SEPARATOR = '='; + char *cmd_env_var = NULL; + while (argc - indx >= 2) + { + char *option = argv[indx++]; + if (!streq (option, MINUS_E)) + continue; + cmd_env_var = argv[indx++]; + char *separator = strchr (cmd_env_var, SEPARATOR); + if (!separator) + // Unrecognized option. Fatal error? + continue; + char *cmd_env_var_val = separator + 1; + if (!strncmp (cmd_env_var, SP_ER_PRINT_TRACE_LEVEL, + strlen (SP_ER_PRINT_TRACE_LEVEL))) + { + if (streq (cmd_env_var_val, "1")) + ipc_trace_level = TRACE_LVL_1; + else if (streq (cmd_env_var_val, "2")) + ipc_trace_level = TRACE_LVL_2; + else if (streq (cmd_env_var_val, "3")) + ipc_trace_level = TRACE_LVL_3; + else if (streq (cmd_env_var_val, "4")) + ipc_trace_level = TRACE_LVL_4; + continue; + } + if (!strncmp (cmd_env_var, SP_IPC_PROTOCOL, strlen (SP_IPC_PROTOCOL))) + { + if (streq (cmd_env_var_val, IPC_PROTOCOL_CURR)) + // Only one protocol is currently supported + ipc_protocol = IPC_PROTOCOL_CURR; + else + ipc_protocol = IPC_PROTOCOL_UNKNOWN; + continue; + } + // Unrecognized option. Fatal error? + } +} + +void +print_ipc_protocol_confirmation () +{ + if (NULL != ipc_protocol) + { + fprintf (stdout, "ER_IPC: %s\n", ipc_protocol); + fflush (stdout); + } +} + +void +ipc_mainLoop (int argc, char *argv[]) +{ + if (getenv ("GPROFNG_DBE_DELAY")) + sleep (20); +#ifdef IPC_LOG + ipc_flags = 1; +#endif + // check_env_args(argc, argv); + + char *er_print_trace_level = getenv ("SP_ER_PRINT_TRACE_LEVEL"); + if (er_print_trace_level != NULL) + { + if (streq (er_print_trace_level, "1")) + ipc_trace_level = TRACE_LVL_1; + else if (streq (er_print_trace_level, "2")) + ipc_trace_level = TRACE_LVL_2; + else if (streq (er_print_trace_level, "3")) + ipc_trace_level = TRACE_LVL_3; + else if (streq (er_print_trace_level, "4")) + ipc_trace_level = TRACE_LVL_4; + } + check_env_args (argc, argv); + print_ipc_protocol_confirmation (); + + if (ipc_flags || getenv ("SP_ER_PRINT_IPC_FLAG") || ipc_trace_level > TRACE_LVL_0) + { + ipc_flags = 1; + if (ipc_trace_level == TRACE_LVL_0) + ipc_trace_level = TRACE_LVL_1; + // reopen stderr as file "ipc_log" + ipc_log_name = getenv ("SP_ER_PRINT_IPC_LOG"); + if (ipc_log_name == NULL) + ipc_log_name = "ipc_log"; + freopen (ipc_log_name, "w", stderr); + if (ipc_trace_level >= TRACE_LVL_2) + { + ipc_request_log_name = "ipc_request_log"; + ipc_response_log_name = "ipc_response_log"; + requestLogFileP = fopen (ipc_request_log_name, "w"); + responseLogFileP = fopen (ipc_response_log_name, "w"); + } + else + { + ipc_request_log_name = "ipc_log"; + ipc_response_log_name = "ipc_log"; + } + begin_time = gethrtime (); + } + else + // Reopen stderr as /dev/null + freopen ("/dev/null", "w", stderr); + + struct sigaction act; + memset (&act, 0, sizeof (struct sigaction)); + term_flag = 0; + /* install a handler for TERM */ + ipc_request_trace (TRACE_LVL_1, "Installing SIGTERM handler to abort on error\n"); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigterm_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGTERM, &act, &old_sigterm_handler) == -1) + { + ipc_request_trace (TRACE_LVL_1, "Unable to install SIGTERM handler\n"); + abort (); + } + /* install a handler for INT */ + ipc_request_trace (TRACE_LVL_1, "Installing SIGINT handler to send message to analyzer\n"); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigint_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGINT, &act, &old_sigint_handler) == -1) + { + ipc_request_trace (TRACE_LVL_1, "Unable to install SIGINT handler\n"); + abort (); + } + ipc_log ("Installed SIGINT handler to handle Ctrl-C properly\n"); + int er_print_catch_crash = 1; // Default: catch fatal signals + char *s = getenv ("GPROFNG_ALLOW_CORE_DUMP"); + if (s && (strcasecmp (s, "no") == 0 || strcmp (s, "0") == 0)) + er_print_catch_crash = 0; + if (er_print_catch_crash) + { + /* reserve memory for fatal error processing */ + fatalErrorDynamicMemory = (char *) malloc (4 * 1024 * 1024); // reserve 4 MB + /* install a handler for SIGABRT */ + ipc_request_trace (TRACE_LVL_1, "Installing SIGABRT handler to send message to analyzer\n"); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigABRT_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGABRT, &act, NULL) == -1) + { + ipc_request_trace (TRACE_LVL_1, "Unable to install SIGABRT handler\n"); + // abort(); + } + else + ipc_log ("Installed SIGABRT handler to handle crash properly\n"); + /* install a handler for SIGSEGV */ + ipc_request_trace (TRACE_LVL_1, "Installing SIGABRT handler to send message to analyzer\n"); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigSEGV_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGSEGV, &act, NULL) == -1) + { + ipc_request_trace (TRACE_LVL_1, "Unable to install SIGSEGV handler\n"); + // abort(); + } + else + ipc_log ("Installed SIGSEGV handler to handle crash properly\n"); + } + ipc_request_trace (TRACE_LVL_1, "Entering ipc_mainLoop; run dir `%s'\n", + theApplication->get_run_dir ()); + cancelRequestedChannelID = 0xFFFFFFFF; + ipcThreadPool = new DbeThreadPool (0); // DbeThreadPool (-1); + responseBufferPool = new BufferPool (); + ipc_log (ipc_single_threaded_mode ? + "RUNNING er_print -IPC IN SINGLE THREADED MODE\n" : + "RUNNING er_print -IPC IN MULTITHREAD MODE\n"); + + /* Send "Ready" signal to the GUI */ + setProgress (100, "Restart engine"); + + /* start listening for requests */ + error_flag = 0; + for (;;) + { + readRequestHeader (); + if (term_flag == 1 || error_flag == 1) + { + ipc_request_trace (TRACE_LVL_1, "SIGTERM received, exiting\n"); + return; + } + } +} + +static const char * +table_name (int flavor) +{ + static char def_name[64]; + + switch ((FuncListDisp_type) flavor) + { + case DSP_FUNCTION: + return ("FUNCTION"); + case DSP_LINE: + return ("LINE"); + case DSP_PC: + return ("PC"); + case DSP_SOURCE: + return ("SOURCE"); + case DSP_DISASM: + return ("DISASM"); + case DSP_SELF: + return ("SELF"); + case DSP_CALLER: + return ("CALLER"); + case DSP_CALLEE: + return ("CALLEE"); + case DSP_CALLTREE: + return ("CALLTREE"); + case DSP_TIMELINE: + return ("TIMELINE"); + case DSP_STATIS: + return ("STATIS"); + case DSP_EXP: + return ("EXP"); + case DSP_LEAKLIST: + return ("LEAKLIST"); + case DSP_HEAPCALLSTACK: + return ("HEAP"); + case DSP_MEMOBJ: + return ("MEMOBJ"); + case DSP_DATAOBJ: + return ("DATAOBJ"); + case DSP_DLAYOUT: + return ("DLAYOUT"); + case DSP_SRC_FILE: + return ("SRC_FILE"); + case DSP_IFREQ: + return ("IFREQ"); + case DSP_RACES: + return ("RACES"); + case DSP_INDXOBJ: + return ("INDXOBJ"); + case DSP_DUALSOURCE: + return ("DUALSOURCE"); + case DSP_SOURCE_DISASM: + return ("SOURCE_DISASM"); + case DSP_DEADLOCKS: + return ("DEADLOCKS"); + case DSP_SOURCE_V2: + return ("SOURCE_V2"); + case DSP_DISASM_V2: + return ("DISASM_V2"); + case DSP_IOACTIVITY: + return ("IOACTIVITY"); + case DSP_OVERVIEW: + return ("OVERVIEW"); + case DSP_SAMPLE: + return ("SAMPLE -- UNEXPECTED"); + default: + snprintf (def_name, sizeof (def_name), "table number %d", flavor); + return (def_name); + } +} diff --git a/gprofng/src/ipcio.cc b/gprofng/src/ipcio.cc new file mode 100644 index 0000000..57f2617 --- /dev/null +++ b/gprofng/src/ipcio.cc @@ -0,0 +1,1025 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <iostream> +#include <iomanip> +#include <sstream> +#include <queue> +#include "vec.h" +#include "util.h" +#include "ipcio.h" +#include "DbeThread.h" +#include "Experiment.h" + +#define ipc_trace if (ipc_flags) ipc_default_log +#define ipc_request_trace if (ipc_flags) ipc_request_log +#define ipc_response_trace if (ipc_flags) ipc_response_log + +using namespace std; + +// IPC implementation +static const int L_PROGRESS = 0; +static const int L_INTEGER = 1; +static const int L_BOOLEAN = 2; +static const int L_LONG = 3; +static const int L_STRING = 4; +static const int L_DOUBLE = 5; +static const int L_ARRAY = 6; +static const int L_OBJECT = 7; +static const int L_CHAR = 8; + +int currentRequestID; +int currentChannelID; +static long maxSize; + +extern int cancellableChannelID; +extern int error_flag; +extern int ipc_delay_microsec; +extern FILE *responseLogFileP; + +IPCresponse *IPCresponseGlobal; + +BufferPool *responseBufferPool; + +IPCrequest::IPCrequest (int sz, int reqID, int chID) +{ + size = sz; + requestID = reqID; + channelID = chID; + status = INITIALIZED; + idx = 0; + buf = (char *) malloc (size); + cancelImmediate = false; +} + +IPCrequest::~IPCrequest () +{ + free (buf); +} + +void +IPCrequest::read (void) +{ + for (int i = 0; i < size; i++) + { + int c = getc (stdin); + ipc_request_trace (TRACE_LVL_4, " IPCrequest:getc(stdin): %02x\n", c); + buf[i] = c; + } +} + +IPCrequestStatus +IPCrequest::getStatus (void) +{ + return status; +} + +void +IPCrequest::setStatus (IPCrequestStatus newStatus) +{ + status = newStatus; +} + +static int +readByte (IPCrequest* req) +{ + int c; + int val = 0; + for (int i = 0; i < 2; i++) + { + if (req == NULL) + { + c = getc (stdin); + ipc_request_trace (TRACE_LVL_4, " readByte:getc(stdin): %02x\n", c); + } + else + c = req->rgetc (); + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + val = val * 16 + c - '0'; + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + val = val * 16 + c - 'a' + 10; + break; + case EOF: + val = EOF; + break; + default: + fprintf (stderr, "readByte: Unknown byte: %d\n", c); + break; + } + } + return val; +} + +static int +readIVal (IPCrequest *req) +{ + int val = readByte (req); + for (int i = 0; i < 3; i++) + val = val * 256 + readByte (req); + ipc_trace (" readIVal: %d\n", val); + return val; +} + +static String +readSVal (IPCrequest *req) +{ + int len = readIVal (req); + if (len == -1) + { + ipc_trace (" readSVal: <NULL>\n"); + return NULL; + } + char *str = (char *) malloc (len + 1); + char *s = str; + *s = (char) 0; + while (len--) + *s++ = req->rgetc (); + *s = (char) 0; + ipc_trace (" readSVal: '%s'\n", str); + return str; +} + +static long long +readLVal (IPCrequest *req) +{ + long long val = readByte (req); + for (int i = 0; i < 7; i++) + val = val * 256 + readByte (req); + ipc_trace (" readLVal: %lld\n", val); + return val; +} + +static bool +readBVal (IPCrequest *req) +{ + int val = readByte (req); + ipc_trace (" readBVal: %s\n", val == 0 ? "true" : "false"); + return val != 0; +} + +static char +readCVal (IPCrequest *req) +{ + int val = readByte (req); + ipc_trace (" readCVal: %d\n", val); + return (char) val; +} + +static double +readDVal (IPCrequest *req) +{ + String s = readSVal (req); + double d = atof (s); + free (s); + return d; +} + +static Object +readAVal (IPCrequest *req) +{ + bool twoD = false; + int type = readByte (req); + if (type == L_ARRAY) + { + twoD = true; + type = readByte (req); + } + ipc_trace ("readAVal: twoD=%s type=%d\n", twoD ? "true" : "false", type); + + int len = readIVal (req); + if (len == -1) + return NULL; + switch (type) + { + case L_INTEGER: + if (twoD) + { + Vector<Vector<int>*> *array = new Vector<Vector<int>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<int>*)readAVal (req)); + return array; + } + else + { + Vector<int> *array = new Vector<int>(len); + for (int i = 0; i < len; i++) + array->store (i, readIVal (req)); + return array; + } + //break; + case L_LONG: + if (twoD) + { + Vector<Vector<long long>*> *array = new Vector<Vector<long long>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<long long>*)readAVal (req)); + return array; + } + else + { + Vector<long long> *array = new Vector<long long>(len); + for (int i = 0; i < len; i++) + array->store (i, readLVal (req)); + return array; + } + //break; + case L_DOUBLE: + if (twoD) + { + Vector<Vector<double>*> *array = new Vector<Vector<double>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<double>*)readAVal (req)); + return array; + } + else + { + Vector<double> *array = new Vector<double>(len); + for (int i = 0; i < len; i++) + array->store (i, readDVal (req)); + return array; + } + //break; + case L_BOOLEAN: + if (twoD) + { + Vector < Vector<bool>*> *array = new Vector < Vector<bool>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<bool>*)readAVal (req)); + return array; + } + else + { + Vector<bool> *array = new Vector<bool>(len); + for (int i = 0; i < len; i++) + array->store (i, readBVal (req)); + return array; + } + //break; + case L_CHAR: + if (twoD) + { + Vector<Vector<char>*> *array = new Vector<Vector<char>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<char>*)readAVal (req)); + return array; + } + else + { + Vector<char> *array = new Vector<char>(len); + for (int i = 0; i < len; i++) + array->store (i, readCVal (req)); + return array; + } + //break; + case L_STRING: + if (twoD) + { + Vector<Vector<String>*> *array = new Vector<Vector<String>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<String>*)readAVal (req)); + return array; + } + else + { + Vector<String> *array = new Vector<String>(len); + for (int i = 0; i < len; i++) + array->store (i, readSVal (req)); + return array; + } + //break; + case L_OBJECT: + if (twoD) + { + Vector<Vector<Object>*> *array = new Vector<Vector<Object>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<Object>*)readAVal (req)); + return array; + } + else + { + Vector<Object> *array = new Vector<Object>(len); + for (int i = 0; i < len; i++) + array->store (i, readAVal (req)); + return array; + } + //break; + default: + fprintf (stderr, "readAVal: Unknown code: %d\n", type); + break; + } + return NULL; +} + +static int iVal; +static bool bVal; +static long long lVal; +static String sVal; +static double dVal; +static Object aVal; + +static void +readResult (int type, IPCrequest *req) +{ + int tVal = readByte (req); + switch (tVal) + { + case L_INTEGER: + iVal = readIVal (req); + break; + case L_LONG: + lVal = readLVal (req); + break; + case L_BOOLEAN: + bVal = readBVal (req); + break; + case L_DOUBLE: + dVal = readDVal (req); + break; + case L_STRING: + sVal = readSVal (req); + break; + case L_ARRAY: + aVal = readAVal (req); + break; + case EOF: + fprintf (stderr, "EOF read in readResult\n"); + sVal = NULL; + return; + default: + fprintf (stderr, "Unknown code: %d\n", tVal); + abort (); + } + if (type != tVal) + { + fprintf (stderr, "Internal error: readResult: parameter mismatch: type=%d should be %d\n", tVal, type); + abort (); + } +} + +int +readInt (IPCrequest *req) +{ + readResult (L_INTEGER, req); + return iVal; +} + +String +readString (IPCrequest *req) +{ + readResult (L_STRING, req); + return sVal; +} + +long long +readLong (IPCrequest *req) +{ + readResult (L_LONG, req); + return lVal; +} + +double +readDouble (IPCrequest *req) +{ + readResult (L_DOUBLE, req); + return dVal; +} + +bool +readBoolean (IPCrequest *req) +{ + readResult (L_BOOLEAN, req); + return bVal; +} + +DbeObj +readObject (IPCrequest *req) +{ + readResult (L_LONG, req); + return (DbeObj) lVal; +} + +Object +readArray (IPCrequest *req) +{ + readResult (L_ARRAY, req); + return aVal; +} + +// Write +IPCresponse::IPCresponse (int sz) +{ + requestID = -1; + channelID = -1; + responseType = -1; + responseStatus = RESPONSE_STATUS_SUCCESS; + sb = new StringBuilder (sz); + next = NULL; +} + +IPCresponse::~IPCresponse () +{ + delete sb; +} + +void +IPCresponse::reset () +{ + requestID = -1; + channelID = -1; + responseType = -1; + responseStatus = RESPONSE_STATUS_SUCCESS; + sb->setLength (0); +} + +void +IPCresponse::sendByte (int b) +{ + ipc_trace ("sendByte: %02x %d\n", b, b); + sb->appendf ("%02x", b); +} + +void +IPCresponse::sendIVal (int i) +{ + ipc_trace ("sendIVal: %08x %d\n", i, i); + sb->appendf ("%08x", i); +} + +void +IPCresponse::sendLVal (long long l) +{ + ipc_trace ("sendLVal: %016llx %lld\n", l, l); + sb->appendf ("%016llx", l); +} + +void +IPCresponse::sendSVal (const char *s) +{ + if (s == NULL) + { + sendIVal (-1); + return; + } + sendIVal ((int) strlen (s)); + ipc_trace ("sendSVal: %s\n", s); + sb->appendf ("%s", s); +} + +void +IPCresponse::sendBVal (bool b) +{ + sendByte (b ? 1 : 0); +} + +void +IPCresponse::sendCVal (char c) +{ + sendByte (c); +} + +void +IPCresponse::sendDVal (double d) +{ + char str[32]; + snprintf (str, sizeof (str), "%.12f", d); + sendSVal (str); +} + +void +IPCresponse::sendAVal (void *ptr) +{ + if (ptr == NULL) + { + sendByte (L_INTEGER); + sendIVal (-1); + return; + } + + VecType type = ((Vector<void*>*)ptr)->type (); + switch (type) + { + case VEC_INTEGER: + { + sendByte (L_INTEGER); + Vector<int> *array = (Vector<int>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendIVal (array->fetch (i)); + break; + } + case VEC_BOOL: + { + sendByte (L_BOOLEAN); + Vector<bool> *array = (Vector<bool>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendBVal (array->fetch (i)); + break; + } + case VEC_CHAR: + { + sendByte (L_CHAR); + Vector<char> *array = (Vector<char>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendCVal (array->fetch (i)); + break; + } + case VEC_LLONG: + { + sendByte (L_LONG); + Vector<long long> *array = (Vector<long long>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendLVal (array->fetch (i)); + break; + } + case VEC_DOUBLE: + { + sendByte (L_DOUBLE); + Vector<double> *array = (Vector<double>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendDVal (array->fetch (i)); + break; + } + case VEC_STRING: + { + sendByte (L_STRING); + Vector<String> *array = (Vector<String>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendSVal (array->fetch (i)); + break; + } + case VEC_STRINGARR: + { + sendByte (L_ARRAY); + sendByte (L_STRING); + Vector<void*> *array = (Vector<void*>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendAVal (array->fetch (i)); + break; + } + case VEC_INTARR: + { + sendByte (L_ARRAY); + sendByte (L_INTEGER); + Vector<void*> *array = (Vector<void*>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendAVal (array->fetch (i)); + break; + } + case VEC_LLONGARR: + { + sendByte (L_ARRAY); + sendByte (L_LONG); + Vector<void*> *array = (Vector<void*>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendAVal (array->fetch (i)); + break; + } + case VEC_VOIDARR: + { + sendByte (L_OBJECT); + Vector<void*> *array = (Vector<void*>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendAVal (array->fetch (i)); + break; + } + default: + fprintf (stderr, "sendAVal: Unknown type: %d\n", type); + abort (); + } +} + +static void +writeResponseHeader (int requestID, int responseType, int responseStatus, int nBytes) +{ + if (responseType == RESPONSE_TYPE_HANDSHAKE) + nBytes = IPC_VERSION_NUMBER; + int use_write = 2; + ipc_response_trace (TRACE_LVL_1, "ResponseHeaderBegin----- %x ---- %x ----- %x -----%x -------\n", requestID, responseType, responseStatus, nBytes); + if (use_write) + { + char buf[23]; + if (use_write == 1) + { + int i = 0; + snprintf (buf + i, 3, "%2x", HEADER_MARKER); + i += 2; + snprintf (buf + i, 9, "%8x", requestID); + i += 8; + snprintf (buf + i, 3, "%2x", responseType); + i += 2; + snprintf (buf + i, 3, "%2x", responseStatus); + i += 2; + snprintf (buf + i, 9, "%8x", nBytes); + } + else + snprintf (buf, 23, "%02x%08x%02x%02x%08x", HEADER_MARKER, requestID, + responseType, responseStatus, nBytes); + buf[22] = 0; + write (1, buf, 22); + } + else + { + cout << setfill ('0') << setw (2) << hex << HEADER_MARKER; + cout << setfill ('0') << setw (8) << hex << requestID; + cout << setfill ('0') << setw (2) << hex << responseType; + cout << setfill ('0') << setw (2) << hex << responseStatus; + cout << setfill ('0') << setw (8) << hex << nBytes; + cout.flush (); + } + ipc_response_trace (TRACE_LVL_1, "----------------------------ResponseHeaderEnd\n"); + if (nBytes > maxSize) + { + maxSize = nBytes; + ipc_trace ("New maxsize %ld\n", maxSize); + } +} + +bool +cancelNeeded (int chID) +{ + if (chID == cancellableChannelID && chID == cancelRequestedChannelID) + return true; + else + return false; +} + +static void +writeResponseWithHeader (int requestID, int channelID, int responseType, + int responseStatus, IPCresponse* os) +{ + if (cancelNeeded (channelID)) + { + responseStatus = RESPONSE_STATUS_CANCELLED; + ipc_trace ("CANCELLING %d %d\n", requestID, channelID); + // This is for gracefully cancelling regular ops like openExperiment - getFiles should never reach here + } + os->setRequestID (requestID); + os->setChannelID (channelID); + os->setResponseType (responseType); + os->setResponseStatus (responseStatus); + os->print (); + os->reset (); + responseBufferPool->recycle (os); +} + +void +writeAckFast (int requestID) +{ + writeResponseHeader (requestID, RESPONSE_TYPE_ACK, RESPONSE_STATUS_SUCCESS, 0); +} + +void +writeAck (int requestID, int channelID) +{ +#if DEBUG + char *s = getenv (NTXT ("SP_NO_IPC_ACK")); +#else /* ^DEBUG */ + char *s = NULL; +#endif /* ^DEBUG */ + if (s) + { + int i = requestID; + int j = channelID; + ipc_request_trace (TRACE_LVL_4, "ACK skipped: requestID=%d channelID=%d\n", i, j); + } + else + { + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_SMALL); + writeResponseWithHeader (requestID, channelID, RESPONSE_TYPE_ACK, + RESPONSE_STATUS_SUCCESS, OUTS); + } +} + +void +writeHandshake (int requestID, int channelID) +{ + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_SMALL); + writeResponseWithHeader (requestID, channelID, RESPONSE_TYPE_HANDSHAKE, RESPONSE_STATUS_SUCCESS, OUTS); + // writeResponseHeader(requestID, RESPONSE_TYPE_HANDSHAKE, RESPONSE_STATUS_SUCCESS, IPC_VERSION_NUMBER); +} + +void +writeResponseGeneric (int responseStatus, int requestID, int channelID) +{ + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_SMALL); + writeResponseWithHeader (requestID, channelID, RESPONSE_TYPE_COMPLETE, responseStatus, OUTS); +} + +BufferPool::BufferPool () +{ + pthread_mutex_init (&p_mutex, NULL); + smallBuf = NULL; + largeBuf = NULL; +} + +BufferPool::~BufferPool () +{ + for (IPCresponse *p = smallBuf; p;) + { + IPCresponse *tmp = p; + p = tmp->next; + delete tmp; + } + for (IPCresponse *p = largeBuf; p;) + { + IPCresponse *tmp = p; + p = tmp->next; + delete tmp; + } +} + +IPCresponse* +BufferPool::getNewResponse (int size) +{ + pthread_mutex_lock (&p_mutex); + if (ipc_single_threaded_mode && size < BUFFER_SIZE_LARGE) + size = BUFFER_SIZE_LARGE; + IPCresponse *newResponse = NULL; + if (size >= BUFFER_SIZE_LARGE) + { + if (largeBuf) + { + newResponse = largeBuf; + largeBuf = largeBuf->next; + } + } + else if (smallBuf) + { + newResponse = smallBuf; + smallBuf = smallBuf->next; + } + if (newResponse) + newResponse->reset (); + else + { + newResponse = new IPCresponse (size); + ipc_trace ("GETNEWBUFFER %d\n", size); + } + pthread_mutex_unlock (&p_mutex); + return newResponse; +} + +void +BufferPool::recycle (IPCresponse *respB) +{ + pthread_mutex_lock (&p_mutex); + if (respB->getCurBufSize () >= BUFFER_SIZE_LARGE) + { + respB->next = largeBuf; + largeBuf = respB; + } + else + { + respB->next = smallBuf; + smallBuf = respB; + } + pthread_mutex_unlock (&p_mutex); +} + +void +writeArray (void *ptr, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_LARGE); + OUTS->sendByte (L_ARRAY); + OUTS->sendAVal (ptr); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), + RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeString (const char *s, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_LARGE); + OUTS->sendByte (L_STRING); + OUTS->sendSVal (s); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), + RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeObject (DbeObj obj, IPCrequest* req) +{ + writeLong ((long long) obj, req); +} + +void +writeBoolean (bool b, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_BOOLEAN); + OUTS->sendBVal (b); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), + RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeInt (int i, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_INTEGER); + OUTS->sendIVal (i); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeChar (char c, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_CHAR); + OUTS->sendCVal (c); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeLong (long long l, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_LONG); + OUTS->sendLVal (l); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeDouble (double d, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_DOUBLE); + OUTS->sendDVal (d); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +int +setProgress (int percentage, const char *proc_str) +{ + if (cancelNeeded (currentChannelID)) + { + // ExperimentLoadCancelException *e1 = new ExperimentLoadCancelException(); + // throw (e1); + return 1; + } + if (NULL == proc_str) + return 1; + int size = strlen (proc_str) + 100; // 100 bytes for additional data + int bs = BUFFER_SIZE_MEDIUM; + if (size > BUFFER_SIZE_MEDIUM) + { + if (size > BUFFER_SIZE_LARGE) return 1; // This should never happen + bs = BUFFER_SIZE_LARGE; + } + IPCresponse *OUTS = responseBufferPool->getNewResponse (bs); + OUTS->sendByte (L_PROGRESS); + OUTS->sendIVal (percentage); + OUTS->sendSVal (proc_str); + writeResponseWithHeader (currentRequestID, currentChannelID, RESPONSE_TYPE_PROGRESS, RESPONSE_STATUS_SUCCESS, OUTS); + return 0; +} + +void +IPCresponse::print (void) +{ + if (ipc_delay_microsec) + usleep (ipc_delay_microsec); + int stringSize = sb->length (); + writeResponseHeader (requestID, responseType, responseStatus, stringSize); + if (stringSize > 0) + { + char *s = sb->toString (); + hrtime_t start_time = gethrtime (); + int use_write = 1; + if (use_write) + write (1, s, stringSize); // write(1, sb->toString(), stringSize); + else + { + cout << s; + cout.flush (); + } + hrtime_t end_time = gethrtime (); + unsigned long long time_stamp = end_time - start_time; + ipc_response_log (TRACE_LVL_3, "ReqID %x flush time %llu nanosec \n", requestID, time_stamp); + free (s); + } +} + +void +setCancelRequestedCh (int chID) +{ + cancelRequestedChannelID = chID; +} + +void +readRequestHeader () +{ + int marker = readByte (NULL); + if (marker != HEADER_MARKER) + { + fprintf (stderr, "Internal error: received request (%d) without header marker\n", marker); + error_flag = 1; + return; + } + else + ipc_request_trace (TRACE_LVL_1, "RequestHeaderBegin------------------------\n"); + int requestID = readIVal (NULL); + int requestType = readByte (NULL); + int channelID = readIVal (NULL); + int nBytes = readIVal (NULL); + if (requestType == REQUEST_TYPE_HANDSHAKE) + { + // write the ack directly to the wire, not through the response queue + // writeAckFast(requestID); + writeAck (requestID, channelID); + maxSize = 0; + writeHandshake (requestID, channelID); + ipc_request_trace (TRACE_LVL_1, "RQ: HANDSHAKE --- %x ----- %x ---- %x --- %x -RequestHeaderEnd\n", requestID, requestType, channelID, nBytes); + } + else if (requestType == REQUEST_TYPE_CANCEL) + { + writeAck (requestID, channelID); + ipc_request_trace (TRACE_LVL_1, "RQ: CANCEL --- RQ: %x ----- %x --- CH: %x --- %x -RequestHeaderEnd\n", requestID, requestType, channelID, nBytes); + if (channelID == cancellableChannelID) + { + // we have worked on at least one request belonging to this channel + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, requestID, channelID); + setCancelRequestedCh (channelID); + ipc_trace ("CANCELLABLE %x %x\n", channelID, currentChannelID); + if (channelID == currentChannelID) + // request for this channel is currently in progress + ipc_request_trace (TRACE_LVL_1, "IN PROGRESS REQUEST NEEDS CANCELLATION"); + // ssp_post_cond(waitingToFinish); + } + else + { + // FIXME: + // it is possible that a request for this channel is on the requestQ + // or has been submitted to the work group queue but is waiting for a thread to pick it up + writeResponseGeneric (RESPONSE_STATUS_FAILURE, requestID, channelID); + setCancelRequestedCh (channelID); + ipc_request_trace (TRACE_LVL_1, "RETURNING FAILURE TO CANCEL REQUEST channel %d\n", channelID); + } + } + else + { + writeAck (requestID, channelID); + ipc_request_trace (TRACE_LVL_1, "RQ: --- %x ----- %x ---- %x --- %x -RequestHeaderEnd\n", requestID, requestType, channelID, nBytes); + IPCrequest *nreq = new IPCrequest (nBytes, requestID, channelID); + nreq->read (); + ipc_request_trace (TRACE_LVL_1, "RQ: --- %x Read from stream \n", requestID); + if (cancelNeeded (channelID)) + { + ipc_request_trace (TRACE_LVL_1, "CANCELLABLE REQ RECVD %x %x\n", channelID, requestID); + writeResponseGeneric (RESPONSE_STATUS_CANCELLED, requestID, channelID); + delete nreq; + return; + } + DbeQueue *q = new DbeQueue (ipc_doWork, nreq); + ipcThreadPool->put_queue (q); + } +} diff --git a/gprofng/src/ipcio.h b/gprofng/src/ipcio.h new file mode 100644 index 0000000..94a635e --- /dev/null +++ b/gprofng/src/ipcio.h @@ -0,0 +1,176 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* Defines the external interface between er_ipc and the routines */ + +#ifndef _IPCIO_H +#define _IPCIO_H +#include <pthread.h> +#include "gp-defs.h" +#include "StringBuilder.h" + +class DbeThreadPool; +typedef long long DbeObj; +typedef void *Object; +typedef char *String; + +#define BUFFER_SIZE_SMALL 512 +#define BUFFER_SIZE_MEDIUM 512 +#define BUFFER_SIZE_LARGE 1024*1024 + +#define REQUEST_HAS_NO_BODY 0xFFFFFFFF +#define RESPONSE_STATUS_DEFAULT 0 +#define RESPONSE_STATUS_SUCCESS 1 +#define RESPONSE_STATUS_FAILURE 2 +#define RESPONSE_STATUS_CANCELLED 3 + +#define RESPONSE_TYPE_ACK 0 +#define RESPONSE_TYPE_PROGRESS 1 +#define RESPONSE_TYPE_COMPLETE 2 +#define RESPONSE_TYPE_HANDSHAKE 3 +#define HEADER_MARKER 0xff + +#define REQUEST_TYPE_DEFAULT 0 +#define REQUEST_TYPE_CANCEL 1 +#define REQUEST_TYPE_HANDSHAKE 2 + +#define IPC_PROTOCOL_STR "IPC_PROTOCOL_38" +#define IPC_VERSION_NUMBER 38 + +enum IPCrequestStatus +{ + INITIALIZED = 0, + IN_PROGRESS, + COMPLETED, + CANCELLED_DEFAULT, + CANCELLED_IMMEDIATE +}; + +enum IPCTraceLevel +{ + TRACE_LVL_0 = 0, + TRACE_LVL_1, + TRACE_LVL_2, + TRACE_LVL_3, + TRACE_LVL_4 +}; + +class IPCrequest +{ + char *buf; + int size; + int idx; + int requestID; + int channelID; + IPCrequestStatus status; + bool cancelImmediate; +public: + IPCrequest (int, int, int); + ~IPCrequest (); + IPCrequestStatus getStatus (); + void setStatus (IPCrequestStatus); + void read (); + + int getRequestID () { return requestID; } + int getChannelID () { return channelID; } + bool isCancelImmediate () { return cancelImmediate; } + void setCancelImmediate () { cancelImmediate = true; } + char rgetc () { return buf[idx++]; } +}; + +class IPCresponse +{ +public: + IPCresponse (int sz); + ~IPCresponse (); + + int getRequestID () { return requestID; } + int getChannelID () { return channelID; } + void setRequestID (int r) { requestID = r; } + void setChannelID (int c) { channelID = c; } + void setResponseType (int r) { responseType = r; } + void setResponseStatus (int s) { responseStatus = s; } + int getCurBufSize () { return sb->capacity (); } + void sendByte (int); + void sendIVal (int); + void sendLVal (long long); + void sendDVal (double); + void sendSVal (const char *); + void sendBVal (bool); + void sendCVal (char); + void sendAVal (void*); + void print (void); + void reset (); + IPCresponse *next; + +private: + int requestID; + int channelID; + int responseType; + int responseStatus; + StringBuilder *sb; +}; + +class BufferPool +{ +public: + BufferPool (); + ~BufferPool (); + IPCresponse* getNewResponse (int); + void recycle (IPCresponse *); +private: + pthread_mutex_t p_mutex; + IPCresponse *smallBuf; + IPCresponse *largeBuf; +}; + +// Read from the wire +int readInt (IPCrequest*); +bool readBoolean (IPCrequest*); +long long readLong (IPCrequest*); +DbeObj readObject (IPCrequest*); +Object readArray (IPCrequest*); +String readString (IPCrequest*); +void readRequestHeader (); + +// write to the wire +void writeString (const char *, IPCrequest*); +void writeBoolean (bool, IPCrequest*); +void writeInt (int, IPCrequest*); +void writeChar (char, IPCrequest*); +void writeLong (long long, IPCrequest*); +void writeDouble (double, IPCrequest*); +void writeArray (void *, IPCrequest*); +void writeObject (DbeObj, IPCrequest*); +void writeResponseGeneric (int, int, int); +int setProgress (int, const char *); // Update the progress bar +int ipc_doWork (void *); // The argument is an IPCrequest + +extern int ipc_flags; +extern int ipc_single_threaded_mode; +extern DbeThreadPool *responseThreadPool; +extern DbeThreadPool *ipcThreadPool; +extern int cancelRequestedChannelID; + +void ipc_default_log (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void ipc_response_log (IPCTraceLevel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +void ipc_request_log (IPCTraceLevel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + +#endif diff --git a/gprofng/src/machinemodels/generic.ermm b/gprofng/src/machinemodels/generic.ermm new file mode 100644 index 0000000..19fcc8e --- /dev/null +++ b/gprofng/src/machinemodels/generic.ermm @@ -0,0 +1,32 @@ +# generic machinemodel file +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + +#mobj_define Vpage_4K "(((ea_pagesize==1<<12 || !ea_pagesize) && VADDR>255)?(VADDR>>12<<12):-1)" +#mobj_define Ppage_4K "((ea_pagesize==1<<12 && PADDR)?(PADDR>>12<<12):-1)" diff --git a/gprofng/src/machinemodels/m5.ermm b/gprofng/src/machinemodels/m5.ermm new file mode 100644 index 0000000..83f125d --- /dev/null +++ b/gprofng/src/machinemodels/m5.ermm @@ -0,0 +1,65 @@ +# Machinemodel file for M5 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define M5_Chip ((CPUID>>3)/6) +indxobj_define M5_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" + +# comment out *CacheTag definitions since we don't have use cases to justify their complexity +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define M5_L1ICacheSet "((PHYSPC>>5)&0x7F)" +#mobj_define M5_L1ICacheTag "((PHYSPC>>12)&0x7FFFFFFFF)" +#mobj_define M5_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" +#mobj_define M5_L1DCacheTag "(PADDR?((PADDR>>12)&0x7FFFFFFFF):-1)" + +#mobj_define M5_L2ICacheSet "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>5)&0x1FF)" +#mobj_define M5_L2ICacheTag "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>14)&0x1FFFFFFFF)" +#mobj_define M5_L2DCacheSet "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>5)&0x1FF):-1)" +#mobj_define M5_L2DCacheTag "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>14)&0x1FFFFFFFF):-1)" + +#mobj_define M5_L3DCacheSet "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0xFFFF):-1)" +#mobj_define M5_L3DCacheTag "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>22)&0x3FFFFFF):-1)" +#mobj_define M5_L3DBank "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x3):-1)" diff --git a/gprofng/src/machinemodels/m6.ermm b/gprofng/src/machinemodels/m6.ermm new file mode 100644 index 0000000..1071e9e --- /dev/null +++ b/gprofng/src/machinemodels/m6.ermm @@ -0,0 +1,65 @@ +# Machinemodel file for M6 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define M6_Chip ((CPUID>>3)/12) +indxobj_define M6_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" + +# comment out *CacheTag definitions since we don't have use cases to justify their complexity +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define M6_L1ICacheSet "((PHYSPC>>5)&0x7F)" +#mobj_define M6_L1ICacheTag "((PHYSPC>>12)&0x7FFFFFFFF)" +#mobj_define M6_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" +#mobj_define M6_L1DCacheTag "(PADDR?((PADDR>>12)&0x7FFFFFFFF):-1)" + +#mobj_define M6_L2ICacheSet "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>5)&0x1FF)" +#mobj_define M6_L2ICacheTag "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>14)&0x1FFFFFFFF)" +#mobj_define M6_L2DCacheSet "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>5)&0x1FF):-1)" +#mobj_define M6_L2DCacheTag "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>14)&0x1FFFFFFFF):-1)" + +#mobj_define M6_L3DCacheSet "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0xFFFF):-1)" +#mobj_define M6_L3DCacheTag "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>22)&0x3FFFFFF):-1)" +#mobj_define M6_L3DBank "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x3):-1)" diff --git a/gprofng/src/machinemodels/m7.ermm b/gprofng/src/machinemodels/m7.ermm new file mode 100644 index 0000000..29e3bef --- /dev/null +++ b/gprofng/src/machinemodels/m7.ermm @@ -0,0 +1,64 @@ +# Machinemodel file for M7/T7 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define M7_Chip (CPUID>>8) +indxobj_define M7_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" +#mobj_define Vpage_16G "((ea_pagesize==1<<34 && VADDR>255)?(VADDR>>34<<34):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" +#mobj_define Ppage_16G "((ea_pagesize==1<<34 && PADDR)?(PADDR>>34<<34):-1)" + +# we dropped the *CacheTag definitions since: +# - they're rarely used +# - it's unclear if they are correct for S4 +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define M7_L1ICacheSet "((PHYSPC>>6)&0x3F)" +#mobj_define M7_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" + +#mobj_define M7_L2ICacheSet "((((PHYSPC&0xFFFFFFFFFFF00FFF)|(((PHYSPC>>24)^(PHYSPC>>16)^(PHYSPC>>8)^PHYSPC)&0xFF000))>>6)&0x1FF)" +#mobj_define M7_L2DCacheSet "(PADDR?((((PADDR&0x2000000000000)?PADDR:((PADDR&0xFFFFFFFFFFF00FFF)|(((PADDR>>24)^(PADDR>>16)^(PADDR>>8)^PADDR)&0xFF000)))>>6)&0x01FF):-1)" + +#mobj_define M7_L3DCacheSet "(PADDR?((((PADDR&0x2000000000000)?PADDR:((PADDR&0xFFFFFFFFFFF00FFF)|(((PADDR>>24)^(PADDR>>16)^(PADDR>>8)^PADDR)&0xFF000)))>>6)&0x3FFF):-1)" +#mobj_define M7_L3DBank "(PADDR?((((PADDR&0x2000000000000)?PADDR:((PADDR&0xFFFFFFFFFFF00FFF)|(((PADDR>>24)^(PADDR>>16)^(PADDR>>8)^PADDR)&0xFF000)))>>6)&0x0001):-1)" diff --git a/gprofng/src/machinemodels/t4.ermm b/gprofng/src/machinemodels/t4.ermm new file mode 100644 index 0000000..e27f3a4 --- /dev/null +++ b/gprofng/src/machinemodels/t4.ermm @@ -0,0 +1,67 @@ +# Machinemodel file for T4 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define T4_Chip (CPUID>>6) +indxobj_define T4_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_512K "((ea_pagesize==1<<19 && VADDR>255)?(VADDR>>19<<19):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_512K "((ea_pagesize==1<<19 && PADDR)?(PADDR>>19<<19):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" + +# comment out *CacheTag definitions since we don't have use cases to justify their complexity +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define T4_L1ICacheSet "((PHYSPC>>5)&0x7F)" +#mobj_define T4_L1ICacheTag "((PHYSPC>>12)&0x7FFFFFFFF)" +#mobj_define T4_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" +#mobj_define T4_L1DCacheTag "(PADDR?((PADDR>>12)&0x7FFFFFFFF):-1)" +#mobj_define T4_L2ICacheSet "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>5)&0x1FF)" +#mobj_define T4_L2ICacheTag "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>14)&0x1FFFFFFFF)" +#mobj_define T4_L2DCacheSet "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>5)&0x1FF):-1)" +#mobj_define T4_L2DCacheTag "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>14)&0x1FFFFFFFF):-1)" +#mobj_define T4_L3DCacheSet "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0xFFF):-1)" +#mobj_define T4_L3DCacheTag "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>18)&0x1FFFFFFF):-1)" +#mobj_define T4_L3DBank "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x7):-1)" +#mobj_define T4_2_Socket "(PADDR?((PADDR>>33)&0x1):-1)" +#mobj_define T4_4_Socket "(PADDR?((PADDR>>33)&0x3):-1)" diff --git a/gprofng/src/machinemodels/t5.ermm b/gprofng/src/machinemodels/t5.ermm new file mode 100644 index 0000000..6d666a9 --- /dev/null +++ b/gprofng/src/machinemodels/t5.ermm @@ -0,0 +1,65 @@ +# Machinemodel file for T5 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# 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 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define T5_Chip (CPUID>>7) +indxobj_define T5_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" + +# comment out *CacheTag definitions since we don't have use cases to justify their complexity +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define T5_L1ICacheSet "((PHYSPC>>5)&0x7F)" +#mobj_define T5_L1ICacheTag "((PHYSPC>>12)&0x7FFFFFFFF)" +#mobj_define T5_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" +#mobj_define T5_L1DCacheTag "(PADDR?((PADDR>>12)&0x7FFFFFFFF):-1)" + +#mobj_define T5_L2ICacheSet "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>5)&0x1FF)" +#mobj_define T5_L2ICacheTag "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>14)&0x1FFFFFFFF)" +#mobj_define T5_L2DCacheSet "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>5)&0x1FF):-1)" +#mobj_define T5_L2DCacheTag "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>14)&0x1FFFFFFFF):-1)" + +#mobj_define T5_L3DCacheSet "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x1FFF):-1)" +#mobj_define T5_L3DCacheTag "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>19)&0x1FFFFFFF):-1)" +#mobj_define T5_L3DBank "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x7):-1)" diff --git a/gprofng/src/parse.cc b/gprofng/src/parse.cc new file mode 100644 index 0000000..ab22270 --- /dev/null +++ b/gprofng/src/parse.cc @@ -0,0 +1,927 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <sys/param.h> +#include <sys/mman.h> + +#include "util.h" +#include "DbeFile.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Emsg.h" +#include "Function.h" +#include "LoadObject.h" +#include "Module.h" +#include "PRBTree.h" +#include "Sample.h" +#include "Elf.h" + +void +Experiment::mrec_insert (MapRecord *mrec) +{ + int sz = mrecs->size (); + MapRecord *tmp = sz > 0 ? mrecs->fetch (sz - 1) : NULL; + + // The following should work in most cases + if (tmp == NULL || tmp->ts <= mrec->ts) + { + mrecs->append (mrec); + return; + } + + // If it didn't... + int lo = 0; + int hi = sz - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + tmp = mrecs->fetch (md); + if (tmp->ts < mrec->ts) + lo = md + 1; + else + hi = md - 1; + } + mrecs->insert (lo, mrec); +} + +int +Experiment::process_arglist_cmd (char *, char *arglist) +{ + uarglist = arglist; + + // find argv[0], and extract its basename + if (strcmp (uarglist, NTXT ("(fork)")) == 0) + return 0; // leaving target name NULL + char *p = uarglist; + char *pp = uarglist; + char *pl; + for (;;) + { + if (*p == '/') + pp = p + 1; + if (*p == ' ' || *p == 0) + { + pl = p; + break; + } + p++; + } + size_t len = pl - pp; + if (len > 0) + utargname = dbe_sprintf (NTXT ("%.*s"), (int) len, pp); + return 0; +} + +int +Experiment::process_desc_start_cmd (char *, hrtime_t ts, char *flavor, + char *nexp, int follow, char *txt) +{ + char *str; + Emsg *m; + + if (follow == 1) + str = dbe_sprintf (GTXT ("Starting %s %ld.%09ld, exp %s.er, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + nexp, txt); + else + str = dbe_sprintf (GTXT ("Starting %s %ld.%09ld, no experiment, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + txt); + m = new Emsg (CMSG_COMMENT, str); + free (str); + runlogq->append (m); + + free (flavor); + free (nexp); + free (txt); + return 0; +} + +int +Experiment::process_desc_started_cmd (char *, hrtime_t ts, char *flavor, + char *nexp, int follow, char *txt) +{ + char *str; + Emsg *m; + + if (follow == 1) + str = dbe_sprintf (GTXT ("Started %s %ld.%09ld, exp %s.er, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + nexp, txt); + else + str = dbe_sprintf (GTXT ("Started %s %ld.%09ld, no experiment, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + txt); + m = new Emsg (CMSG_COMMENT, str); + free (str); + runlogq->append (m); + free (flavor); + free (nexp); + free (txt); + return 0; +} + +LoadObject * +Experiment::get_dynfunc_lo (const char *loName) +{ + LoadObject *lo = loadObjMap->get (loName); + if (lo == NULL) + { + lo = createLoadObject (loName, expIdx);// DYNFUNC_SEGMENT is always unique + lo->dbeFile->filetype |= DbeFile::F_FICTION; + lo->flags |= SEG_FLAG_DYNAMIC; + lo->type = LoadObject::SEG_TEXT; + lo->set_platform (platform, wsize); + append (lo); + } + return lo; +} + +Function * +Experiment::create_dynfunc (Module *mod, char *fname, int64_t vaddr, + int64_t fsize) +{ + Function *f = dbeSession->createFunction (); + f->set_name (fname); + f->flags |= FUNC_FLAG_DYNAMIC; + f->size = fsize; + f->img_offset = vaddr; + f->module = mod; + mod->functions->append (f); + mod->loadobject->functions->append (f); + return f; +} + +static int +func_cmp (const void *a, const void *b) +{ + Function *fp1 = *((Function **) a); + Function *fp2 = *((Function **) b); + uint64_t i1 = fp1->img_offset; + uint64_t i2 = fp2->img_offset; + return i1 < i2 ? -1 : i1 == i2 ? 0 : 1; +} + +int +Experiment::process_fn_load_cmd (Module *mod, char *fname, Vaddr vaddr, + int fsize, hrtime_t ts) +{ + Dprintf (DEBUG_MAPS, + "process_fn_load_cmd:%s (%s) vaddr=0x%llx msize=%lld ts=%lld\n", + STR (mod ? mod->get_name () : NULL), STR (fname), + (unsigned long long) vaddr, (long long) fsize, (long long) ts); + if (mod != NULL) + { + mod->functions->sort (func_cmp); + uint64_t lastVaddr = vaddr; + for (int i = 0, sz = mod->functions->size (); i < sz; i++) + { + Function *f = mod->functions->fetch (i); + if (lastVaddr < f->img_offset) + { + char *fnm = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"), + (unsigned long long) lastVaddr, fname); + create_dynfunc (mod, fnm, lastVaddr, f->img_offset - lastVaddr); + free (fnm); + } + lastVaddr = f->img_offset + f->size; + } + if (lastVaddr < vaddr + fsize) + { + char *fnm = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"), + (unsigned long long) lastVaddr, fname); + create_dynfunc (mod, fnm, lastVaddr, vaddr + fsize - lastVaddr); + free (fnm); + } + mod->functions->sort (func_cmp); + for (int i = 0, sz = mod->functions->size (); i < sz; i++) + { + Function *f = mod->functions->fetch (i); + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = f; + mrec->base = f->img_offset; + mrec->size = f->size; + mrec->ts = ts; + mrec->foff = 0; + mrec_insert (mrec); + } + return 0; + } + + LoadObject *ds = get_dynfunc_lo (DYNFUNC_SEGMENT); + Function *dfunc = create_dynfunc (ds->noname, fname, vaddr, fsize); + + // check for special functions, USER, IDLE, TRUNC to disable offsets in disassembly + // XXX -- check based on name now + // Optimization: use pre-initialized localized strings + static const char * localized_USER_MODE = NULL; + static const char * localized_IDLE = NULL; + static const char * localized_TRUNCATED_STACK = NULL; + if (localized_USER_MODE == NULL) + { + localized_USER_MODE = GTXT ("<USER_MODE>"); + localized_IDLE = GTXT ("<IDLE>"); + localized_TRUNCATED_STACK = GTXT ("<TRUNCATED_STACK>"); + } + if (strcmp (fname, localized_USER_MODE) == 0 + || strcmp (fname, localized_IDLE) == 0 + || strcmp (fname, localized_TRUNCATED_STACK) == 0) + dfunc->flags |= FUNC_FLAG_NO_OFFSET; + + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = dfunc; + mrec->base = vaddr; + mrec->size = fsize; + mrec->ts = ts; + mrec->foff = 0; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_fn_unload_cmd (char *, Vaddr vaddr, hrtime_t ts) +{ + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::UNLOAD; + mrec->base = vaddr; + mrec->ts = ts; + mrec_insert (mrec); + return 0; +} + +void +Experiment::register_metric (Metric::Type type) +{ + BaseMetric *mtr = dbeSession->register_metric (type); + metrics->append (mtr); +} + +void +Experiment::register_metric (Hwcentry *ctr, const char* aux, const char* uname) +{ + BaseMetric *mtr = dbeSession->register_metric (ctr, aux, uname); + metrics->append (mtr); + if (mtr->get_dependent_bm ()) + metrics->append (mtr->get_dependent_bm ()); +} + +int +Experiment::process_hwcounter_cmd (char *, int cpuver, char *counter, + char * int_name, int interval, int tag, + int i_tpc, char *modstr) +{ + char *str; + Emsg *m; + Hwcentry *ctr; + ABST_type tpc = (ABST_type) i_tpc; + + // Use previously ignored tag to associate counter packets. + if (tag < 0 || tag >= MAX_HWCOUNT) + { + // invalid tag specified, warn user + str = dbe_sprintf (GTXT ("*** Error: HW counter tag %d out of range [%d - %d]; ignored"), + tag, 0, MAX_HWCOUNT - 1); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + free (counter); + return 0; + } + if (coll_params.hw_aux_name[tag]) + { + // duplicate tag used, warn user + str = dbe_sprintf (GTXT ("*** Error: Duplicate HW counter tag %d specified; ignored"), + tag); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + free (counter); + return 0; + } + hw_cpuver = cpuver; + + // map it to a machinemodel string + if (hw_cpuver != CPUVER_UNDEFINED) + { + free (machinemodel); + if (hw_cpuver == 1104) + machinemodel = dbe_strdup (NTXT ("t4")); + else if (hw_cpuver == 1110) + machinemodel = dbe_strdup (NTXT ("t5")); + else if (hw_cpuver == 1204) + machinemodel = dbe_strdup (NTXT ("m4")); + else if (hw_cpuver == 1210) + machinemodel = dbe_strdup (NTXT ("m5")); + else if (hw_cpuver == 1220) + machinemodel = dbe_strdup (NTXT ("m6")); + else if (hw_cpuver == 1230) + machinemodel = dbe_strdup (NTXT ("m7")); + else + machinemodel = dbe_strdup (NTXT ("generic")); + } + + // Find the entry in the machine table, and dup it + ctr = new Hwcentry; + dbeSession->append (ctr); + hwc_post_lookup (ctr, counter, int_name, cpuver); + ctr->sort_order = tag; + ctr->memop = tpc; + + // Check if HWC name is to be modified + if (modstr != NULL) + { + char *s = ctr->name; + ctr->name = dbe_sprintf (NTXT ("%s%s"), modstr, s); + s = ctr->int_name; + ctr->int_name = dbe_sprintf (NTXT ("%s%s"), modstr, s); + s = ctr->metric; + if (s) + ctr->metric = dbe_sprintf (NTXT ("%s%s"), modstr, s); + } + + char * cname = dbe_strdup (ctr->name); + char * uname = dbe_strdup (hwc_i18n_metric (ctr)); + coll_params.hw_aux_name[tag] = cname; + coll_params.hw_username[tag] = uname; + coll_params.hw_interval[tag] = interval; + coll_params.hw_tpc[tag] = tpc; + coll_params.hw_cpu_ver[tag] = cpuver; + + // set hw_mode and xhw_mode? + coll_params.hw_mode = 1; + if (ABST_MEMSPACE_ENABLED (tpc)) + { + // yes, dataspace data available + coll_params.xhw_mode = 1; + + // set dataspace available + dataspaceavail = true; + } + register_metric (ctr, cname, uname); + free (counter); + return 0; +} + +// TBR:? + +int +Experiment::process_hwsimctr_cmd (char *, int cpuver, char *nm, char *int_name, + char *metric, int reg, + int interval, int timecvt, int i_tpc, int tag) +{ + char *str; + Emsg *m; + Hwcentry *ctr; + ABST_type tpc = (ABST_type) i_tpc; + + // Use previously ignored tag to associate counter packets. + if (tag < 0 || tag >= MAX_HWCOUNT) + { + // invalid tag specified, warn user + str = dbe_sprintf (GTXT ("*** Error: HW counter tag %d out of range [%d - %d]; ignored"), + tag, 0, MAX_HWCOUNT - 1); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + + free (nm); + free (int_name); + free (metric); + return 0; + } + if (coll_params.hw_aux_name[tag]) + { + // duplicate tag used, warn user + str = dbe_sprintf (GTXT ("*** Error: Duplicate HW counter tag %d specified; ignored"), + tag); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + free (nm); + free (int_name); + free (metric); + return 0; + } + hw_cpuver = cpuver; + ctr = new Hwcentry; + { + static Hwcentry empty; + *ctr = empty; + } + ctr->name = nm; + ctr->int_name = int_name; + ctr->metric = metric; + ctr->reg_num = reg; + ctr->val = interval; + ctr->timecvt = timecvt; + ctr->memop = tpc; + ctr->sort_order = tag; + + char *cname = dbe_strdup (ctr->name); + char *uname = dbe_strdup (hwc_i18n_metric (ctr)); + + coll_params.hw_aux_name[tag] = cname; + coll_params.hw_username[tag] = uname; + coll_params.hw_interval[tag] = interval; + coll_params.hw_tpc[tag] = tpc; + coll_params.hw_cpu_ver[tag] = cpuver; + + // set hw_mode and xhw_mode? + coll_params.hw_mode = 1; + if (ABST_MEMSPACE_ENABLED (tpc)) + { + coll_params.xhw_mode = 1; + // set dataspace available + if (getenv ("ANALYZER_DATASPACE_COUNT") != 0) + dataspaceavail = true; + } + + register_metric (ctr, cname, uname); + return 0; +} + +int +Experiment::process_jcm_load_cmd (char *, Vaddr mid, Vaddr vaddr, + int msize, hrtime_t ts) +{ + if (jmaps == NULL) + return 1; + + JMethod *jfunc = (JMethod*) jmaps->locate_exact_match (mid, ts); + if (jfunc == NULL || jfunc->get_type () != Histable::FUNCTION) + return 1; + + LoadObject *ds = get_dynfunc_lo (JAVA_COMPILED_METHODS); + Module *jmodule = jfunc->module; + Module *dmodule = ds->noname; + if (jmodule) + { + dmodule = dbeSession->createModule (ds, jmodule->get_name ()); + dmodule->lang_code = Sp_lang_java; + dmodule->set_file_name (dbe_strdup (jmodule->file_name)); + } + + JMethod *dfunc = dbeSession->createJMethod (); + dfunc->flags |= FUNC_FLAG_DYNAMIC; + dfunc->size = msize; + dfunc->module = dmodule; + dfunc->usrfunc = jfunc; + dfunc->set_addr (vaddr); + dfunc->set_mid (mid); + dfunc->set_signature (jfunc->get_signature ()); + dfunc->set_name (jfunc->get_mangled_name ()); + ds->functions->append (dfunc); + dmodule->functions->append (dfunc); + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = dfunc; + mrec->base = vaddr; + mrec->size = msize; + mrec->ts = ts; + mrec->foff = 0; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_jcm_unload_cmd (char *, Vaddr /*mid*/, hrtime_t /*ts*/) +{ + if (jmaps == NULL) + return 1; + + // We are ignoring this record because of the flaw in + // JVMPI desing that doesn't distinguish between two or more + // compiled instances of a method when an unload event is + // generated: + // JVMPI_COMPILED_METHOD_LOAD( mid, addr1, ... ) + // JVMPI_COMPILED_METHOD_LOAD( mid, addr2, ... ) + // JVMPI_COMPILED_METHOD_UNLOAD( mid ) -- which one? + // We rely on the ability of the PRBTree algorithms to + // perform mapping appropriately based on timestamps. + return 0; +} + +int +Experiment::process_jthr_end_cmd (char *, uint64_t tid64, Vaddr jthr, + Vaddr jenv, hrtime_t ts) +{ + int lt = 0; + int rt = jthreads_idx->size () - 1; + uint32_t ttid = mapTagValue (PROP_THRID, tid64); + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jthread = jthreads_idx->fetch (md); + if (jthread->tid < ttid) + lt = md + 1; + else if (jthread->tid > ttid) + rt = md - 1; + else + { + for (; jthread; jthread = jthread->next) + { + if (jthread->jenv == jenv) + { + jthread->end = ts; + return 0; + } + } + return 0; + } + } + JThread *jthread = new JThread; + jthread->tid = mapTagValue (PROP_THRID, tid64); + jthread->jthr = jthr; + jthread->jenv = jenv; + jthread->jthr_id = jthreads->size (); + jthread->start = ZERO_TIME; + jthread->end = ts; + jthread->next = NULL; + jthreads->append (jthread); + if (lt == jthreads_idx->size ()) + jthreads_idx->append (jthread); + else + jthreads_idx->insert (lt, jthread); + return 0; +} + +int +Experiment::process_jthr_start_cmd (char *, char *thread_name, char *group_name, + char *parent_name, uint64_t tid64, + Vaddr jthr, Vaddr jenv, hrtime_t ts) +{ + JThread *jthread = new JThread; + jthread->name = thread_name; + jthread->group_name = group_name; + jthread->parent_name = parent_name; + jthread->tid = mapTagValue (PROP_THRID, tid64); + jthread->jthr = jthr; + jthread->jenv = jenv; + jthread->jthr_id = jthreads->size (); + jthread->start = ts; + jthread->end = MAX_TIME; + jthread->next = NULL; + + jthreads->append (jthread); + + int lt = 0; + int rt = jthreads_idx->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jtmp = jthreads_idx->fetch (md); + if (jtmp->tid < jthread->tid) + lt = md + 1; + else if (jtmp->tid > jthread->tid) + rt = md - 1; + else + { + jthread->next = jtmp; + jthreads_idx->store (md, jthread); + return 0; + } + } + if (lt == jthreads_idx->size ()) + jthreads_idx->append (jthread); + else + jthreads_idx->insert (lt, jthread); + return 0; +} + +int +Experiment::process_gc_end_cmd ( + hrtime_t ts) +{ + if (gcevents->size () == 0) + { + GCEvent *gcevent = new GCEvent; + gcevent->start = ZERO_TIME; + gcevent->end = ts; + gcevent->id = gcevents->size () + 1; + gcevents->append (gcevent); + return 0; + } + GCEvent *gcevent = gcevents->fetch (gcevents->size () - 1); + if (gcevent->end == MAX_TIME) + gcevent->end = ts; + else + // Weird: gc_end followed by another gc_end + gcevent->end = ts; // extend the previous event + return 0; +} + +int +Experiment::process_gc_start_cmd ( + hrtime_t ts) +{ + if (gcevents->size () != 0) + { + GCEvent *gcevent = gcevents->fetch (gcevents->size () - 1); + // Weird: gc_start followed by another gc_start + if (gcevent->end == MAX_TIME) + return 0; // ignore nested gc_starts + } + GCEvent *gcevent = new GCEvent; + gcevent->start = ts; + gcevent->end = MAX_TIME; + gcevent->id = gcevents->size () + 1; + gcevents->append (gcevent); + return 0; +} + +int +Experiment::process_sample_cmd (char */*cmd*/, hrtime_t /*log_xml_time*/, + int sample_number, char *label) +{ + // sample 0 is not a sample but the starting point + if (sample_number == 0) + { + first_sample_label = label; + return 0; + } + Sample *prev_sample = samples->size () > 0 ? + samples->fetch (samples->size () - 1) : NULL; + char *start_lable = prev_sample ? + prev_sample->end_label : first_sample_label; + Sample *sample = new Sample (sample_number); + sample->start_label = dbe_strdup (start_lable); + sample->end_label = label; + samples->append (sample); + return 0; +} + +int +Experiment::process_sample_sig_cmd (char *, int sig) +{ + char *str; + Emsg *m; + str = dbe_sprintf (GTXT ("Sample signal %d"), sig); + m = new Emsg (CMSG_COMMENT, str); + free (str); + runlogq->append (m); + return 0; +} + +int +Experiment::process_seg_map_cmd (char */*cmd*/, hrtime_t ts, Vaddr vaddr, + int mapsize, int /*pagesize*/, int64_t offset, + int64_t modeflags, int64_t chk, char *nm) +{ + if (nm == NULL || + strncmp (nm + 1, SP_MAP_UNRESOLVABLE, strlen (SP_MAP_UNRESOLVABLE)) == 0) + return 0; + + LoadObject *lo = loadObjMap->get (nm); + if (lo == NULL) + { + if (chk == 0) + { + char *archName = checkFileInArchive (nm, false); + if (archName) + { + Elf *elf = new Elf (archName); + if (elf->status == Elf::ELF_ERR_NONE) + { + chk = elf->elf_checksum (); + } + free (archName); + delete elf; + } + } + lo = dbeSession->find_lobj_by_name (nm, chk); + if (lo == NULL) + { + // Skip non-text segments + if (modeflags != (PROT_READ | PROT_EXEC)) + return 0; + // A new segment + lo = createLoadObject (nm, chk); + if (strstr (nm, NTXT ("libjvm.so"))) + { + lo->flags |= SEG_FLAG_JVM; + // Make sure <JVM-System> is created + (void) dbeSession->get_jvm_Function (); + } + else if (strstr (nm, NTXT ("libmtsk.so"))) + { + lo->flags |= SEG_FLAG_OMP; + // Make sure all pseudo functions are created + for (int i = 0; i < OMP_LAST_STATE; i++) + (void) dbeSession->get_OMP_Function (i); + } + else if (dbe_strcmp (utargname, get_basename (nm)) == 0) + { + lo->flags |= SEG_FLAG_EXE; + (void) dbeSession->comp_lobjs->get ((char *) COMP_EXE_NAME, lo); + } + lo->checksum = chk; + // This is the default segment type + lo->type = LoadObject::SEG_TEXT; + lo->flags = lo->flags | SEG_FLAG_REORDER; + lo->set_platform (platform, wsize); + } + if (lo->dbeFile->get_location (false) == NULL) + { + char *archName = checkFileInArchive (nm, false); + if (archName) + { + lo->dbeFile->set_location (archName); + lo->dbeFile->inArchive = true; + lo->dbeFile->check_access (archName); // init 'sbuf' + lo->dbeFile->sbuf.st_mtime = 0; // Don't check timestamps + free (archName); + } + else + { + archName = checkFileInArchive (nm, true); + if (archName) + { + lo->set_archname (archName); + lo->need_swap_endian = need_swap_endian; + } + } + if (!dbeSession->archive_mode) + lo->sync_read_stabs (); + } + append (lo); + } + if (lo->size == 0) + lo->size = mapsize; + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = lo; + mrec->base = vaddr; + mrec->size = mapsize; + mrec->ts = ts; + mrec->foff = offset; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_seg_unmap_cmd (char */*cmd*/, hrtime_t ts, Vaddr vaddr) +{ + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::UNLOAD; + mrec->base = vaddr; + mrec->ts = ts; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_Linux_kernel_cmd (hrtime_t ts) +{ + LoadObject *lo = createLoadObject ("LinuxKernel"); + lo->flags |= SEG_FLAG_EXE; + lo->type = LoadObject::SEG_TEXT; + lo->set_platform (platform, wsize); + append (lo); + long long unsigned lo_min = (long long unsigned) (-1); + long long unsigned lo_max = 0; + Module *mod = dbeSession->createModule (lo, "LinuxKernel"); + /* + * XXX need to review mod initialization + * A specific issue is mod->file_name. Options include: + * *) NULL + * This leads to seg faults in, e.g., Timeline view. + * *) "/lib/modules/$(uname -r)/kernel/kernel/ctf/ctf.ko" + * This leads to garbage in the Source view. + * *) "/boot/vmlinuz-$(uname -r)" + * This cannot be parsed for DWARF and is sometimes not found, + * but the Analyzer seems to handle such problems. + * *) "LinuxKernel" + * This is not a proper file name, + * but again Analyzer handles the case of not finding the file or not reading DWARF from it. + */ + mod->set_file_name (dbe_strdup ("LinuxKernel")); + char last_mod_name[256]; + last_mod_name[0] = '\0'; + size_t line_n = 0; + char *line = NULL; + char kallmodsyms_copy[MAXPATHLEN]; + snprintf (kallmodsyms_copy, sizeof (kallmodsyms_copy), "%s/kallmodsyms", + expt_name); + FILE *fd = fopen (kallmodsyms_copy, "r"); + if (fd == NULL) + { + char *s = dbe_sprintf (GTXT ("*** Error: Cannot find kernel module symbols file %s; ignored"), + kallmodsyms_copy); + Emsg *m = new Emsg (CMSG_ERROR, s); + free (s); + errorq->append (m); + lo_min = 0; + } + else + { + while (getline (&line, &line_n, fd) > 0) + { + long long unsigned sym_addr; + long long unsigned sym_size; + char sym_type; + int sym_text; + char sym_name[256]; + char mod_name[256] = "vmlinux]"; /* note trailing ] */ + sscanf (line, "%llx %llx %c %s [%s", &sym_addr, &sym_size, &sym_type, + sym_name, mod_name); + if (line[0] == '\n' || line[0] == 0) + continue; + sym_text = (sym_type == 't' || sym_type == 'T'); + mod_name[strlen (mod_name) - 1] = '\0'; /* chop trailing ] */ + if (strcmp (mod_name, "ctf") == 0) + strcpy (mod_name, "shared_ctf"); + + char *mod_name_ptr; + int skip; +#define strstarts(var, x) (strncmp(var, x, strlen (x)) == 0) + if (strcmp (sym_name, "__per_cpu_start") == 0 + || strcmp (sym_name, "__per_cpu_end") == 0 + || strstarts (sym_name, "__crc_") + || strstarts (sym_name, "__ksymtab_") + || strstarts (sym_name, "__kcrctab_") + || strstarts (sym_name, "__kstrtab_") + || strstarts (sym_name, "__param_") + || strstarts (sym_name, "__syscall_meta__") + || strstarts (sym_name, "__p_syscall_meta__") + || strstarts (sym_name, "__event_") + || strstarts (sym_name, "event_") + || strstarts (sym_name, "ftrace_event_") + || strstarts (sym_name, "types__") + || strstarts (sym_name, "args__") + || strstarts (sym_name, "__tracepoint_") + || strstarts (sym_name, "__tpstrtab_") + || strstarts (sym_name, "__tpstrtab__") + || strstarts (sym_name, "__initcall_") + || strstarts (sym_name, "__setup_") + || strstarts (sym_name, "__pci_fixup_") + || strstarts (sym_name, "__dta_") + || strstarts (sym_name, "__dtrace_probe_") + || (strstr (sym_name, ".") != NULL + && strstr (sym_name, ".clone.") == NULL)) + { + mod_name_ptr = last_mod_name; + skip = 1; + } + else + { + mod_name_ptr = mod_name; + skip = 0; + } +#undef strstarts + + if (sym_text && skip == 0) + { + char fname[128]; + snprintf (fname, sizeof (fname), "%s`%s", mod_name_ptr, sym_name); + Function *func = dbeSession->createFunction (); + func->set_name (fname); + // func->flags |= FUNC_FLAG_???; // XXX + func->size = sym_size; + func->img_offset = sym_addr; + func->module = mod; + lo->functions->append (func); + mod->functions->append (func); + if (lo_min > sym_addr) + lo_min = sym_addr; + if (lo_max < sym_addr + sym_size) + lo_max = sym_addr + sym_size; + } + sprintf (last_mod_name, mod_name_ptr); + } + fclose (fd); + } + free (line); + lo->size = lo_max; + lo->functions->sort (func_cmp); + mod->functions->sort (func_cmp); + + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = lo; + mrec->base = lo_min; + mrec->size = lo_max - lo_min; + mrec->ts = ts; + mrec->foff = lo_min; + mrec_insert (mrec); + return 0; +} diff --git a/gprofng/src/stab.h b/gprofng/src/stab.h new file mode 100644 index 0000000..4610105 --- /dev/null +++ b/gprofng/src/stab.h @@ -0,0 +1,205 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* + * This file gives definitions supplementing <a.out.h> + * for debugging symbol table entries. + * These entries must have one of the N_STAB bits on, + * and are subject to relocation according to the masks in <a.out.h> + * on 4.x (stabs not relocated on SVR4). + */ + +#ifndef _STAB_H +#define _STAB_H + +/* this file also contains fragments of a.out.h relevant to + * support of stab processing within ELF files + * (when a.out.h is not available) + */ +struct stab +{ + unsigned n_strx; /* index into file string table */ + unsigned char n_type; /* type flag (N_TEXT,..) */ + char n_other; /* used by N_SLINE stab */ + short n_desc; /* see stabs documentation */ + unsigned n_value; /* value of symbol (or sdb offset) */ +}; + +/* patchtypes for N_PATCH stab (n_desc field) */ +#define P_BITFIELD 0x1 +#define P_SPILL 0x2 +#define P_SCOPY 0x3 + +/* markers for N_CODETAG stab (n_other field) */ +#define CODETAG_BITFIELD 0x1 /* load/store of a bit field */ +#define CODETAG_SPILL 0x2 /* spill of registers */ +#define CODETAG_SCOPY 0x3 /* structure copy load/store */ +#define CODETAG_FSTART 0x4 /* points to first inst of new frame (0==leaf)*/ +#define CODETAG_END_CTORS 0x5 /* end of calls to super-class constructors */ +/* UNUSED 0x6 DW_ATCF_SUN_branch_target in dwarf, not used in stabs */ +#define CODETAG_STACK_PROBE 0x7 /* marks insns which probe the stack memory */ + +/* + * Simple values for n_type. + */ +#define N_UNDF 0x0 /* undefined */ +#define N_ABS 0x2 /* absolute */ +#define N_TEXT 0x4 /* text */ +#define N_DATA 0x6 /* data */ +#define N_BSS 0x8 /* bss */ +#define N_COMM 0x12 /* common (internal to ld) */ +#define N_FN 0x1f /* file name symbol */ +#define N_EXT 01 /* external bit, or'ed in */ +#define N_TYPE 0x1e /* mask for all the type bits */ + +/* + * maximum length of stab string before using continuation stab. + * (this is just a suggested limit), assembler has no limit. + */ +#define MAX_STAB_STR_LEN 250 + +/* + * for symbolic debuggers: + */ +#define N_GSYM 0x20 /* global symbol: name,,0,type,0 */ +#define N_FNAME 0x22 /* procedure name (f77 kludge): name,,0 */ +#define N_FUN 0x24 /* procedure: name,,0,linenumber,0 */ +#define N_OUTL 0x25 /* outlined func: name,,0,linenumber,0 */ +#define N_STSYM 0x26 /* static symbol: name,,0,type,0 or section relative */ +#define N_TSTSYM 0x27 /* thread static symbol: Ttdata.data */ +#define N_LCSYM 0x28 /* .lcomm symbol: name,,0,type,0 or section relative */ +#define N_TLCSYM 0x29 /* thread local symbol: Ttbss.bss */ +#define N_MAIN 0x2a /* name of main routine : name,,0,0,0 */ +#define N_ROSYM 0x2c /* ro_data: name,,0,type,0 or section relative */ +#define N_FLSYM 0x2e /* fragmented data: name,,0,type,0 */ +#define N_TFLSYM 0x2f /* thread fragmented data: name,,0,type,0 */ +#define N_PC 0x30 /* global pascal symbol: name,,0,subtype,line */ +#define N_CMDLINE 0x34 /* command line info */ +#define N_OBJ 0x38 /* object file path or name */ +#define N_OPT 0x3c /* compiler options */ +#define N_RSYM 0x40 /* register sym: name,,0,type,register */ +#define N_SLINE 0x44 /* src line: 0,,0,linenumber,function relative */ +#define N_XLINE 0x45 /* h.o. src line: 0,,0,linenumber>>16,0 */ +#define N_ILDPAD 0x4c /* now used as ild pad stab value=strtab delta + * was designed for "function start.end" */ +#define N_SSYM 0x60 /* structure elt: name,,0,type,struct_offset */ +#define N_ENDM 0x62 /* last stab emitted for object module */ +#define N_SO 0x64 /* source file name: name,,0,0,0 */ +#define N_MOD 0x66 /* f90 module: name,,0,0,0 */ +#define N_EMOD 0x68 /* end of f90 module: name,,0,0,0 */ +#define N_READ_MOD 0x6a /* use of f90 module: name;locallist,,0,0,0 */ +#define N_ALIAS 0x6c /* alias name: name,,0,0,0 */ +#define N_LSYM 0x80 /* local sym: name,,0,type,offset */ +#define N_BINCL 0x82 /* header file: name,,0,0,0 */ +#define N_SOL 0x84 /* #included file name: name,,0,0,0 */ +#define N_PSYM 0xa0 /* parameter: name,,0,type,offset */ +#define N_EINCL 0xa2 /* end of include file */ +#define N_ENTRY 0xa4 /* alternate entry: name,linenumber,0 */ +#define N_SINCL 0xa6 /* shared include file */ +#define N_LBRAC 0xc0 /* left bracket: 0,,0,nesting level,function relative */ +#define N_EXCL 0xc2 /* excluded include file */ +#define N_USING 0xc4 /* C++ using command */ +#define N_ISYM 0xc6 /* position independent type symbol, internal */ +#define N_ESYM 0xc8 /* position independent type symbol, external */ +#define N_PATCH 0xd0 /* Instruction to be ignored by run-time checking. */ +#define N_CONSTRUCT 0xd2 /* C++ constructor call. */ +#define N_DESTRUCT 0xd4 /* C++ destructor call. */ +#define N_CODETAG 0xd8 /* Generic code tag */ +#define N_FUN_CHILD 0xd9 /* Identifies a child function */ +#define N_RBRAC 0xe0 /* right bracket: 0,,0,nesting level,function relative */ +#define N_BCOMM 0xe2 /* begin common: name,, */ +#define N_TCOMM 0xe3 /* begin task common: name,, */ +#define N_ECOMM 0xe4 /* end task_common/common: name,, */ +#define N_XCOMM 0xe6 /* excluded common block */ +#define N_ECOML 0xe8 /* end common (local name): ,,address */ +#define N_WITH 0xea /* pascal with statement: type,,0,0,offset */ +#define N_LENG 0xfe /* second stab entry with length information */ + +/* + * for analyzer (cache profile feedback support) + */ +#define N_CPROF 0xf0 /* annotation for cache profile feedback */ + +/* + * n_descr values used in N_CPROF stabs. The n_descr field of + * an N_CPROF stab identifies the type of table whose location + * is defined by the N_CPROF stab. + */ +typedef enum n_cprof_instr_type_t +{ + N_CPROF_INSTR_TYPE_LOAD = 0, /* profiled load ops */ + N_CPROF_INSTR_TYPE_STORE, /* profiled store ops */ + N_CPROF_INSTR_TYPE_PREFETCH, /* profiled prefetch ops */ + N_CPROF_INSTR_TYPE_BRTARGET, /* branch target locations */ + N_CPROF_INSTR_TYPE_NTYPES /* number of types */ +} n_cprof_instr_type_t; + +/* + * for code browser only + */ +#define N_BROWS 0x48 /* path to associated .cb file */ + +/* + * for functions -- n_other bits for N_FUN stab + */ +#define N_FUN_PURE (1 << 0) +#define N_FUN_ELEMENTAL (1 << 1) +#define N_FUN_RECURSIVE (1 << 2) +#define N_FUN_AMD64_PARMDUMP (1 << 3) + +/* + * for variables -- n_other bits for N_LSYM, N_GSYM, N_LCSYM, N_STSYM, ... + */ +#define N_SYM_OMP_TLS (1 << 3) + +/* + * Optional language designations for N_SO (n_desc field) + */ +#define N_SO_AS 1 /* Assembler */ +#define N_SO_C 2 /* C */ +#define N_SO_ANSI_C 3 /* ANSI C */ +#define N_SO_CC 4 /* C++ */ +#define N_SO_FORTRAN 5 /* Fortran 77 */ +#define N_SO_FORTRAN77 5 /* Fortran 77 */ +#define N_SO_PASCAL 6 /* Pascal */ +#define N_SO_FORTRAN90 7 /* Fortran 90 */ +#define N_SO_JAVA 8 /* Java */ +#define N_SO_C99 9 /* C99 */ + +/* + * Floating point type values (encoded in "R" type specification string) + */ +#define NF_NONE 0 /* Undefined type */ +#define NF_SINGLE 1 /* Float IEEE 32 bit floating point */ +#define NF_DOUBLE 2 /* Double IEEE 64 bit floating point */ +#define NF_COMPLEX 3 /* Complex (2 32bit floats) */ +#define NF_COMPLEX16 4 /* Complex (2 64bit doubles) */ +#define NF_COMPLEX32 5 /* Complex (2 128bit long doubles) */ +#define NF_LDOUBLE 6 /* Long double 128 bit floating point */ +#define NF_INTERARITH 7 /* Interval (2 32bit floats) */ +#define NF_DINTERARITH 8 /* Interval (2 64bit doubles) */ +#define NF_QINTERARITH 9 /* Interval (2 128bit long doubles) */ +#define NF_IMAGINARY 10 /* Imaginary (1 32bit floats) */ +#define NF_DIMAGINARY 11 /* Imaginary (1 64bit doubles) */ +#define NF_QIMAGINARY 12 /* Imaginary (1 128bit long doubles) */ + +#endif + + diff --git a/gprofng/src/util.cc b/gprofng/src/util.cc new file mode 100644 index 0000000..b93deaf --- /dev/null +++ b/gprofng/src/util.cc @@ -0,0 +1,1582 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <sys/param.h> +#include <stdarg.h> +#include <unistd.h> +#include <dirent.h> // readdir() +#include <sys/param.h> // MAXPATHLEN +#include <pthread.h> // mutex +#include <libgen.h> // dirname +#include <sys/types.h> // open +#include <sys/stat.h> // open +#include <errno.h> // errno +#include <fcntl.h> // open + +#include "util.h" +#include "dbe_structs.h" +#include "StringBuilder.h" +#include "StringMap.h" // For directory names +#include "Application.h" // Only for get_prog_name +#include "vec.h" + +void +tsadd (timestruc_t *result, timestruc_t *time) +{ + // This routine will add "time" to "result". + result->tv_sec += time->tv_sec; + result->tv_nsec += time->tv_nsec; + if (result->tv_nsec >= NANOSEC) + { + result->tv_nsec -= NANOSEC; + result->tv_sec++; + } +} + +void +tssub (timestruc_t *result, timestruc_t *time1, timestruc_t *time2) +{ + // This routine will store "time1" - "time2" in "result". + + if (time1->tv_nsec >= time2->tv_nsec) + { + result->tv_nsec = time1->tv_nsec - time2->tv_nsec; + if (time1->tv_sec >= time2->tv_sec) + result->tv_sec = time1->tv_sec - time2->tv_sec; + else + { + result->tv_sec = -1; + result->tv_nsec = 0; + } + } + else + { + result->tv_nsec = time1->tv_nsec + NANOSEC - time2->tv_nsec; + if (time1->tv_sec - 1 >= time2->tv_sec) + result->tv_sec = time1->tv_sec - 1 - time2->tv_sec; + else + { + result->tv_sec = -1; + result->tv_nsec = 0; + } + } +} + +int +tscmp (timestruc_t *time1, timestruc_t *time2) +{ + // This routine will return 1 if "time1" is greater than "time2" + // and 0 if "time1" is equal to "time2" and -1 otherwise. + if (time1->tv_sec == time2->tv_sec) + return time1->tv_nsec > time2->tv_nsec ? 1 : + time1->tv_nsec == time2->tv_nsec ? 0 : -1; + else + return time1->tv_sec > time2->tv_sec ? 1 : -1; +} + +void +int_max (int *maximum, int count) +{ + if (count > *maximum) + *maximum = count; +} + +double +TValue::to_double () +{ + switch (tag) + { + case VT_DOUBLE: + return (double) d; + case VT_INT: + return (double) i; + case VT_ULLONG: + return (double) ull; + case VT_LLONG: + case VT_ADDRESS: + return (double) ll; + case VT_FLOAT: + return (double) f; + case VT_SHORT: + return (double) s; + default: + return 0.0; + } +} + +int +TValue::to_int () +{ + switch (tag) + { + case VT_DOUBLE: + return (int) d; + case VT_INT: + return (int) i; + case VT_ULLONG: + return (int) ull; + case VT_LLONG: + case VT_ADDRESS: + return (int) ll; + case VT_FLOAT: + return (int) f; + case VT_SHORT: + return (int) s; + default: + return 0; + } +} + +size_t +TValue::get_len () +{ + char buf[256]; + return strlen (to_str (buf, sizeof (buf))); +} + +char * +TValue::to_str (char *str, size_t strsz) +{ + switch (tag) + { + case VT_DOUBLE: + if (d == 0.) + { + if (sign) + snprintf (str, strsz, NTXT ("+0. ")); + else + snprintf (str, strsz, NTXT ("0. ")); + } + else if (sign) + snprintf (str, strsz, NTXT ("%+.3lf"), d); + else + snprintf (str, strsz, NTXT ("%.3lf"), d); + break; + case VT_INT: + snprintf (str, strsz, NTXT ("%u"), i); + break; + case VT_LLONG: + if (sign) + snprintf (str, strsz, NTXT ("%+lld"), ll); + else + snprintf (str, strsz, NTXT ("%lld"), ll); + break; + case VT_ULLONG: + snprintf (str, strsz, NTXT ("%llu"), ll); + break; + case VT_ADDRESS: + snprintf (str, strsz, NTXT ("%u:0x%08x"), ADDRESS_SEG (ll), ADDRESS_OFF (ll)); + break; + case VT_FLOAT: + snprintf (str, strsz, NTXT ("%.3f"), f); + break; + case VT_SHORT: + snprintf (str, strsz, NTXT ("%hu"), s); + break; + case VT_LABEL: + return l; // 'str' is not used !!! + default: + *str = '\0'; + break; + } + + return str; +} + +void +TValue::make_delta (TValue *v1, TValue *v2) +{ + assert (v1->tag == v2->tag); + tag = v1->tag; + sign = true; + switch (v1->tag) + { + case VT_INT: + i = v1->i - v2->i; + break; + case VT_LLONG: + ll = v1->ll - v2->ll; + break; + case VT_ULLONG: + case VT_ADDRESS: + tag = VT_LLONG; + ll = (long long) (v1->ull - v2->ull); + break; + case VT_FLOAT: + f = v1->f - v2->f; + break; + case VT_DOUBLE: + d = v1->d - v2->d; + break; + default: + assert (0); + break; + } +} + +void +TValue::make_ratio (TValue *v1, TValue *v2) +{ + assert (v1->tag == v2->tag); + double x1 = v1->to_double (); + double x2 = v2->to_double (); + sign = false; + if (x1 == 0.) + { + // if the numerator is 0, the ratio is 1. or 0. only + d = (x2 == 0.) ? 1. : 0.; + tag = VT_DOUBLE; + } + else + { + // EUGENE replace 99.999 with a variable that is known by both DBE and GUI + if (x1 > 99.999 * x2) + { + l = dbe_strdup (">99.999"); + tag = VT_LABEL; + } + else if (x1 < -99.999 * x2) + { + l = dbe_strdup ("<-99.999"); + tag = VT_LABEL; + } + else + { + d = x1 / x2; + tag = VT_DOUBLE; + } + } +} + +int +TValue::compare (TValue *v) +{ + if (tag != v->tag) + { // Only for comparison (Ratio) + if (tag == VT_LABEL) + { + if (v->tag == VT_LABEL) + return strcoll (l, v->l); + return 1; + } + if (v->tag == VT_LABEL) + return -1; + return ll < v->ll ? -1 : (ll == v->ll ? 0 : 1); + } + switch (tag) + { + case VT_SHORT: + return s < v->s ? -1 : (s == v->s ? 0 : 1); + case VT_INT: + return i < v->i ? -1 : (i == v->i ? 0 : 1); + case VT_FLOAT: + return f < v->f ? -1 : (f == v->f ? 0 : 1); + case VT_DOUBLE: + return d < v->d ? -1 : (d == v->d ? 0 : 1); + case VT_LABEL: + return strcoll (l, v->l); + case VT_LLONG: + case VT_ULLONG: + case VT_ADDRESS: + case VT_HRTIME: + default: + return (ll < v->ll) ? -1 : ((ll == v->ll) ? 0 : 1); + } +} + +char * +strstr_r (char *s1, const char *s2) +{ + char *str = NULL; + for (char *s = s1; s;) + { + s = strstr (s, s2); + if (s) + { + str = s; + s++; + } + } + return str; +} + +// reversal order of strpbrk + +char * +strrpbrk (const char *string, const char *brkset) +{ + const char *p; + const char *s; + for (s = string + strlen (string) - 1; s >= string; s--) + { + for (p = brkset; *p != '\0' && *p != *s; ++p) + ; + if (*p != '\0') + return ((char *) s); + } + return NULL; +} + +char * +read_line (FILE *fptr) +{ + // get an input line, no size limit + int line_sz = 128; // starting size + char *line = (char *) malloc (line_sz); + + // read as much of the line as will fit in memory + line[0] = 0; + int len = 0; + for (;;) + { + while (fgets (line + len, line_sz - len, fptr) != NULL) + { + len = (int) strlen (line); + if (len == 0 || line[len - 1] == '\n') + break; + // increase the buffer + char *lineNew = (char *) malloc (2 * line_sz); + strncpy (lineNew, line, line_sz); + lineNew[line_sz] = '\0'; + free (line); + line = lineNew; + line_sz *= 2; + if (line == NULL) + { + fprintf (stderr, GTXT (" Line too long -- out of memory; exiting\n")); + exit (1); + } + } + if (len == 0) + { + free (line); + return NULL; + } + // see if there's a continuation line + if ((len >= 2) && (line[len - 1] == '\n') && (line[len - 2] == '\\')) + { + // remove the trailing \ and the \n, and keep going + line[len - 2] = 0; + len -= 2; + } + else + break; + } + return line; // expecting the caller to free it +} + +Vector<char *> * +split_str (char *str, char delimiter) +{ + Vector<char *> *v = new Vector<char *>; + for (char *s = str; s;) + { + if (*s == '"') + { + char *next_s = NULL; + char *tok = parse_qstring (s, &next_s); + if (tok && *tok != '\0') + v->append (tok); + if (*next_s) + s = next_s + 1; + else + s = NULL; + } + else + { + char *next_s = strchr (s, delimiter); + if (next_s) + { + if (next_s != s) + v->append (dbe_strndup (s, next_s - s)); + s = next_s + 1; + } + else + { + if (*s != '\0') + v->append (dbe_strdup (s)); + s = NULL; + } + } + } + return v; +} + +// get quoted string +char * +parse_qstring (char *in_str, char **endptr) +{ + int i; + char c, c2; + char term; + char csnum[2 * MAXPATHLEN]; + + // Skip any leading blanks or tabs + while (*in_str == '\t' || *in_str == ' ') + in_str++; + + int gtxt = 0; + if (*in_str == 'G' && *(in_str + 1) == 'T' && *(in_str + 2) == 'X' + && *(in_str + 3) == 'T' && *(in_str + 4) == '(') + { + gtxt = 1; + in_str += 5; + } + // non-quoted string + if (*in_str == '"') + term = '"'; + else if (*in_str == '\'') + term = '\''; + else + return strtok_r (in_str, NTXT (" "), endptr); + + StringBuilder sb; + while ((c = *(++in_str)) != '\0') + { + if (c == term) // the closing quote + break; + if (c == '\\') + { // handle any escaped characters + c2 = *(++in_str); + switch (c2) + { + case '\"': + sb.append ('\"'); + break; + case '\'': + sb.append ('\''); + break; + case '\\': + sb.append ('\\'); + break; + case 't': + sb.append ('\t'); + break; + case 'r': + sb.append ('\r'); + break; + case 'b': + sb.append ('\b'); + break; + case 'f': + sb.append ('\f'); + break; + case 'n': + sb.append ('\n'); + break; + default: + if ((c2 >= '0') && (c2 <= '9')) + { + for (i = 0; i < MAXPATHLEN; i++) + { + if (((c2 < '0') || (c2 > '9')) && (c2 != 'x') && + ((c2 < 'a') || (c2 > 'f')) && + ((c2 < 'A') || (c2 > 'F'))) + { + csnum[i] = '\0'; + --in_str; + break; + } + else + { + csnum[i] = c2; + c2 = *(++in_str); + } + } + sb.append ((char) strtoul (csnum, endptr, 0)); + } + else + sb.append (c2); + break; + } + } + else + sb.append (c); + } + if (c == term && gtxt && *in_str == ')') + in_str++; + if (*in_str == '\0') + *endptr = in_str; + else + *endptr = in_str + 1; + return sb.toString (); +} + +// parse a file name of the form name`name2` +// returns name +// stores the pointer to named in fcontext +// returns NULL if the string is not properly formatted +char * +parse_fname (char *in_str, char **fcontext) +{ + *fcontext = NULL; + int ch = '`'; + if (in_str == NULL) + return NULL; + char *copy = strdup (in_str); + char *p = strchr (copy, ch); + if (p != NULL) + { + // yes, there's an embedded file name + *p = '\0'; + p++; + // now find the terminating single quote + char *p1 = strchr (p, ch); + if (p1 == NULL) + { + // if we don't have the closing `, the format is incorrect + free (copy); + return NULL; + } + //remove the closing quote + *p1 = '\0'; + // see if there's anything following it + if (*(p1 + 1) != 0) + { + // error in format + free (copy); + return NULL; + } + free (*fcontext); + *fcontext = strdup (p); + } + return copy; +} + +int +get_paren (const char *name) +{ + char buf[8192]; + char *ptr; + int temp_level1, temp_level2; + + temp_level1 = temp_level2 = 0; + snprintf (buf, sizeof (buf), NTXT ("%s"), name); + while ((ptr = strrpbrk (buf, "><)(")) != NULL) + { + if (*ptr == '>') + temp_level1++; + else if (*ptr == '<') + temp_level1--; + else if (*ptr == ')') + temp_level2++; + else + { + temp_level2--; + if (temp_level1 <= 0 && temp_level2 <= 0) + return (int) (ptr - buf); + } + *ptr = '\0'; + } + return -1; +} + +// CRC-64 based on x^64 + x^11 + x^2 + x + 1 polynomial. +// This algorithm doesn't perform well but is short and +// readable. We currently use it for a small amount of +// short strings. Should this change, another algorithm +// with better performance is to be used instead. +static uint64_t masks[256] = { + /* 0 */ 0x000000, 0x000807, 0x00100e, 0x001809, 0x00201c, 0x00281b, + /* 6 */ 0x003012, 0x003815, 0x004038, 0x00483f, 0x005036, 0x005831, + /* 12 */ 0x006024, 0x006823, 0x00702a, 0x00782d, 0x008070, 0x008877, + /* 18 */ 0x00907e, 0x009879, 0x00a06c, 0x00a86b, 0x00b062, 0x00b865, + /* 24 */ 0x00c048, 0x00c84f, 0x00d046, 0x00d841, 0x00e054, 0x00e853, + /* 30 */ 0x00f05a, 0x00f85d, 0x0100e0, 0x0108e7, 0x0110ee, 0x0118e9, + /* 36 */ 0x0120fc, 0x0128fb, 0x0130f2, 0x0138f5, 0x0140d8, 0x0148df, + /* 42 */ 0x0150d6, 0x0158d1, 0x0160c4, 0x0168c3, 0x0170ca, 0x0178cd, + /* 48 */ 0x018090, 0x018897, 0x01909e, 0x019899, 0x01a08c, 0x01a88b, + /* 54 */ 0x01b082, 0x01b885, 0x01c0a8, 0x01c8af, 0x01d0a6, 0x01d8a1, + /* 60 */ 0x01e0b4, 0x01e8b3, 0x01f0ba, 0x01f8bd, 0x0201c0, 0x0209c7, + /* 66 */ 0x0211ce, 0x0219c9, 0x0221dc, 0x0229db, 0x0231d2, 0x0239d5, + /* 72 */ 0x0241f8, 0x0249ff, 0x0251f6, 0x0259f1, 0x0261e4, 0x0269e3, + /* 78 */ 0x0271ea, 0x0279ed, 0x0281b0, 0x0289b7, 0x0291be, 0x0299b9, + /* 84 */ 0x02a1ac, 0x02a9ab, 0x02b1a2, 0x02b9a5, 0x02c188, 0x02c98f, + /* 90 */ 0x02d186, 0x02d981, 0x02e194, 0x02e993, 0x02f19a, 0x02f99d, + /* 96 */ 0x030120, 0x030927, 0x03112e, 0x031929, 0x03213c, 0x03293b, + /* 102 */ 0x033132, 0x033935, 0x034118, 0x03491f, 0x035116, 0x035911, + /* 108 */ 0x036104, 0x036903, 0x03710a, 0x03790d, 0x038150, 0x038957, + /* 114 */ 0x03915e, 0x039959, 0x03a14c, 0x03a94b, 0x03b142, 0x03b945, + /* 120 */ 0x03c168, 0x03c96f, 0x03d166, 0x03d961, 0x03e174, 0x03e973, + /* 126 */ 0x03f17a, 0x03f97d, 0x040380, 0x040b87, 0x04138e, 0x041b89, + /* 132 */ 0x04239c, 0x042b9b, 0x043392, 0x043b95, 0x0443b8, 0x044bbf, + /* 138 */ 0x0453b6, 0x045bb1, 0x0463a4, 0x046ba3, 0x0473aa, 0x047bad, + /* 144 */ 0x0483f0, 0x048bf7, 0x0493fe, 0x049bf9, 0x04a3ec, 0x04abeb, + /* 150 */ 0x04b3e2, 0x04bbe5, 0x04c3c8, 0x04cbcf, 0x04d3c6, 0x04dbc1, + /* 156 */ 0x04e3d4, 0x04ebd3, 0x04f3da, 0x04fbdd, 0x050360, 0x050b67, + /* 162 */ 0x05136e, 0x051b69, 0x05237c, 0x052b7b, 0x053372, 0x053b75, + /* 168 */ 0x054358, 0x054b5f, 0x055356, 0x055b51, 0x056344, 0x056b43, + /* 174 */ 0x05734a, 0x057b4d, 0x058310, 0x058b17, 0x05931e, 0x059b19, + /* 180 */ 0x05a30c, 0x05ab0b, 0x05b302, 0x05bb05, 0x05c328, 0x05cb2f, + /* 186 */ 0x05d326, 0x05db21, 0x05e334, 0x05eb33, 0x05f33a, 0x05fb3d, + /* 192 */ 0x060240, 0x060a47, 0x06124e, 0x061a49, 0x06225c, 0x062a5b, + /* 198 */ 0x063252, 0x063a55, 0x064278, 0x064a7f, 0x065276, 0x065a71, + /* 204 */ 0x066264, 0x066a63, 0x06726a, 0x067a6d, 0x068230, 0x068a37, + /* 210 */ 0x06923e, 0x069a39, 0x06a22c, 0x06aa2b, 0x06b222, 0x06ba25, + /* 216 */ 0x06c208, 0x06ca0f, 0x06d206, 0x06da01, 0x06e214, 0x06ea13, + /* 222 */ 0x06f21a, 0x06fa1d, 0x0702a0, 0x070aa7, 0x0712ae, 0x071aa9, + /* 228 */ 0x0722bc, 0x072abb, 0x0732b2, 0x073ab5, 0x074298, 0x074a9f, + /* 234 */ 0x075296, 0x075a91, 0x076284, 0x076a83, 0x07728a, 0x077a8d, + /* 240 */ 0x0782d0, 0x078ad7, 0x0792de, 0x079ad9, 0x07a2cc, 0x07aacb, + /* 246 */ 0x07b2c2, 0x07bac5, 0x07c2e8, 0x07caef, 0x07d2e6, 0x07dae1, + /* 252 */ 0x07e2f4, 0x07eaf3, 0x07f2fa, 0x07fafd +}; + +uint64_t +crc64 (const char *str, size_t len) +{ + uint64_t res = 0LL; + for (size_t i = 0; i < len; i++) + { + unsigned char b = (unsigned char) ((res >> 56) ^ *str++); + res = res << 8; + res ^= masks [b]; + } + return res; +} + +/** + * Canonize path inside the string provided by the argument + * @param path + * @return path + */ +char * +canonical_path (char *path) +{ + char *s1, *s2; + if (!path) + return path; + s1 = path; + s2 = path; + while (*s1) + { + if (*s1 == '.' && s1[1] == '/') + { // remove ./// + for (s1++; *s1; s1++) + if (*s1 != '/') + break; + } + else if (*s1 == '/') + { // replace /// with / + *(s2++) = *s1; + for (s1++; *s1; s1++) + if (*s1 != '/') + break; + } + else + { + while (*s1) + { // copy file or directory name + if (*s1 == '/') + break; + *(s2++) = *(s1++); + } + } + } + *s2 = 0; + if (s2 != path && (s2 - 1) != path && s2[-1] == '/') // remove last / + *(s2 - 1) = 0; + return path; +} + +char * +get_relative_path (char *name) +{ + if (*name == '/' && theApplication) + { + char *cwd = theApplication->get_cur_dir (); + if (cwd) + { + size_t len = strlen (cwd); + if (len > 0 && len < strlen (name) && name[len] == '/' + && strncmp (cwd, name, len) == 0) + { + for (name += len + 1; *name == '/'; name++) + ; + return name; + } + } + } + return name; +} + +/** + * Generate a relative link name from path_from to path_to + * Example: + * path_from=a/b/c/d + * path_to=a/b/e/f/g + * lname=../../e/f/g + * @param path_to + * @param path_from + * @return lname - relative link + */ +char * +get_relative_link (const char *path_from, const char *path_to) +{ + if (!path_to) + path_to = "."; + if (!path_from) + path_from = "."; + char *s1 = dbe_strdup (path_to); + s1 = canonical_path (s1); + char *s2 = dbe_strdup (path_from); + s2 = canonical_path (s2); + long l = dbe_sstrlen (s1); + // try to find common directories + int common_slashes = 0; + int last_common_slash = -1; + for (int i = 0; i < l; i++) + { + if (s1[i] != s2[i]) break; + if (s1[i] == 0) break; + if (s1[i] == '/') + { + common_slashes++; + last_common_slash = i; + } + } + // find slashes in remaining path_to + int slashes = 0; + for (int i = last_common_slash + 1; i < l; i++) + { + if (s1[i] == '/') + { + // Exclude "/./" case + if (i > last_common_slash + 2) + { + if (s1[i - 1] == '.' && s1[i - 2] == '/') + continue; + } + else if (i > 0 && s1[i - 1] == '.') + continue; + slashes++; + } + } + // generate relative path + StringBuilder sb; + for (int i = 0; i < slashes; i++) + sb.append ("../"); + sb.append (s2 + last_common_slash + 1); + char *lname = sb.toString (); + free (s1); + free (s2); + return lname; +} + +char * +get_prog_name (int basename) +{ + char *nm = NULL; + if (theApplication) + { + nm = theApplication->get_name (); + if (nm && basename) + nm = get_basename (nm); + } + return nm; +} + +char * +dbe_strndup (const char *str, size_t len) +{ + if (str == NULL) + return NULL; + char *s = (char *) malloc (len + 1); + strncpy (s, str, len); + s[len] = '\0'; + return s; +} + +char * +dbe_sprintf (const char *fmt, ...) +{ + char buffer[256]; + int buf_size; + va_list vp; + + va_start (vp, fmt); + buf_size = vsnprintf (buffer, sizeof (buffer), fmt, vp) + 1; + va_end (vp); + if (buf_size < (int) sizeof (buffer)) + { + if (buf_size <= 1) + buffer[0] = 0; + return strdup (buffer); + } + + va_start (vp, fmt); + char *buf = (char *) malloc (buf_size); + vsnprintf (buf, buf_size, fmt, vp); + va_end (vp); + return buf; +} + +ssize_t +dbe_write (int f, const char *fmt, ...) +{ + char buffer[256]; + int buf_size; + va_list vp; + + va_start (vp, fmt); + buf_size = vsnprintf (buffer, sizeof (buffer), fmt, vp) + 1; + va_end (vp); + if (buf_size < (int) sizeof (buffer)) + { + if (buf_size <= 1) + buffer[0] = 0; + return write (f, buffer, strlen (buffer)); + } + + va_start (vp, fmt); + char *buf = (char *) malloc (buf_size); + vsnprintf (buf, buf_size, fmt, vp); + va_end (vp); + ssize_t val = write (f, buf, strlen (buf)); + free (buf); + return val; +} + +/* Worker Threads to avoid hanging on file servers */ + +/* + * Thread states + */ +enum +{ + THREAD_START, + THREAD_STARTED, + THREAD_CANCEL, + THREAD_CANCELED, + THREAD_CREATE, + THREAD_NOT_CREATED, + THREAD_FINISHED +}; + +/* + * Communication structure + */ +struct worker_thread_info +{ + pthread_t thread_id; /* ID returned by pthread_create() */ + int thread_num; /* Application-defined thread # */ + volatile int control; /* Thread state */ + volatile int result; /* Return status */ + struct stat64 statbuf; /* File info from stat64() */ + const char *path; /* File */ +}; + +static pthread_mutex_t worker_thread_lock = PTHREAD_MUTEX_INITIALIZER; +static int worker_thread_number = 0; +/** + * Call stat64() on current worker thread + * Check if control is not THREAD_CANCEL + * If control is THREAD_CANCEL return (exit thread) + * @param *wt_info + */ +static void * +dbe_stat_on_thread (void *arg) +{ + struct worker_thread_info *wt_info = (struct worker_thread_info *) arg; + pthread_mutex_lock (&worker_thread_lock); + { + if (wt_info->control != THREAD_START) + { + // Already too late + pthread_mutex_unlock (&worker_thread_lock); + return 0; + } + wt_info->control = THREAD_STARTED; + } + pthread_mutex_unlock (&worker_thread_lock); + const char * path = wt_info->path; + int st = stat64 (path, &(wt_info->statbuf)); + pthread_mutex_lock (&worker_thread_lock); + { + if (wt_info->control == THREAD_CANCEL) + { + // Too late. + pthread_mutex_unlock (&worker_thread_lock); + free (wt_info); + return 0; + } + wt_info->result = st; + wt_info->control = THREAD_FINISHED; + } + pthread_mutex_unlock (&worker_thread_lock); + return 0; +} + +/** + * Create a worker thread to call specified function + * Wait for its result, but not longer than 5 seconds + * If the timeout happens, tell the thread to cancel + * @param path + * @param wt_info + * @return thread state + */ +static int +dbe_dispatch_on_thread (const char *path, struct worker_thread_info *wt_info) +{ + wt_info->result = 0; + wt_info->control = THREAD_START; + pthread_attr_t attr; + /* Initialize thread creation attributes */ + int res = pthread_attr_init (&attr); + if (res != 0) + { + wt_info->control = THREAD_NOT_CREATED; + return THREAD_NOT_CREATED; + } + wt_info->thread_id = 0; + wt_info->path = path; + // Lock + pthread_mutex_lock (&worker_thread_lock); + worker_thread_number++; + wt_info->thread_num = worker_thread_number; + // Unlock + pthread_mutex_unlock (&worker_thread_lock); + // Create thread + res = pthread_create (&wt_info->thread_id, &attr, &dbe_stat_on_thread, wt_info); + if (res != 0) + { + wt_info->control = THREAD_NOT_CREATED; + pthread_attr_destroy (&attr); + return THREAD_NOT_CREATED; + } + // Wait for the thread to finish + res = 0; + useconds_t maxusec = 5000000; // 5 seconds + useconds_t deltausec = 1000; // 1 millisecond + int max = maxusec / deltausec; + for (int i = 0; i < max; i++) + { + if (THREAD_FINISHED == wt_info->control) + break; // We are done + usleep (deltausec); + } + // Lock + pthread_mutex_lock (&worker_thread_lock); + if (THREAD_FINISHED != wt_info->control) + { + // Cancel thread + wt_info->control = THREAD_CANCEL; // Cannot use wt_info after that! + res = THREAD_CANCEL; + } + // Unlock + pthread_mutex_unlock (&worker_thread_lock); + // Destroy the thread attributes object, since it is no longer needed + pthread_attr_destroy (&attr); + // Report that thread was canceled + if (THREAD_CANCEL == res) + return res; /* Cannot free memory allocated by thread */ + // Free all thread resources + void *resources = 0; + res = pthread_join (wt_info->thread_id, &resources); + free (resources); /* Free memory allocated by thread */ + return THREAD_FINISHED; +} + +static pthread_mutex_t dirnames_lock = PTHREAD_MUTEX_INITIALIZER; +static Map<const char*, int> *dirnamesMap = NULL; + +#define DIR_STATUS_EXISTS 0 +#define DIR_STATUS_UNKNOWN 2 + +/** + * Check if this directory name is known + * Return: + * @param path + * 0 - known, exists + * 1 - known, does not exist + * 2 - not known + */ +static int +check_dirname (const char *path) +{ + pthread_mutex_lock (&dirnames_lock); + if (NULL == dirnamesMap) + dirnamesMap = new StringMap<int>(128, 128); + pthread_mutex_unlock (&dirnames_lock); + int res = DIR_STATUS_UNKNOWN; + if (path && *path) + { + char *fn = dbe_strdup (path); + char *dn = dirname (fn); + if (dn && *dn) + res = dirnamesMap->get (dn); + free (fn); + } + return res; +} + +/** + * Save directory name and its status + * @param path + * @param status + * @return + */ +static void +extract_and_save_dirname (const char *path, int status) +{ + pthread_mutex_lock (&dirnames_lock); + if (NULL == dirnamesMap) + dirnamesMap = new StringMap<int>(128, 128); + pthread_mutex_unlock (&dirnames_lock); + char *fn = dbe_strdup (path); + if (fn && *fn != 0) + { + char *dn = dirname (fn); + if (dn && (*dn != 0)) + { + int st = 0; // exists + if (0 != status) + st = 1; // does not exist + dirnamesMap->put (dn, st); + } + } + free (fn); +} + +// get status for specified file +static int +dbe_stat_internal (const char *path, struct stat64 *sbuf, bool file_only) +{ + struct stat64 statbuf; + int dir_status = check_dirname (path); + if (dir_status == DIR_STATUS_UNKNOWN) + { + // Try to use a worker thread + if (theApplication->get_number_of_worker_threads () > 0) + { + struct worker_thread_info *wt_info; + wt_info = (worker_thread_info *) calloc (1, sizeof (worker_thread_info)); + if (wt_info != NULL) + { + int res = dbe_dispatch_on_thread (path, wt_info); + if (THREAD_FINISHED == res) + { + int st = wt_info->result; + extract_and_save_dirname (path, st); + if (st == 0 && file_only) + if (S_ISREG ((wt_info->statbuf).st_mode) == 0) + st = -1; // It is not a regular file + if (sbuf != NULL) + *sbuf = wt_info->statbuf; + free (wt_info); + return st; + } + else + { + if (THREAD_CANCEL == res) + { + // Worker thread hung. Cannot free wt_info. + // Allocated memory will be freed by worker thread. + // save directory + extract_and_save_dirname (path, 1); + return 1; // stat64 failed + } + else // THREAD_NOT_CREATED - continue on current thread + free (wt_info); + } + } + } + } + else if (dir_status != DIR_STATUS_EXISTS) + return -1; // does not exist + if (sbuf == NULL) + sbuf = &statbuf; + int st = stat64 (path, sbuf); + Dprintf (DEBUG_DBE_FILE, NTXT ("dbe_stat %d '%s'\n"), st, path); + if (st == -1) + return -1; + else if (file_only && S_ISREG (sbuf->st_mode) == 0) + return -1; // It is not ordinary file + return st; +} + +// get status for the regular file + +int +dbe_stat_file (const char *path, struct stat64 *sbuf) +{ + int res = dbe_stat_internal (path, sbuf, true); + return res; +} + +// get status for specified file + +int +dbe_stat (const char *path, struct stat64 *sbuf) +{ + int res = dbe_stat_internal (path, sbuf, false); + return res; +} + +/** + * Reads directory and prepares list of files according to the specified format + * Supported formats: + * "/bin/ls -a" - see 'man ls' for details + * "/bin/ls -aF" - see 'man ls' for details + * @param path + * @param format + * @return char * files + */ +char * +dbe_read_dir (const char *path, const char *format) +{ + StringBuilder sb; + DIR *dir = opendir (path); + if (dir == NULL) + return sb.toString (); + int format_aF = 0; + if (!strcmp (format, NTXT ("/bin/ls -aF"))) + format_aF = 1; + struct dirent *entry = NULL; + if (format != NULL) + { + while ((entry = readdir (dir)) != NULL) + { + sb.append (entry->d_name); + if (format_aF) + { + const char *attr = NTXT ("@"); // Link + struct stat64 sbuf; + sbuf.st_mode = 0; + char filename[MAXPATHLEN + 1]; + snprintf (filename, sizeof (filename), NTXT ("%s/%s"), path, entry->d_name); + dbe_stat (filename, &sbuf); + if (S_IREAD & sbuf.st_mode) + { // Readable + if (S_ISDIR (sbuf.st_mode) != 0) // Directory + attr = NTXT ("/"); + else if (S_ISREG (sbuf.st_mode) != 0) // Regular file + attr = NTXT (""); + } + sb.append (attr); + } + sb.append (NTXT ("\n")); + } + } + closedir (dir); + return sb.toString (); +} + +/** + * Gets list of processes according to the specified format + * Supported formats: + * "/bin/ps -ef" - see 'man ps' for details + * @param format + * @return char * processes + */ +char * +dbe_get_processes (const char *format) +{ + StringBuilder sb; + if (!strcmp (format, NTXT ("/bin/ps -ef"))) + { + char buf[BUFSIZ]; + FILE *ptr = popen (format, "r"); + if (ptr != NULL) + { + while (fgets (buf, BUFSIZ, ptr) != NULL) + sb.append (buf); + pclose (ptr); + } + } + return sb.toString (); +} + +/** + * Creates the directory named by the specified path name, including any + * necessary but nonexistent parent directories. + * Uses system utility "/bin/mkdir -p" + * Temporary limitation: path name should not contain spaces. + * Returns message from "/bin/mkdir -p" + * @param pathname + * @return result + */ +char * +dbe_create_directories (const char *pathname) +{ + StringBuilder sb; + char *makedir = dbe_sprintf (NTXT ("/bin/mkdir -p %s 2>&1"), pathname); + char out[BUFSIZ]; + FILE *ptr = popen (makedir, "r"); + if (ptr != NULL) + { + while (fgets (out, BUFSIZ, ptr) != NULL) + sb.append (out); + pclose (ptr); + } + free (makedir); + DIR *dir = opendir (pathname); + if (dir != NULL) + { + closedir (dir); + return NULL; // success + } + else + sb.append (NTXT ("\nError: Cannot open directory\n")); // DEBUG + return sb.toString (); // error +} + +/** + * Deletes the file or the directory named by the specified path name. + * If this pathname denotes a directory, then the directory must be empty in order to be deleted. + * Uses system utility "/bin/rm" or "/bin/rmdir" + * Temporary limitation: path name should not contain spaces. + * Returns error message from system utility + * @param pathname + * @return result + */ +char * +dbe_delete_file (const char *pathname) +{ + StringBuilder sb; + char *cmd = NULL; + struct stat64 sbuf; + sbuf.st_mode = 0; + int st = dbe_stat (pathname, &sbuf); + if (st == 0) + { // Exists + if (S_ISDIR (sbuf.st_mode) != 0) // Directory + cmd = dbe_sprintf (NTXT ("/bin/rmdir %s 2>&1"), pathname); + else if (S_ISREG (sbuf.st_mode) != 0) // Regular file + cmd = dbe_sprintf (NTXT ("/bin/rm %s 2>&1"), pathname); + } + else + return NULL; // Nothing to remove + if (cmd != NULL) + { + char out[BUFSIZ]; + FILE *ptr = popen (cmd, "r"); + if (ptr != NULL) + { + while (fgets (out, BUFSIZ, ptr) != NULL) + sb.append (out); + pclose (ptr); + } + free (cmd); + } + else + sb.sprintf (NTXT ("Error: cannot remove %s - not a regular file and not a directory\n"), pathname); + return sb.toString (); +} + +char * +dbe_xml2str (const char *s) +{ + if (s == NULL) + return NULL; + StringBuilder sb; + while (*s) + { + if (*s == '&') + { + if (strncmp (s, NTXT (" "), 6) == 0) + { + sb.append (' '); + s += 6; + continue; + } + else if (strncmp (s, NTXT ("""), 6) == 0) + { + sb.append ('"'); + s += 6; + continue; + } + else if (strncmp (s, NTXT ("&"), 5) == 0) + { + sb.append ('&'); + s += 5; + continue; + } + else if (strncmp (s, NTXT ("<"), 4) == 0) + { + sb.append ('<'); + s += 4; + continue; + } + else if (strncmp (s, NTXT (">"), 4) == 0) + { + sb.append ('>'); + s += 4; + continue; + } + } + sb.append (*s); + s++; + } + return sb.toString (); +} + +void +swapByteOrder (void *p, size_t sz) +{ + if (sz == 8) + { + uint64_t *pv = (uint64_t *) p; + uint64_t v = *pv; + v = ((v & 0x00000000FF000000) << 8) | ((v >> 8) & 0x00000000FF000000) | + ((v & 0x0000000000FF0000) << 24) | ((v >> 24) & 0x0000000000FF0000) | + ((v & 0x000000000000FF00) << 40) | ((v >> 40) & 0x000000000000FF00) | + (v >> 56) | (v << 56); + *pv = v; + } + else if (sz == 4) + { + uint32_t *pv = (uint32_t *) p; + uint32_t v = *pv; + v = (v >> 24) | (v << 24) | ((v & 0x0000FF00) << 8) | ((v >> 8) & 0x0000FF00); + *pv = v; + } + else if (sz == 2) + { + uint16_t *pv = (uint16_t *) p; + uint16_t v = *pv; + v = (v >> 8) | (v << 8); + *pv = v; + } +} + +void +destroy (void *vec) +{ + if (vec == NULL) + return; + Vector<void*> *array = (Vector<void*>*)vec; + switch (array->type ()) + { + case VEC_STRING: + ((Vector<char *>*)array)->destroy (); + break; + case VEC_VOIDARR: + case VEC_STRINGARR: + case VEC_INTARR: + case VEC_BOOLARR: + case VEC_LLONGARR: + case VEC_DOUBLEARR: + for (long i = 0; i < array->size (); i++) + destroy (array->fetch (i)); + break; + case VEC_INTEGER: + case VEC_CHAR: + case VEC_BOOL: + case VEC_DOUBLE: + case VEC_LLONG: + default: + break; + } + delete array; +} + +int64_t +read_from_file (int fd, void *buffer, int64_t nbyte) +{ + int64_t cnt = 0; + char *buf = (char *) buffer; + while (nbyte > 0) + { // Sometimes system cannot read 'nbyte' + ssize_t n = read (fd, (void *) (buf + cnt), (size_t) nbyte); + if (n <= 0) + break; + nbyte -= n; + cnt += n; + } + return cnt; +} + +/** + * Create symbolic link to the path + * @param path - path with spaces + * @param dir - directory where the link should be created + * @return symbolic link + */ +char * +dbe_create_symlink_to_path (const char *path, const char *dir) +{ + char *symbolic_link = NULL; + if (NULL == path || NULL == dir) + return NULL; + int res = mkdir (dir, 0777); + if (res != 0 && dbe_stat (dir, NULL) != 0) + return NULL; // Cannot create directory + long len = dbe_sstrlen (path); + if (len <= 4) + return NULL; // Unknown situation + if (strcmp ((path + len - 4), "/bin") != 0) // Unknown situation + return NULL; + int max = 99; // Just an arbitrary number + for (int i = 1; i <= max; i++) + { + // Try to create symbolic link + char *d = dbe_sprintf ("%s/%d", dir, i); + if (NULL == d) + return NULL; + res = mkdir (d, 0777); + symbolic_link = dbe_sprintf ("%s/%s", d, "bin"); + free (d); + if (NULL == symbolic_link) // Not enough memory + return NULL; + res = symlink (path, symbolic_link); + if (res == 0) // Link is created - use it. + break; + // Check if such link already exists + int e = errno; + char buf[MAXPATHLEN + 1]; + memset (buf, 0, MAXPATHLEN + 1); + ssize_t n = readlink (symbolic_link, buf, MAXPATHLEN); + if (n == len && strcmp (path, buf) == 0) // Link is correct - use it. + break; + if (i == max) + { // report the error + fprintf (stderr, GTXT ("Error: symlink(%s, %s) returned error: %d\n"), path, symbolic_link, res); + fprintf (stderr, GTXT ("Error: errno=%d (%s)\n"), e, strerror (e)); + fflush (stderr); + } + free (symbolic_link); + symbolic_link = NULL; + } + return symbolic_link; +} + +// Compute checksum for specified file. +// This code is from usr/src/cmd/cksum.c, adapted for us +// crcposix -- compute posix.2 compatable 32 bit CRC +// +// The POSIX.2 (draft 10) CRC algorithm. +// This is a 32 bit CRC with polynomial +// x**32 + x**26 + x**23 + x**22 + x**16 + x**12 + x**11 + x**10 + +// x**8 + x**7 + x**5 + x**4 + x**2 + x**1 + x**0 +// +// layout is from the POSIX.2 Rationale + +static uint32_t crctab_posix[256] = { + 0x00000000L, + 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, 0x130476DCL, 0x17C56B6BL, + 0x1A864DB2L, 0x1E475005L, 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, + 0x2B4BCB61L, 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, 0x5F15ADACL, + 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, 0x6A1936C8L, 0x6ED82B7FL, + 0x639B0DA6L, 0x675A1011L, 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, + 0x745E66CDL, 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, 0xBE2B5B58L, + 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, 0xAD2F2D84L, 0xA9EE3033L, + 0xA4AD16EAL, 0xA06C0B5DL, 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, + 0xD9714B49L, 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, 0xE13EF6F4L, + 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, 0x34867077L, 0x30476DC0L, + 0x3D044B19L, 0x39C556AEL, 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, + 0x2AC12072L, 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, 0x7897AB07L, + 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, 0x6B93DDDBL, 0x6F52C06CL, + 0x6211E6B5L, 0x66D0FB02L, 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, + 0x53DC6066L, 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, 0xBFA1B04BL, + 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, 0x8AAD2B2FL, 0x8E6C3698L, + 0x832F1041L, 0x87EE0DF6L, 0x99A95DF3L, 0x9D684044L, 0x902B669DL, + 0x94EA7B2AL, 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, 0xC6BCF05FL, + 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, 0xD5B88683L, 0xD1799B34L, + 0xDC3ABDEDL, 0xD8FBA05AL, 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, + 0x644FC637L, 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, 0x5C007B8AL, + 0x58C1663DL, 0x558240E4L, 0x51435D53L, 0x251D3B9EL, 0x21DC2629L, + 0x2C9F00F0L, 0x285E1D47L, 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, + 0x3B5A6B9BL, 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, 0xF12F560EL, + 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, 0xE22B20D2L, 0xE6EA3D65L, + 0xEBA91BBCL, 0xEF68060BL, 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, + 0xDA649D6FL, 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, 0xAE3AFBA2L, + 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, 0x9B3660C6L, 0x9FF77D71L, + 0x92B45BA8L, 0x9675461FL, 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, + 0x857130C3L, 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, 0x7B827D21L, + 0x7F436096L, 0x7200464FL, 0x76C15BF8L, 0x68860BFDL, 0x6C47164AL, + 0x61043093L, 0x65C52D24L, 0x119B4BE9L, 0x155A565EL, 0x18197087L, + 0x1CD86D30L, 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, 0x2497D08DL, + 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, 0xC5A92679L, 0xC1683BCEL, + 0xCC2B1D17L, 0xC8EA00A0L, 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, + 0xDBEE767CL, 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, 0x89B8FD09L, + 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, 0x9ABC8BD5L, 0x9E7D9662L, + 0x933EB0BBL, 0x97FFAD0CL, 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, + 0xA2F33668L, 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; + +static void +m_crcposix (uint32_t *crcp, unsigned char *bp, uint32_t n) +{ + while (n-- > 0) + *crcp = (*crcp << 8) ^ crctab_posix[(unsigned char) ((*crcp >> 24)^*bp++)]; +} + +// Do CRC-POSIX function by calling a library entry point that has a +// slightly different calling sequence. +static uint32_t +docrcposix (uint32_t crcval, unsigned char *bp, uint32_t n) +{ + m_crcposix (&crcval, bp, n); + return (crcval); +} + +// Sum algorithms require various kinds of post-processing. +// The 'S' and 'R' variables are from the POSIX.2 (Draft 8?) description +// of the "sum" utility. +static uint32_t +postprocess (uint32_t S, long long n) +{ + // POSIX tacks on significant bytes of the length so that + // different length sequences of '\0' have different sums; + // then it complements sum. + unsigned char char_n[sizeof (n)]; + uint32_t i; + for (i = 0; n != 0; n >>= 8, ++i) + char_n[i] = (unsigned char) (n & 0xFF); + return (~docrcposix (S, char_n, i)); +} + +uint32_t +get_cksum (const char * pathname, char ** errmsg) +{ + int fd = open (pathname, O_RDONLY); + if (fd < 0) + { + if (errmsg) + *errmsg = dbe_sprintf (GTXT ("*** Warning: Error opening file for reading: %s"), pathname); + return 0; // error + } + uint32_t crcval = 0; + long long bytes = 0; + int64_t n; + unsigned char buf[4096]; + while ((n = read_from_file (fd, (char *) buf, sizeof (buf))) > 0) + { + bytes += n; + crcval = docrcposix (crcval, buf, n); + } + close (fd); + crcval = postprocess (crcval, bytes); + return crcval; +} diff --git a/gprofng/src/util.h b/gprofng/src/util.h new file mode 100644 index 0000000..0d1b8bc --- /dev/null +++ b/gprofng/src/util.h @@ -0,0 +1,185 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _PERFAN_UTIL_H +#define _PERFAN_UTIL_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <stdint.h> + +#include "gp-defs.h" +#include "gp-time.h" +#include "i18n.h" +#include "debug.h" + +#define SWAP_ENDIAN(x) swapByteOrder((void *) (&(x)), sizeof(x)) +#define AppendString(len, arr, ...) len += snprintf(arr + len, sizeof(arr) - len, __VA_ARGS__) +#define ARR_SIZE(x) (sizeof (x) / sizeof (*(x))) + +// Utility routines. + +// +// Inline functions +// +// max(a, b) - Return the maximum of two values +inline int +max (int a, int b) +{ + return (a >= b) ? a : b; +} + +// min(a, b) - Return the minimum of two values +inline int +min (int a, int b) +{ + return (a <= b) ? a : b; +} + +// streq(s1, s2) - Returns 1 if strings are the same, 0 otherwise +inline int +streq (const char *s1, const char *s2) +{ + return strcmp (s1, s2) == 0; +} + +// StrChr(str, ch) - Rerurn 'str' if 'ch' does not occur in 'str' or +// a pointer to the next symbol after the first occurrence of 'ch' in 'str' +inline char * +StrChr (char *str, char ch) +{ + char *s = strchr (str, ch); + return s ? (s + 1) : str; +} + +// StrRchr(str, ch) - Rerurn 'str' if 'ch' does not occur in 'str' or +// a pointer to the next symbol after the last occurrence of 'ch' in 'str' +inline char * +StrRchr (char *str, char ch) +{ + char *s = strrchr (str, ch); + return s ? (s + 1) : str; +} + +inline char* +STR (const char *s) +{ + return s ? (char*) s : (char*) NTXT ("NULL"); +} + +inline char* +get_str (const char *s, const char *s1) +{ + return s ? (char*) s : (char*) s1; +} + +inline char * +get_basename (const char* name) +{ + return StrRchr ((char*) name, '/'); +} + +inline char * +dbe_strdup (const char *str) +{ + return str ? strdup (str) : NULL; +} + +inline long +dbe_sstrlen (const char *str) +{ + return str ? (long) strlen (str) : 0; +} + +inline int +dbe_strcmp (const char *s1, const char *s2) +{ + return s1 ? (s2 ? strcmp (s1, s2) : 1) : (s2 ? -1 : 0); +} + +// tstodouble(t) - Return timestruc_t in (double) seconds +inline double +tstodouble (timestruc_t t) +{ + return (double) t.tv_sec + (double) (t.tv_nsec / 1000000000.0); +} + +inline void +hr2timestruc (timestruc_t *d, hrtime_t s) +{ + d->tv_sec = (long) (s / NANOSEC); + d->tv_nsec = (long) (s % NANOSEC); +} + +inline hrtime_t +timestruc2hr (timestruc_t *s) +{ + return (hrtime_t) s->tv_sec * NANOSEC + (hrtime_t) s->tv_nsec; +} + +struct stat64; + +#if defined(__cplusplus) +extern "C" +{ +#endif + // + // Declaration of utility functions + // + void tsadd (timestruc_t *result, timestruc_t *time); + void tssub (timestruc_t *result, timestruc_t *time1, timestruc_t *time2); + int tscmp (timestruc_t *time1, timestruc_t *time2); + void int_max (int *maximum, int count); + char *strstr_r (char *s1, const char *s2); + char *strrpbrk (const char *string, const char *brkset); + char *read_line (FILE *); + char *parse_qstring (char *in_str, char **endptr); + char *parse_fname (char *in_str, char **fcontext); + int get_paren (const char *name); + + uint64_t crc64 (const char *str, size_t len); + char *canonical_path (char *path); + char *get_relative_path (char *name); + char *get_relative_link (const char *path_to, const char *path_from); + char *get_prog_name (int basename); + char *dbe_strndup (const char *str, size_t len); + int dbe_stat (const char *path, struct stat64 *sbuf); + int dbe_stat_file (const char *path, struct stat64 *sbuf); + char *dbe_read_dir (const char *path, const char *format); + char *dbe_get_processes (const char *format); + char *dbe_create_directories (const char *pathname); + char *dbe_delete_file (const char *pathname); + char *dbe_xml2str (const char *s); + void swapByteOrder (void *p, size_t sz); + char *dbe_sprintf (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); + ssize_t dbe_write (int f, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + char *dbe_create_symlink_to_path (const char *path, const char *dir); + int64_t read_from_file (int fd, void *buffer, int64_t nbyte); + uint32_t get_cksum (const char * pathname, char ** errmsg); + +#ifdef __cplusplus +} +int catch_out_of_memory (int (*real_main)(int, char*[]), int argc, char *argv[]); +#endif + + +#endif /* _UTIL_H */ diff --git a/gprofng/src/vec.h b/gprofng/src/vec.h new file mode 100644 index 0000000..28b1800c --- /dev/null +++ b/gprofng/src/vec.h @@ -0,0 +1,524 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _PERFAN_VEC_H +#define _PERFAN_VEC_H + +#include <assert.h> +#include <inttypes.h> +#include <string.h> +#include <stdlib.h> + +// This package implements a vector of items. + +#define Destroy(x) if (x) { (x)->destroy(); delete (x); (x) = NULL; } +#define VecSize(x) ((x) ? (x)->size() : 0) + +void destroy (void *vec); // Free up the "two-dimension" Vectors + +typedef int (*CompareFunc)(const void*, const void*); +typedef int (*ExtCompareFunc)(const void*, const void*, const void*); +typedef int (*SearchFunc)(char*, char*); + +extern "C" +{ + typedef int (*StdCompareFunc)(const void*, const void*); +} + +enum Search_type +{ + LINEAR, + BINARY, + HASH +}; + +enum Direction +{ + FORWARD, + REVERSE +}; + +enum VecType +{ + VEC_VOID = 0, + VEC_INTEGER, + VEC_CHAR, + VEC_BOOL, + VEC_DOUBLE, + VEC_LLONG, + VEC_VOIDARR, + VEC_STRING, + VEC_INTARR, + VEC_BOOLARR, + VEC_LLONGARR, + VEC_STRINGARR, + VEC_DOUBLEARR +}; + +template <class ITEM> void +qsort (ITEM *, size_t, ExtCompareFunc, void *); + +template <typename ITEM> class Vector +{ +public: + + Vector () + { + count = 0; + data = NULL; + limit = 0; + sorted = false; + }; + + Vector (long sz); + + virtual + ~Vector () + { + free (data); + } + + void append (const ITEM item); + void addAll (Vector<ITEM> *vec); + Vector<ITEM> *copy (); // Return a copy of "this". + + ITEM + fetch (long index) + { + return data[index]; + } + + ITEM + get (long index) + { + return data[index]; + } + + // Return the first index in "this" that equals "item". + // Return -1 if "item" is not found. + long find (const ITEM item); + long find_r (const ITEM item); + + // Insert "item" into "index"'th slot of "this", + // moving everything over by 1. + void insert (long index, const ITEM item); + + // Insert "item" after locating its appropriate index + void incorporate (const ITEM item, CompareFunc func); + + // Remove and return the "index"'th item from "this", + // moving everything over by 1. + ITEM remove (long index); + + // Swap two items in "this", + void swap (long index1, long index2); + + long + size () + { + return count; + } + + // Store "item" into the "index"'th slot of "this". + void store (long index, const ITEM item); + + void + put (long index, const ITEM item) + { + store (index, item); + } + + // Sort the vector according to compare + void + sort (CompareFunc compare, void *arg = NULL) + { + qsort (data, count, (ExtCompareFunc) compare, arg); + sorted = true; + } + + // Binary search, vector must be sorted + long bisearch (long start, long end, void *key, CompareFunc func); + void destroy (); // delete all vector elements (must be pointers!) + + void + reset () + { + count = 0; + sorted = false; + } + + bool + is_sorted () + { + return sorted; + } + + virtual VecType + type () + { + return VEC_VOID; + } + + virtual void + dump (const char * /* msg */) + { + return; + } + +private: + + void resize (long index); + + ITEM *data; // Pointer to data vector + long count; // Number of items + long limit; // Vector length (power of 2) + bool sorted; +}; + +template<> VecType Vector<int>::type (); +template<> VecType Vector<unsigned>::type (); +template<> VecType Vector<char>::type (); +template<> VecType Vector<bool>::type (); +template<> VecType Vector<double>::type (); +template<> VecType Vector<long long>::type (); +template<> VecType Vector<uint64_t>::type (); +template<> VecType Vector<void*>::type (); +template<> VecType Vector<char*>::type (); +template<> VecType Vector<Vector<int>*>::type (); +template<> VecType Vector<Vector<char*>*>::type (); +template<> VecType Vector<Vector<long long>*>::type (); +template<> void Vector<char *>::destroy (); + +#define KILOCHUNK 1024 +#define MEGACHUNK 1024*1024 +#define GIGACHUNK 1024*1024*1024 + +// A standard looping construct: +#define Vec_loop(ITEM, vec, index, item) \ +if (vec != NULL) \ + for (index = 0, item = ((vec)->size() > 0) ? (vec)->fetch(0) : (ITEM)0; \ + index < (vec)->size(); \ + item = (++index < (vec)->size()) ? (vec)->fetch(index) : (ITEM)0) + +template <typename ITEM> +Vector<ITEM>::Vector (long sz) +{ + count = 0; + limit = sz > 0 ? sz : KILOCHUNK; // was 0; + data = limit ? (ITEM *) malloc (sizeof (ITEM) * limit) : NULL; + sorted = false; +} + +template <typename ITEM> void +Vector<ITEM> +::resize (long index) +{ + if (index < limit) + return; + if (limit < 16) + limit = 16; + while (index >= limit) + { + if (limit > GIGACHUNK) + limit += GIGACHUNK; // Deoptimization for large experiments + else + limit = limit * 2; + } + data = (ITEM *) realloc (data, limit * sizeof (ITEM)); +} + +template <typename ITEM> void +Vector<ITEM>::append (const ITEM item) +{ + // This routine will append "item" to the end of "this". + if (count >= limit) + resize (count); + data[count++] = item; +} + +template <typename ITEM> void +Vector<ITEM>::addAll (Vector<ITEM> *vec) +{ + if (vec) + for (int i = 0, sz = vec->size (); i < sz; i++) + append (vec->fetch (i)); +} + +template <typename ITEM> Vector<ITEM> * +Vector<ITEM>::copy () +{ + // This routine will return a copy of "this". + Vector<ITEM> *vector; + vector = new Vector<ITEM>; + vector->count = count; + vector->limit = limit; + vector->data = (ITEM *) malloc (sizeof (ITEM) * limit); + (void) memcpy ((char *) vector->data, (char *) data, sizeof (ITEM) * count); + return vector; +} + +template <typename ITEM> long +Vector<ITEM>::find (const ITEM match_item) +{ + for (long i = 0; i < size (); i++) + if (match_item == get (i)) + return i; + return -1; +} + +template <typename ITEM> long +Vector<ITEM>::find_r (const ITEM match_item) +{ + for (long i = size () - 1; i >= 0; i--) + if (match_item == get (i)) + return i; + return -1; +} + +template <typename ITEM> void +Vector<ITEM>::insert (long index, const ITEM item) +{ + // This routine will insert "item" into the "index"'th slot of "this". + // An error occurs if "index" > size(). + // "index" is allowed to be equal to "count" in the case that + // you are inserting past the last element of the vector. + // In that case, the bcopy below becomes a no-op. + assert (index >= 0); + assert (index <= count); + append (item); + (void) memmove (((char *) (&data[index + 1])), (char *) (&data[index]), + (count - index - 1) * sizeof (ITEM)); + data[index] = item; +} + +template <typename ITEM> ITEM +Vector<ITEM>::remove (long index) +{ + // This routine will remove the "index"'th item from "this" and + // return it. An error occurs if "index" >= size();. + assert (index >= 0); + assert (index < count); + ITEM item = data[index]; + for (long i = index + 1; i < count; i++) + data[i - 1] = data[i]; + count--; + // Bad code that works good when ITEM is a pointer type + data[count] = item; + return data[count]; +} + +template <typename ITEM> void +Vector<ITEM>::swap (long index1, long index2) +{ + ITEM item; + item = data[index1]; + data[index1] = data[index2]; + data[index2] = item; +} + +template <typename ITEM> void +Vector<ITEM>::store (long index, const ITEM item) +{ + if (index >= count) + { + resize (index); + memset (&data[count], 0, (index - count) * sizeof (ITEM)); + count = index + 1; + } + data[index] = item; +} + +// This routine performs a binary search across +// the entire vector, with "start" being the low boundary. +// It is assumed that the vector is SORTED in +// ASCENDING ORDER by the same criteria as the +// compare function. +// If no match is found, -1 is returned. +template <typename ITEM> long +Vector<ITEM>::bisearch (long start, long end, void *key, CompareFunc compare) +{ + ITEM *itemp; + if (end == -1) + end = count; + if (start >= end) + return -1; // start exceeds limit + itemp = (ITEM *) bsearch ((char *) key, (char *) &data[start], + end - start, sizeof (ITEM), (StdCompareFunc) compare); + if (itemp == (ITEM *) 0) + return -1; // not found + return (long) (itemp - data); +} + +template <typename ITEM> void +Vector<ITEM>::incorporate (const ITEM item, CompareFunc compare) +{ + long lt = 0; + long rt = count - 1; + while (lt <= rt) + { + long md = (lt + rt) / 2; + if (compare (data[md], item) < 0) + lt = md + 1; + else + rt = md - 1; + } + if (lt == count) + append (item); + else + insert (lt, item); +} + +#define QSTHRESH 6 + +template <typename ITEM> void +qsort (ITEM *base, size_t nelem, ExtCompareFunc qcmp, void *arg) +{ + for (;;) + { + // For small arrays use insertion sort + if (nelem < QSTHRESH) + { + for (size_t i = 1; i < nelem; i++) + { + ITEM *p = base + i; + ITEM *q = p - 1; + if (qcmp (q, p, arg) > 0) + { + ITEM t = *p; + *p = *q; + while (q > base && qcmp (q - 1, &t, arg) > 0) + { + *q = *(q - 1); + --q; + } + *q = t; + } + } + return; + } + + ITEM *last = base + nelem - 1; + ITEM *mid = base + nelem / 2; + // Sort the first, middle, and last elements + ITEM *a1 = base, *a2, *a3; + if (qcmp (base, mid, arg) > 0) + { + if (qcmp (mid, last, arg) > 0) + { // l-m-b + a2 = last; + a3 = last; + } + else if (qcmp (base, last, arg) > 0) + { // l-b-m + a2 = mid; + a3 = last; + } + else + { // m-b-l + a2 = mid; + a3 = mid; + } + } + else if (qcmp (mid, last, arg) > 0) + { + a1 = mid; + a3 = last; + if (qcmp (base, last, arg) > 0) // m-l-b + a2 = base; + else // b-l-m + a2 = a3; + } + else // b-m-l + a3 = a2 = a1; + if (a1 != a2) + { + ITEM t = *a1; + *a1 = *a2; + if (a2 != a3) + *a2 = *a3; + *a3 = t; + } + + // Partition + ITEM *i = base + 1; + ITEM *j = last - 1; + for (;;) + { + while (i < mid && qcmp (i, mid, arg) <= 0) + i++; + while (j > mid && qcmp (mid, j, arg) <= 0) + j--; + if (i == j) + break; + ITEM t = *i; + *i = *j; + *j = t; + if (i == mid) + { + mid = j; + i++; + } + else if (j == mid) + { + mid = i; + j--; + } + else + { + i++; + j--; + } + } + + // Compare two partitions. Do the smaller one by recursion + // and loop over the larger one. + size_t nleft = mid - base; + size_t nright = nelem - nleft - 1; + if (nleft <= nright) + { + qsort (base, nleft, qcmp, arg); + base = mid + 1; + nelem = nright; + } + else + { + qsort (mid + 1, nright, qcmp, arg); + nelem = nleft; + } + } +} + +template<> inline void +Vector<char*>::destroy () +{ + for (long i = 0; i < count; i++) + free (data[i]); + count = 0; +} + +template <typename ITEM> inline void +Vector<ITEM>::destroy () +{ + for (long i = 0; i < count; i++) + delete data[i]; + count = 0; +} + +#endif /* _VEC_H */ diff --git a/gprofng/testsuite/config/default.exp b/gprofng/testsuite/config/default.exp new file mode 100644 index 0000000..6fde0a1 --- /dev/null +++ b/gprofng/testsuite/config/default.exp @@ -0,0 +1,38 @@ +# Basic expect script for gprofng tests +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# The "make check" target in the Makefile passes in +# "CC=$(CC_FOR_TARGET)". But, if the user invokes runtest directly, +# these flags may not be set. +if {![info exists CC]} { + set CC [find_gcc] +} +if {![info exists CC_FOR_TARGET]} { + set CC_FOR_TARGET $CC +} +if {![info exists CFLAGS]} { + set CFLAGS "-g -O2" +} + +# Make a temporary install dir to run gprofng from, and point at it +remote_exec host "sh -c \"rm -rf tmpdir; mkdir -p tmpdir; $MAKE -C .. install-gprofng program_transform_name= DESTDIR=`pwd`/tmpdir/root\"" + +load_lib display-lib.exp diff --git a/gprofng/testsuite/gprofng.display/display.exp b/gprofng/testsuite/gprofng.display/display.exp new file mode 100644 index 0000000..108144c --- /dev/null +++ b/gprofng/testsuite/gprofng.display/display.exp @@ -0,0 +1,86 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +if {[info exists env(LC_ALL)]} { + set old_lc_all $env(LC_ALL) +} +set env(LC_ALL) "C" + +set pltf [exec uname -i] +switch $pltf { + x86_64 { + # Columns in the table represent: + # dir cflags gprofflags Others + set table { + {"jsynprog" "-g -Wall" "-p on -j on"} + {"mttest" "" ""} + {"mttest" "-g -Wall" "-p on"} + {"mttest" "-g -O0" "-p on"} + {"mttest" "-g -O" "-p on"} + {"mttest" "-g -O" "-h on"} + {"mttest" "-g -O" "-h on"} + {"mttest" "-g -O" "-p on -h on"} + {"synprog" "" ""} + {"synprog" "-g" "-p on"} + {"synprog" "-g -O0" "-p on"} + {"synprog" "-g -O" "-p on"} + {"synprog" "-g" "-p on -h on"} + {"synprog" "-g -O0" "-p on -h on"} + {"synprog" "-g -O" "-p on -h on"} + } + } + aarch64 { + set table { + {"jsynprog" "-g -Wall" "-p on -j on"} + {"mttest" "" ""} + {"mttest" "-g -Wall" "-p on"} + {"mttest" "-g -O0" "-p on"} + {"mttest" "-g -O" "-p on"} + {"synprog" "" ""} + {"synprog" "-g" "-p on"} + {"synprog" "-g -O" "-p on"} + } + } + default { + # Columns in the table represent: + # dir cflags gprofflags Others + set table { + {"mttest" "" ""} + {"synprog" "" ""} + } + } +} + +foreach line $table { + set dir [lindex $line 0] + set cflags [lindex $line 1] + set gprofflags [lindex $line 2] + + verbose [file rootname $line] + verbose running display test $line + run_display_test $dir $cflags $gprofflags +} + + +if {[info exists old_lc_all]} { + set env(LC_ALL) $old_lc_all +} else { + unset env(LC_ALL) +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Intface.java b/gprofng/testsuite/gprofng.display/jsynprog/Intface.java new file mode 100644 index 0000000..016e7b2 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Intface.java @@ -0,0 +1,6 @@ +// Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + +public interface Intface { + public int add_int (int scale); + public double add_double (int scale); +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Launcher.java b/gprofng/testsuite/gprofng.display/jsynprog/Launcher.java new file mode 100644 index 0000000..33ee06c --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Launcher.java @@ -0,0 +1,90 @@ +// Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +// @(#)Launcher.java 1.3 10/03/24 SMI + +import java.lang.reflect.*; + +public class Launcher { +// Byte array for dynamically loaded class: // +//public class DynLoadedClass { // +// public int DynamicFunction(int x) { // +// float f = 0; // +// for (int k=0 ; k<20000; k++) { // +// f = ((float)k) / x; // +// } // +// return (int)f; // +// } // +// // +// public static void main(String[] args){ // +// DynLoadedClass dcls = new DynLoadedClass(); // +// for (int k=0 ; k<10; k++) { // +// dcls.DynamicFunction(k); // +// } // +// } // +//} // +static final byte [] bClassGenerated = { + -54, -2, -70, -66, 0, 0, 0, 46, 0, 20, 10, 0, 5, 0, 16, 7, 0, 17, 10, 0, 2, 0, 16, 10, 0, 2, + 0, 18, 7, 0, 19, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, + 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 15, 68, 121, + 110, 97, 109, 105, 99, 70, 117, 110, 99, 116, 105, 111, 110, 1, 0, 4, 40, 73, 41, 73, 1, 0, 4, 109, 97, + 105, 110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, + 41, 86, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 19, 68, 121, 110, 76, 111, 97, 100, + 101, 100, 67, 108, 97, 115, 115, 46, 106, 97, 118, 97, 12, 0, 6, 0, 7, 1, 0, 14, 68, 121, 110, 76, 111, + 97, 100, 101, 100, 67, 108, 97, 115, 115, 12, 0, 10, 0, 11, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, + 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 2, 0, 5, 0, 0, 0, 0, 0, 3, 0, 1, 0, 6, 0, + 7, 0, 1, 0, 8, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, + 1, 0, 9, 0, 0, 0, 6, 0, 1, 0, 0, 0, 1, 0, 1, 0, 10, 0, 11, 0, 1, 0, 8, 0, 0, + 0, 66, 0, 2, 0, 4, 0, 0, 0, 26, 11, 69, 3, 62, 29, 17, 78, 32, -94, 0, 15, 29, -122, 27, -122, + 110, 69, -124, 3, 1, -89, -1, -16, 36, -117, -84, 0, 0, 0, 1, 0, 9, 0, 0, 0, 22, 0, 5, 0, 0, + 0, 3, 0, 2, 0, 4, 0, 11, 0, 5, 0, 17, 0, 4, 0, 23, 0, 7, 0, 9, 0, 12, 0, 13, 0, + 1, 0, 8, 0, 0, 0, 69, 0, 2, 0, 3, 0, 0, 0, 29, -69, 0, 2, 89, -73, 0, 3, 76, 3, 61, + 28, 16, 10, -94, 0, 15, 43, 28, -74, 0, 4, 87, -124, 2, 1, -89, -1, -15, -79, 0, 0, 0, 1, 0, 9, + 0, 0, 0, 22, 0, 5, 0, 0, 0, 11, 0, 8, 0, 12, 0, 16, 0, 13, 0, 22, 0, 12, 0, 28, 0, + 15, 0, 1, 0, 14, 0, 0, 0, 2, 0, 15 + }; + + private static DynClassLoader persistentInstance; + + public static DynClassLoader getPersistentInstance() + { + if (persistentInstance == null) + persistentInstance = new DynClassLoader(); + return persistentInstance; + } + + public static void main(String args []) { + if (args.length != 1) { + System.err.println("Usage: Launcher DynLoadedClass"); + return; + } + + String className = args[0]; // Dynamic class name + + try { + Class genClass = getPersistentInstance().getClassFromByteArray(className, bClassGenerated); + Method[] methods_g = genClass.getDeclaredMethods(); + + for (int i = 0; i < methods_g.length; i++) { + Method m = methods_g[i]; + String methodName = m.getName(); + String progArgs[] = new String[1]; + //System.out.println("Invoking method " + className + "." + methodName); + if (methodName.equals("main")) + m.invoke( null, (Object[]) progArgs ); + } + } catch (InvocationTargetException iex) { + System.err.println("InvocationTargetException"); + } catch (IllegalAccessException aex) { + System.err.println("IllegalAccessException"); + } + } + + // Class loader to generate dynamic class on the fly from the byte array + private static class DynClassLoader extends ClassLoader { + public DynClassLoader() { } + public Class getClassFromByteArray(String name, byte[] b) { + return super.defineClass(name, b, 0, b.length); + } + } + + +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Makefile b/gprofng/testsuite/gprofng.display/jsynprog/Makefile new file mode 100644 index 0000000..e78b692 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Makefile @@ -0,0 +1,56 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +TARGETS = libcloop.so jsynprog.class +TARGET = jsynprog +ACCT_FILE = jsynprog.acct + +srcdir = . +include $(srcdir)/../../lib/Makefile.skel + +JAVACFLAGS = + +SRCS = \ + $(srcdir)/../mttest/gethrtime.c \ + $(srcdir)/cloop.cc \ + $(NULL) + +JAVA_SRCS = \ + $(srcdir)/Intface.java \ + $(srcdir)/Routine.java \ + $(srcdir)/Sub_Routine.java \ + $(srcdir)/jsynprog.java \ + $(srcdir)/Launcher.java \ + $(NULL) + +HDRS = jsynprog.h + +libcloop.so: $(SRCS) + @echo " ---- Build: $@ -----" + $(CC) $(jdk_inc) $(CCOPTS) $(SHAREDOPT) -o $@ $(SRCS) + +jsynprog.class: $(JAVA_SRCS) + @echo " ---- Build: $@ -----" + $(JAVAC) $(JAVACFLAGS) -d . $(JAVA_SRCS) + +$(EXPERIMENT): $(TARGETS) + @echo " ---- Build: $@ -----" + rm -rf $@ + $(COLLECT) $(COLLECT_FLAGS) -o $@ $(JAVA) $(JAVACFLAGS) jsynprog + diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Routine.java b/gprofng/testsuite/gprofng.display/jsynprog/Routine.java new file mode 100644 index 0000000..cfe45d2 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Routine.java @@ -0,0 +1,224 @@ +/** + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * This class implements the Intface interface + * increments value of integer and floats + */ + +import java.util.*; + +public class Routine implements Intface { + + /* add integers */ + public int add_int (int scale) { + int x = 0; + int kmax = 100*scale; + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { x = 0; + for (int k=0; k<kmax;k++) { + for (int j=0; j<10000;j++) { + x = x + 1; + } + } + } while (jsynprog.Timer() < tEnd); + return x; + } + + /* add double */ + public double add_double (int scale) { + double y = 0.0; + int kmax = 1*scale; + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { y = 0.0; + for (int k=0; k<kmax;k++) { + for (int j=0; j<10000;j++) { + y = y + 1.0; + } + } + } while (jsynprog.Timer() < tEnd); + return y; + } + + /* Use inner class */ + public Integer[] has_inner_class(int scale) { + class JInner { + Integer[] g_int = new Integer[3]; + + public Integer[] buildlist(int scale) { + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + for (int k=0; k<g_int.length; k++) { + int x = 0; + int imax = 10*scale; + for (int i=0; i<imax;i++) { + for (int j=0; j<10000;j++) { + x = x + 1; + } + } + g_int[k]=new Integer (x); + } + } while (jsynprog.Timer() < tEnd); + return g_int; + } + } + return ((new JInner()).buildlist(scale)); + } + + public void memalloc (int nsize, int scale) { + class myobj { + int nitem; + String shape; + String color; + + myobj() { + nitem = 4; + shape = "square"; + color = "blue"; + } + } + for (int j=0; j<60; j++) { + for (int i=0; i<20; i++) { + myobj[] blueobj = new myobj[1000000]; + } + } + } + + /* routine to do recursion */ + public void recurse(int i, int imax, int scale) { + if(i == imax) { + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + double x; + int j, k; + x = 0.0; + for(k=0; k<scale; k++) { + for(j=0; j<5000000; j++) { + x = x + 1.0; + } + } + } while (jsynprog.Timer() < tEnd); + } else { + recurse(i+1, imax, scale); + } + } + + /* routine to do deep recursion */ + public void recursedeep(int i, int imax, int scale) { + if(i == imax) { + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + double x; + int j, k; + x = 0.0; + for(k=0; k<scale; k++) { + for(j=0; j<5000000; j++) { + x = x + 1.0; + } + } + } while (jsynprog.Timer() < tEnd); + } else { + recursedeep(i+1, imax, scale); + } + } + + + /* bounce -- example of indirect recursion */ + public void bounce(int i, int imax, int scale) { + if(i == imax) { + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + double x; + int j, k; + x = 0.0; + for(k=0; k < scale; k++) { + for(j=0; j<5000000; j++) { + x = x + 1.0; + } + } + } while (jsynprog.Timer() < tEnd); + } else { + bounce_b(i, imax, scale); + } + } + + private void bounce_b(int i, int imax, int scale) { + bounce(i+1, imax, scale); + return; + } + + + /* large array */ + public void array_op(int scale) { + int size = 50000; + int imax = 1*scale; + Integer[] y = allocate_array(3*size); + Integer[] z = allocate_array(size); + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + for (int i=0; i<imax; i++) { + System.arraycopy(y, 2, z, 0, size); + } + } while (jsynprog.Timer() < tEnd); + } + + /* define large array */ + private Integer[] allocate_array(int num) { + Integer[] x = new Integer[num]; + for (int i=0; i<num;i++) { + x[i] = new Integer(i); + } + return x; + } + + + /* large vector */ + public void vector_op(int scale) { + Vector v = allocate_vector(); + int imax = 1*scale; + int jmax = 1*scale; + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + for (int i=0; i<imax; i++) { + vrem_last(v); + } + for (int j=0; j<jmax; j++) { + vrem_first(v); + } + } while (jsynprog.Timer() < tEnd); + } + + /* define large Vector */ + private Vector allocate_vector() { + Vector<Integer> v1 = new Vector<Integer> (200000); + for (int i=0; i<1000000;i++) { + v1.add(new Integer(i)); + } + return v1; + } + + /* remove last element of vector */ + private void vrem_last(Vector v) { + v.remove(v.size()-1); + } + + /* remove first element of vector */ + private void vrem_first(Vector v) { + v.remove(0); + } + + + /* Spend time in system calls */ + public void sys_op(int scale) { + long stime ; + int jmax = 1000000; + int imax = 4; + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + for (int i = 0; i < imax; i++) { + for(int j=0; j<jmax; j++) { + stime = System.currentTimeMillis(); + } + } + } while (jsynprog.Timer() < tEnd); + } + +} //end of class diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Sub_Routine.java b/gprofng/testsuite/gprofng.display/jsynprog/Sub_Routine.java new file mode 100644 index 0000000..11e045e --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Sub_Routine.java @@ -0,0 +1,54 @@ +/* Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +** @(#)Sub_Routine.java 1.4 10/03/24 SMI +** This is subclass of Routine , overrides one method +*/ + +public class Sub_Routine extends Routine { + private static native double cTimer(); + + /* + ** Calls another method c() many times, overridden methos + */ + public int add_int(int scale) { + int w = 0; + int kmax = 100*scale; + if (scale == 1) { + kmax /= 100; + } + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { w = 0; + for (int k=0 ; k<kmax; k++) { + w = addcall(w) + 1; + } + } while (jsynprog.Timer() < tEnd); + return w; + } + + private static int addcall(int x) { + int jmax = 100; + int imax = 10; + for (int j=0; j<jmax;j++) { + for (int i=0; i<imax; i++) { + x = (i%2==0)?x:(x + 1); + } + } + return x; + } + + public int naptime(int k, int scale) + { + int i; + int imax = k * scale; + + try { + for (i = 0; i < imax; i++) { + System.out.println(i + " sleeping"); + Thread.currentThread().sleep(10); + i=i+1; + } + } catch (InterruptedException e) {e.printStackTrace();} + System.out.println("In naptime"); + return 0; + } + +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/check_results.pl b/gprofng/testsuite/gprofng.display/jsynprog/check_results.pl new file mode 100755 index 0000000..eac58a2 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/check_results.pl @@ -0,0 +1,33 @@ +#!/bin/sh -- # This comment tells perl not to loop! + +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +eval 'exec ${PERL:=/usr/dist/exe/perl} -S $0 ${1+"$@"}' +if 0; + +use strict; +require "acct.pm"; + +my(@checkTime) = (1, 2); +acct::readAcct($ARGV[0], @checkTime); +acct::read_er_print_out($ARGV[1], -1); +acct::createDiff(); +exit acct::set_retVal(0); + diff --git a/gprofng/testsuite/gprofng.display/jsynprog/cloop.cc b/gprofng/testsuite/gprofng.display/jsynprog/cloop.cc new file mode 100644 index 0000000..cf8b779 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/cloop.cc @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + */ +#include <jni.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdlib.h> +#include <sys/time.h> +#include "jsynprog.h" + +typedef long long hrtime_t; +extern "C" { + hrtime_t gethrtime(); + hrtime_t gethrvtime(); +} +static jdouble testtime = 3.0 * 1e9; + +int cfunc(int); + +JNIEXPORT jdouble JNICALL +Java_jsynprog_Timer (JNIEnv *env, jclass obj) +{ + jdouble jd; + hrtime_t start; + + start = gethrtime(); + jd = (double)(start); + return jd; +} + +JNIEXPORT jdouble JNICALL +Java_jsynprog_cTimer (JNIEnv *env, jclass obj) +{ + jdouble jd; + hrtime_t vstart; + + vstart = gethrvtime(); + jd = (double)(vstart); + return jd; +} + +JNIEXPORT jdouble JNICALL +Java_jsynprog_computeSet (JNIEnv *env, jclass obj) +{ + char *s; + + testtime = 3.0; + s = getenv("SP_COLLECTOR_TEST_TIMER"); + if( s ) { + testtime = atof(s); + if (testtime < 1.0) + testtime = 1.0; + } + testtime *= 1e9; + return testtime; +} + +JNIEXPORT jint JNICALL +Java_jsynprog_JavaJavaC (JNIEnv *env, jclass obj, jint n, int scale ) +{ + // fprintf(stderr, "Entering Java_jsynprog_JavaJavaC, scale = %d\n", scale); + int imax = 100000; + n = 0; + for (int i =0; i<imax; i++) { + n=n+((i%2==0)?1:2); + } + return n; +} + +JNIEXPORT void JNICALL +Java_jsynprog_JavaCC (JNIEnv *env, jclass obj, int scale) +{ + fprintf(stderr, "Entering Java_jsynprog_JavaCC, scale = %d\n", scale); + int n =0; + if (scale == 1) { + scale *= 1000; + } + int imax = 4*scale; + double tEnd = gethrtime() + testtime; + do { n = 0; + for (int i =0; i<imax; i++) { + n = cfunc(n); + } + } while (gethrtime() < tEnd); +} + +int cfunc (int n) { + for (int j =0; j<100000;j++) { + n=n+1; + } + return n; +} + +JNIEXPORT void JNICALL +Java_jsynprog_JavaCJava (JNIEnv *env, jclass obj, int scale) +{ + fprintf(stderr, "Entering Java_jsynprog_JavaCJava, scale = %d\n", scale); + int pnum = 0; + jmethodID mid = (env)->GetStaticMethodID(obj, "javafunc", "(I)I"); + if (mid == 0) { + fprintf(stderr, "Can't get jmethodID for \"javafunc\", \"(I)I\"\n"); + return; + } + fprintf(stderr, "Calling CallStaticIntMethod, scale = %d\n", scale); + pnum = (env)->CallStaticIntMethod(obj, mid, scale); +} + +JNIEXPORT jint JNICALL +Java_jsynprog_isJVMPI (JNIEnv *env, jclass obj) +{ + char *jvmpi = getenv("SP_COLLECTOR_USE_JVMPI"); + + return jvmpi ? 1 : 0; +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.h b/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.h new file mode 100644 index 0000000..34b4f6c --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.h @@ -0,0 +1,74 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All Rights Reserved. */ +#include <jni.h> +/* Header for class jsynprog */ + +#ifndef _Included_jsynprog +#define _Included_jsynprog +#ifdef __cplusplus +extern "C" { +#endif +/* Inaccessible static: dir_home */ +/* Inaccessible static: log */ +/* Inaccessible static: pstart */ +/* Inaccessible static: cstart */ +/* + * Class: jsynprog + * Method: Timer + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_jsynprog_Timer + (JNIEnv *, jclass); + +/* + * Class: jsynprog + * Method: cTimer + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_jsynprog_cTimer + (JNIEnv *, jclass); + +/* + * Class: jsynprog + * Method: computeSet + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_jsynprog_computeSet + (JNIEnv *, jclass); + +/* + * Class: jsynprog + * Method: JavaJavaC + * Signature: (I, I)I + */ +JNIEXPORT jint JNICALL Java_jsynprog_JavaJavaC + (JNIEnv *, jclass, jint, int); + +/* + * Class: jsynprog + * Method: JavaCC + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_jsynprog_JavaCC + (JNIEnv *, jclass, int); + +/* + * Class: jsynprog + * Method: JavaCJava + * Signature: (I, I)V + */ +JNIEXPORT void JNICALL Java_jsynprog_JavaCJava + (JNIEnv *, jclass, int); + +/* + * Class: jsynprog + * Method: isJVMPI + * Signature: (I)V + */ +JNIEXPORT jint JNICALL Java_jsynprog_isJVMPI + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.java b/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.java new file mode 100644 index 0000000..eb98b5e --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.java @@ -0,0 +1,229 @@ +// Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. +// @(#)jsynprog.java SMI + +import java.util.*; +import java.io.*; +import java.text.*; + +class jsynprog +{ + private static String dir_home; + private static PrintWriter log; + private static double pstart, cstart; + + /* JNI calls */ + public static native double Timer(); + private static native double cTimer(); + private static native double computeSet(); + private static native int JavaJavaC(int np, int scale); + private static native void JavaCC(int scale); + private static native void JavaCJava(int scale); + private static native int isJVMPI(); + + public static double testtime = 3.0 * 1e9; + + public static void main (String [] args) + { + jsynprog jsyn_obj = new jsynprog(); + Integer ni; + int scale = 1000; + + createAcct(); + LoadJNILibrary(args); + testtime = computeSet(); + + /* check for invocation parameter */ + if (args.length != 0) { + if (args[0].equals("fast")) { + scale = 10000; + } else if (args[0].equals("slow")) { + scale = 1; + } else { + System.err.println("fatal: unexpected argument: " + args[0] ); + System.exit(1); + } + } + + /* large memory allocations, trigger gc */ + Routine rtn = new Routine(); + Sub_Routine sbrt = new Sub_Routine(); + recTime(); + rtn.memalloc(10000, scale); + printValue("Routine.memalloc", false); + + /* add integers */ + recTime(); + ni = new Integer (rtn.add_int(scale)); + printValue("Routine.add_int", true); + + /* add double */ + recTime(); + Double nd = new Double(rtn.add_double(scale)); + printValue("Routine.add_double", true); + + /* call method in derived class */ + recTime(); + ni = new Integer (sbrt.add_int(scale)); + printValue("Sub_Routine.add_int", true); + + /* call method that defines an inner class */ + recTime(); + Integer[] na = rtn.has_inner_class(scale); + printValue("Routine.has_inner_class", true); + + /* recursion */ + recTime(); + rtn.recurse(0,80, scale); + printValue("Routine.recurse", true); + + /* deep recursion */ + recTime(); + rtn.recursedeep(0,500, scale); + printValue("<Truncated-stack>", true); + + /* indirect recursion */ + recTime(); + rtn.bounce(0,20, scale); + printValue("Routine.bounce", true); + + /* array operations */ + recTime(); + rtn.array_op(scale); + printValue("Routine.array_op", false); + + /* Vector operations */ + recTime(); + rtn.vector_op(scale); + printValue("Routine.vector_op", false); + + /* spend time in system calls */ + recTime(); + rtn.sys_op(scale); + printValue("Routine.sys_op", false); + + /* java->java->c */ + recTime(); + int np = 0; + jni_JavaJavaC(np, scale); + printValue("jsynprog.jni_JavaJavaC", true); + + /* java->c->c */ + recTime(); + JavaCC(scale); + printValue("jsynprog.JavaCC", true); + + /* java->c->java */ + recTime(); + JavaCJava(scale); + printValue("jsynprog.JavaCJava", true); + + + /* dynamically loaded classes */ + String java_ver = System.getProperty("java.version"); + Launcher lnch = new Launcher(); + String[] params = new String[]{"DynLoadedClass"}; + recTime(); + lnch.main(params); + printValue("Launcher.main", true); + + System.gc(); + } + + /* + ** Create accounting file + */ + private static void createAcct() { + System.out.println ("Directing output to acct file..."); + try { + log = new PrintWriter (new FileWriter("jsynprog.acct"), true); + } catch (IOException ioe) { + ioe.printStackTrace(); + System.err.println("fatal: Cannot create accounting file "); + System.exit(1); + } + + log.println("X\tLWPTime\tCPUTime\tFunction"); + } + + /* + ** Print output in acct file + */ + private static void printValue (String fname, boolean noignore) { + double p_end = Timer(); // Global.Timer(); + double c_end = cTimer(); // Global.cTimer(); + double prog_elapsed = p_end - pstart; + double cpu_elapsed = c_end - cstart; + DecimalFormat format_decimal = new DecimalFormat("0.000"); + + System.out.println("Running " + fname + "; T = " + format_decimal.format(prog_elapsed * 0.000000001) + +" UCPU = " + format_decimal.format(cpu_elapsed * 0.000000001)); + log.print( (noignore == true? "X" : "Y") + + "\t" + format_decimal.format(prog_elapsed * 0.000000001) + "\t" + + format_decimal.format(cpu_elapsed * 0.000000001) + "\t"); + log.println(fname); + } + + /* + ** Record intial times + */ + private static void recTime() { + pstart = Timer(); // Global.Timer(); + cstart = cTimer(); // Global.cTimer(); + } + + /* + ** Load dynamic shared library for JNI + */ + private static void LoadJNILibrary(String[] args) { + + try { + dir_home = (new File(".")).getCanonicalPath(); + } catch (IOException e) { + dir_home = ".."; + } + System.out.println("libpath:"+dir_home); + + // Find which JVM was invoked + String jvm_format = System.getProperty("java.vm.name"); + System.out.println("jvm "+ jvm_format); + + try { + System.out.println("Loading library.... " + dir_home + "/libcloop.so"); + System.load(dir_home + "/libcloop.so"); + } catch (UnsatisfiedLinkError e) { + System.err.println("fatal: Cannot load shared library " + e); + System.exit(1); + } + } + + /* + ** Makes a lot of JNI calls + */ + private static void jni_JavaJavaC(int np, int scale) { + int ret = 0; + int jmax = 10000; + System.out.println("Entering jni_JavaJavaC, scale = " + scale); + double tEnd = Timer() + testtime; + do { + for (int j =0 ; j<jmax; j++) { + ret = JavaJavaC(np, scale); + } + } while (Timer() < tEnd); + } + + public static int javafunc (int scale) { + int jmax = 200*scale; + int imax = 40; + int np = 0; + // System.out.println("Entering javafunc, scale = " + scale); + double tEnd = Timer() + testtime; + do { np = 0; + for (int j =0 ; j<jmax; j++) { + for (int i =0 ; i<imax; i++) { + np = (i%2==0)?np:(np + 1); + } + } + } while (Timer() < tEnd); + return np; + } +} diff --git a/gprofng/testsuite/gprofng.display/mttest/Makefile b/gprofng/testsuite/gprofng.display/mttest/Makefile new file mode 100644 index 0000000..0613ffb --- /dev/null +++ b/gprofng/testsuite/gprofng.display/mttest/Makefile @@ -0,0 +1,41 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +# This Makefile builds the mttest demo code + +# Select a thread flag, BOUND or UNBOUND, and comment the other one +#FLAG = UNBOUND +FLAG = BOUND + +TARGETS = ./mttest +TARGET = ./mttest +ACCT_FILE = mttest.acct + +srcdir = . +include $(srcdir)/../../lib/Makefile.skel + +SRCS = $(srcdir)/gethrtime.c $(srcdir)/mttest.c + +$(TARGET): $(SRCS) + $(CC) $(CFLAGS) -D$(FLAG) -pthread -o $@ $(SRCS) + +$(EXPERIMENT): $(TARGETS) + rm -rf $@ + $(COLLECT) $(COLLECT_FLAGS) -o $@ $(TARGET) $(TARGET_FLAGS) + diff --git a/gprofng/testsuite/gprofng.display/mttest/check_results.pl b/gprofng/testsuite/gprofng.display/mttest/check_results.pl new file mode 100644 index 0000000..2d67e8b --- /dev/null +++ b/gprofng/testsuite/gprofng.display/mttest/check_results.pl @@ -0,0 +1,46 @@ +#!/bin/sh -- # This comment tells perl not to loop! + +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +eval 'exec ${PERL:=/usr/bin/perl} -S $0 ${1+"$@"}' +if 0; + +use strict; +use File::Basename; +require "acct.pm"; + +# XXX This needs better documentation. Or any, really. +# e.g. what does (1, 2, 3) signify? +sub read_acct +{ + my ($fname) = @_; + my(@checkTime, $nlines); + @checkTime = (1, 2, 3); + acct::readAcct($fname, @checkTime); + if (exists $acct::Acct{"*"}) + { + printf "Signal lost\n"; + exit 1; + } +} + +read_acct($ARGV[0]); +acct::read_er_print_out($ARGV[1], -1); +exit acct::createDiff(); diff --git a/gprofng/testsuite/gprofng.display/mttest/gethrtime.c b/gprofng/testsuite/gprofng.display/mttest/gethrtime.c new file mode 100644 index 0000000..a985401 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/mttest/gethrtime.c @@ -0,0 +1,265 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/times.h> +#include <limits.h> + +#if defined(sparc) || defined(__sparcv9) +#define SPARC 1 +#elif defined(__aarch64__) +#define Aarch64 1 +#else +#define Intel 1 +#endif + +/* typedef and function prototypes for hi-resolution timers */ +typedef long long hrtime_t; +#if defined(__cplusplus) +extern "C" +{ +#endif + hrtime_t gethrtime (); + hrtime_t gethrvtime (); + hrtime_t gethrustime (); + hrtime_t gethrpxtime (); + int get_clock_rate (); + int get_ncpus (); + + /* prototype underscore-appended wrappers for Fortran usage */ + hrtime_t gethrtime_ (); + hrtime_t gethrustime_ (); + hrtime_t gethrpxtime_ (); + hrtime_t gethrvtime_ (); + int get_clock_rate_ (); +#if defined(__cplusplus) +} +#endif + +/* =============================================================== */ +/* + * Below this are the get_clock_rate() and get_ncpus() for all OSs and architectures + */ +/* prototypes */ + +/* implementation */ +static int clock_rate = 0; +static int ncpus = 0; +static char msgbuf[1024]; + +int +get_clock_rate (void) +{ + /* Linux version -- read /proc/cpuinfo + * Note the parsing is different on intel-Linux and sparc-Linux + */ + FILE *fp = fopen ("/proc/cpuinfo", "r"); + if (fp != NULL) + { + char temp[1024]; + while (fgets (temp, sizeof (temp), fp) != NULL) + { +#if defined(SPARC) + /* cpu count for SPARC linux -- read from /proc/cpuinfo */ + if (strncmp (temp, "ncpus active", 12) == 0) + { + char *val = strchr (temp, ':'); + ncpus = val ? atol (val + 1) : 0; + } +#endif + + if (clock_rate == 0) + { + /* pick the first line that gives a CPU clock rate */ +#if defined(SPARC) + long long clk; + if (strncmp (temp, "Cpu0ClkTck", 10) == 0) + { + char *val = strchr (temp, ':'); + clk = val ? strtoll (val + 1, NULL, 16) : 0; + clock_rate = (int) (clk / 1000000); + } +#elif defined(Intel) + if (strncmp (temp, "cpu MHz", 7) == 0) + { + char *val = strchr (temp, ':'); + clock_rate = val ? atoi (val + 1) : 0; + } +#endif + } + + /* did we get a clock rate? */ + if (clock_rate != 0) + { +#if defined(SPARC) + /* since we got a cpu count, we can break from the look */ + break; +#endif + } +#if defined(Intel) + /* On intel-Linux, count cpus based on "cpu MHz" lines */ + if (strncmp (temp, "cpu MHz", 7) == 0) + ncpus++; +#endif + } + fclose (fp); + } + + if (clock_rate != 0) + sprintf (msgbuf, "Clock rate = %d MHz (from reading /proc/cpuinfo) %d CPUs\n", clock_rate, ncpus); + + /* did we get a clock rate? */ + if (clock_rate == 0) + { + clock_rate = 1000; + sprintf (msgbuf, "Clock rate = %d MHz (set by default) %d CPUs\n", + clock_rate, ncpus); + } + return clock_rate; +} + +int +get_ncpus (void) +{ + if (clock_rate == 0) + (void) get_clock_rate (); + return ncpus; +} + + +/* gethrpxtime -- per-process user+system CPU time from POSIX times() */ +/* does not include the child user and system CPU time */ +hrtime_t +gethrpxtime (void) +{ + hrtime_t rc = 0; + static int initted = 0; + static hrtime_t ns_per_tick; + if (!initted) + { + static long ticks_per_sec; + ticks_per_sec = sysconf (_SC_CLK_TCK); + ns_per_tick = 1000000000 / ticks_per_sec; + initted = 1; + } + struct tms mytms = {0}; + + clock_t curtick = times (&mytms); + if (curtick == 0) return rc; + rc = (mytms.tms_utime + mytms.tms_stime) * ns_per_tick; + return rc; +} + +/* gethrustime -- per-process user+system CPU time from getrusage() */ +hrtime_t +gethrustime (void) +{ + struct rusage usage; + if (0 == getrusage (RUSAGE_SELF, &usage)) + { + hrtime_t rc = usage.ru_utime.tv_sec /* seconds */ + + usage.ru_stime.tv_sec; + rc = usage.ru_utime.tv_usec /* microseconds */ + + usage.ru_stime.tv_usec + 1000000 * rc; + rc *= 1000; /* nanoseconds */ + return rc; + } + else + return 0; +} + +/* + * Below this are the Linux versions of gethrvtime and gethrtime + */ +hrtime_t +gethrtcputime (void) +{ + struct timespec tp; + hrtime_t rc = 0; + int r = clock_gettime (CLOCK_THREAD_CPUTIME_ID, &tp); + if (r == 0) + rc = ((hrtime_t) tp.tv_sec) * 1000000000 + (hrtime_t) tp.tv_nsec; + return rc; +} + +/* generic gethrvtime -- uses gethrtcputime */ +hrtime_t +gethrvtime () +{ + return gethrtcputime (); +} + +/* + * CLOCK_MONOTONIC + * Clock that cannot be set and represents monotonic time since some + * unspecified starting point. + */ +hrtime_t +gethrtime (void) +{ + struct timespec tp; + hrtime_t rc = 0; + int r = clock_gettime (CLOCK_MONOTONIC, &tp); + if (r == 0) + rc = ((hrtime_t) tp.tv_sec) * 1000000000 + (hrtime_t) tp.tv_nsec; + return rc; +} + +/* + * define underscore-appended wrappers for Fortran usage + */ +hrtime_t +gethrtime_ () +{ + return gethrtime (); +} + +hrtime_t +gethrustime_ () +{ + return gethrustime (); +} + +hrtime_t +gethrpxtime_ () +{ + return gethrpxtime (); +} + +hrtime_t +gethrvtime_ () +{ + return gethrvtime (); +} + +int +get_clock_rate_ () +{ + return get_clock_rate (); +} + +void +init_micro_acct () { } diff --git a/gprofng/testsuite/gprofng.display/mttest/mttest.c b/gprofng/testsuite/gprofng.display/mttest/mttest.c new file mode 100644 index 0000000..5d22af7 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/mttest/mttest.c @@ -0,0 +1,1306 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* mttest -- show threaded use of global and local locks */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/sysinfo.h> +#include <sys/procfs.h> +#include <sys/fcntl.h> +#include <stdio.h> +#include <malloc.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <math.h> +#include <unistd.h> +#include <fcntl.h> +#include <assert.h> +#include <pthread.h> +#include <semaphore.h> +#include <errno.h> + +#ifdef CLONE +#include <linux/sched.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/syscall.h> +#include <sys/mman.h> +#include <linux/futex.h> +#include <linux/unistd.h> +static int CLONE_FLAGS[] = { + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID | CLONE_IO, + CLONE_VM | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID, + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID | CLONE_IO, + CLONE_VM | CLONE_SIGHAND | CLONE_THREAD | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID +}; + +#define CLONE_STACK_SIZE 8388608 +#define CLONE_TLS_SIZE 4096 +#define CLONE_RED_SIZE 4096 + +#endif /* CLONE */ + +typedef int processorid_t; +typedef long long hrtime_t; +typedef struct timespec timespec_t; +extern hrtime_t gethrtime (); +extern hrtime_t gethrvtime (); + +timespec_t * hrt_to_ts (hrtime_t hrt); +static const pthread_mutex_t mutex_initializer = PTHREAD_MUTEX_INITIALIZER; +#ifdef CLONE +#define CLONE_IO 0x80000000 /* Clone io context */ +char *model = "Cloned threads"; +#else +#ifdef BOUND +char *model = "Bound Posix threads"; +#else +char *model = "Unbound Posix threads"; +#endif +#endif + +char *prtime (time_t *); +int get_clock_rate (void); +int get_ncpus (); + +#ifdef SELFTEST +void start_prof (void); +void finish_prof (void); +#endif + +#define _STRUCTURED_PROC 1 +#define TRUE 1 +#define FALSE 0 +#define NUM_OF_THREADS 4 +#define NUM_OF_BLOCKS 4 +#define NUM_OF_RESOURCES 3 +#define MYTIMEOUT 1000000000 +#define MYDBLTIMEOUT ((double) 1000000000.) + +int repeat_count = 1; /* number of times to repeat test */ +int job_index = -1; /* index of selected job, if just one */ +int uniprocessor = 0; /* non-zero if -u specified; causes single processor bind */ +processorid_t cpuid; +processorid_t ocpuid; + +// not a typedef; simplifies analyzer data display output +#define workCtr_t double + +typedef struct workStruct_t +{ + workCtr_t sum_ctr; +} workStruct_t; + +struct Workblk; + +typedef struct Workblk +{ + int index; /* index of this block */ + int strategy; /* specifies type of locking to do */ + int proffail; /* flag set if thread loses interrupts */ +#ifdef CLONE + pid_t tid; /* Linux kernel thread id */ +#else + pthread_t tid; /* thread processing buffer */ +#endif + pthread_mutex_t lock; /* lock for this buffer */ + lwpid_t ilwpid; /* lwp processing buffer (initially) */ + lwpid_t lwpid; /* lwp processing buffer (after sync) */ + + /* timers */ + hrtime_t start; /* buffer fetched, wall clock */ + hrtime_t vstart; /* buffer fetched, CPU timer */ + hrtime_t ready; /* lock acquired (if needed), wall clock */ + hrtime_t vready; /* lock acquired (if needed), CPU timer */ + hrtime_t done; /* work done, wall clock */ + hrtime_t vdone; /* work done, CPU timer */ + hrtime_t compute_ready; /* compute ready, wall clock */ + hrtime_t compute_vready; /* compute ready, CPU timer */ + hrtime_t compute_done; /* compute done, wall clock */ + hrtime_t compute_vdone; /* compute done, CPU timer */ + struct Workblk *next; /* for queue management */ + workStruct_t list[100]; +} Workblk; + +/* lookup table for behavior scripts */ +struct scripttab +{ + char *test_name; + void (*test_func)(Workblk *, struct scripttab *); + char *called_name; + void (*called_func)(workStruct_t *); +}; + +int locktest (); +void resolve_symbols (); +void init_micro_acct (); +void compute_set (volatile workStruct_t *x); +void compute (workStruct_t *x); +void computeA (workStruct_t *x); +void computeB (workStruct_t *x); +void computeC (workStruct_t *x); +void computeD (workStruct_t *x); +void computeE (workStruct_t *x); +void computeF (workStruct_t *x); +void computeG (workStruct_t *x); +void computeH (workStruct_t *x); +void computeI (workStruct_t *x); +void computeJ (workStruct_t *x); +void computeK (workStruct_t *x); +void addone (workCtr_t *x); +void init_arrays (int strat); +void dump_arrays (); +void *do_work (void *v); +void thread_work (); +void nothreads (Workblk *array, struct scripttab *k); +void lock_none (Workblk *array, struct scripttab *k); +void cache_trash (Workblk *array, struct scripttab *k); +void lock_global (Workblk *array, struct scripttab *k); +void trylock_global (Workblk *array, struct scripttab *k); +void lock_local (Workblk *array, struct scripttab *k); +void calladd (Workblk *array, struct scripttab *k); +void cond_global (Workblk *array, struct scripttab *k); +void cond_timeout_global (Workblk *array, struct scripttab *k); +void sema_global (Workblk *array, struct scripttab *k); +void read_write (Workblk *array, struct scripttab *k); +void s5sem (Workblk *array, struct scripttab *k); +FILE *open_output (char *filename); +int close_file (FILE *f); +void scale_init (int argcc, char **argvv); +void +Print_Usage (int); + +struct scripttab scripttab[] = { +#ifdef CLONE + {"nothreads", nothreads, "compute", compute}, + {"lock_none", lock_none, "computeA", computeA}, + {"cache_trash", cache_trash, "computeB", computeB}, + {"calladd", calladd, "computeF", computeF}, + {"sema_global", sema_global, "computeI", computeI}, +#else + {"nothreads", nothreads, "compute", compute}, + {"cond_timeout_global", cond_timeout_global, "computeH", computeH}, + {"lock_none", lock_none, "computeA", computeA}, + {"cache_trash", cache_trash, "computeB", computeB}, + {"lock_global", lock_global, "computeC", computeC}, + {"trylock_global", trylock_global, "computeD", computeD}, + {"lock_local", lock_local, "computeE", computeE}, + {"calladd", calladd, "computeF", computeF}, + {"sema_global", sema_global, "computeI", computeI}, + {"cond_global", cond_global, "computeG", computeG}, +#endif + {NULL, NULL, NULL, NULL} +}; + +static pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t global_cond_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t global_cond_lock2 = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER; +static timespec_t time_out; +static sem_t global_sema_lock; /* dynamically initted */ +static int s5_sema_id; +static int global_cond_flag = TRUE; +static int count = NUM_OF_RESOURCES; + +/* an array of workStruct_ts that is contiguous */ +workStruct_t *element; + +typedef struct +{ + int size; + Workblk *arrays; +} Head; + +int nthreads = NUM_OF_THREADS; +int narrays = NUM_OF_BLOCKS; +static Head head; +char *name; +FILE *fid; + +#ifdef CLONE +static sem_t fetch_sema_lock; +static pid_t *tid; +static void *stack_space[NUM_OF_THREADS]; +static void *stack[NUM_OF_THREADS]; +int stack_size = CLONE_STACK_SIZE; +#else +static pthread_t *tid; +#endif +pthread_attr_t attr; + +int +main (int argc, char **argv, char **envp) +{ + int i; + scale_init (argc, argv); + +#define ALIGNMENTOFFSET 2 /* adjust alignment */ + i = sizeof (workStruct_t) * (narrays + ALIGNMENTOFFSET); + element = memalign (64, i); + if (element == NULL) + { + perror ("calloc( narrays, sizeof(workStruct_t) )"); + exit (1); + } + compute_set (element); + memset (element, 0, i); + element += ALIGNMENTOFFSET; + +#ifdef SELFTEST + start_prof (); +#endif + fid = open_output ("mttest.acct"); + if (job_index == -1) + i = (sizeof (scripttab) / sizeof ( struct scripttab) - 1); + else + i = 1; + fprintf (fid, "Number of tests: %d Repeat count: %d\n", i, repeat_count); + fprintf (fid, "MHz: %d\n", get_clock_rate ()); + fprintf (fid, "X Incl. Total Incl. CPU Incl. Sync. Wait Name (%s)\n", + model); + fprintf (fid, "X %7.3f %7.3f %7.3f %s\n", + 0.0, 0.0, 0.0, "<Unknown>"); + fflush (fid); + name = strdup (argv[0]); + init_micro_acct (); + pthread_attr_init (&attr); + +#ifdef BOUND + pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); +#endif + sem_init (&global_sema_lock, 0, count); +#ifdef CLONE + sem_init (&fetch_sema_lock, 0, 1); + for (i = 0; i < nthreads; i++) + { + stack_space[i] = mmap (NULL, stack_size, PROT_READ | PROT_WRITE + | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); + if ((void*) - 1 == stack_space[i]) + { + fprintf (stderr, "Error: mmap returned -1\n"); + exit (1); + } + mprotect (stack_space[i], CLONE_RED_SIZE, PROT_NONE); + stack[i] = (char*) (stack_space[i]) + stack_size - CLONE_TLS_SIZE; // stack grows back + } +#endif + + resolve_symbols (); + i = locktest (); + close_file (fid); + +#ifdef SELFTEST + finish_prof (); +#endif + return 0; +} + +Workblk *in_queue = NULL; +Workblk *in_queue_last = NULL; + +pthread_mutex_t queue_lock; + +void +queue_work (Workblk * w) +{ + if (in_queue == NULL) + { + in_queue = w; + in_queue_last = w; + } + else + { + in_queue_last->next = w; + in_queue_last = w; + } +} + +Workblk * +fetch_work () +{ + /* acquire the queue lock */ +#ifdef CLONE + sem_wait (&fetch_sema_lock); +#else + pthread_mutex_lock (&queue_lock); +#endif + + /* get the next block */ + Workblk *w = in_queue; + if (w != NULL) + { + in_queue = w->next; + w->next = NULL; + if (in_queue == NULL) + in_queue_last = NULL; + } +#ifdef CLONE + sem_post (&fetch_sema_lock); +#else + pthread_mutex_unlock (&queue_lock); +#endif + + /* return the block */ + return w; +} + +int +locktest () +{ + int i; + Workblk *array; + struct scripttab *k; + hrtime_t start; + hrtime_t vstart; + hrtime_t end; + hrtime_t vend; + struct timeval ttime; + time_t secs; + + head.size = narrays; + head.arrays = (Workblk *) calloc (narrays, sizeof (Workblk)); + + for (i = 0, array = head.arrays; i < narrays; i++, array++) + array->index = i; + + printf ("%s: number of %s = %d, number of blocks = %d, repeat %d times %s\n", + name, model, nthreads, narrays, repeat_count, + (uniprocessor == 0 ? "" : "[single CPU]")); +#ifdef CLONE + tid = (pid_t *) calloc (nthreads*repeat_count, sizeof (pid_t)); +#else + tid = (pthread_t *) calloc (nthreads*repeat_count, sizeof (pthread_t)); +#endif + for (count = 0; count < repeat_count; count++) + { + (void) gettimeofday (&ttime, NULL); + secs = (time_t) ttime.tv_sec; + printf ("Iteration %d, starting %s\n", count + 1, prtime (&secs)); + if (job_index == -1) + { + for (i = 0;; i++) + { + k = &scripttab[i]; + if (k->test_name == NULL) + break; + + printf ("begin thread_work, %s\n", k->test_name); + init_arrays (i); + start = gethrtime (); + vstart = gethrvtime (); + + if (strcmp (k->test_name, "nothreads") == 0) + { + /* the "nothreads" task is special-cased to run in the main thread */ + int one_thread = 1; + do_work (&one_thread); + } + else if (nthreads == 1) + { + int one_thread = 1; + do_work (&one_thread); + } + else + thread_work (); + end = gethrtime (); + vend = gethrvtime (); + dump_arrays (end - start, vend - vstart, i); + } + } + else + { + k = &scripttab[job_index]; + if (k->test_name == NULL) + break; + + printf ("begin thread_work, %s\n", k->test_name); + init_arrays (job_index); + start = gethrtime (); + vstart = gethrvtime (); + if (strcmp (k->test_name, "nothreads") == 0) + { + /* first one is special-cased to run in 1 thread */ + int one_thread = 1; + do_work (&one_thread); + } + else if (nthreads == 1) + do_work (NULL); + else + thread_work (); + end = gethrtime (); + vend = gethrvtime (); + dump_arrays (end - start, vend - vstart, job_index); + } + } + + /* we're done, return */ + return (0); +} + +void +init_arrays (int strat) +{ + int i; + Workblk *array; + for (i = 0, array = head.arrays; i < narrays; i++, array++) + { + bzero (array, sizeof (Workblk)); + array->index = i; + array->strategy = strat; + queue_work (array); + } +} + +void +dump_arrays (hrtime_t real, hrtime_t cpu, int case_index) +{ + int i; + double t1, t2, t3, t4, t5, t6, t7, t8; + Workblk *array; + struct scripttab *k; + double sumtotal = 0.; + double sumCPU = 0.; + double sumlock = 0.; + double sumCompTotal = 0.; + double sumCompCPU = 0.; + int proffail = 0; + printf (" real real real CPU\n"); + printf ("idx (t id) total lock crunch crunch\n"); + for (i = 0, array = head.arrays; i < narrays; i++, array++) + { + /* check to see if data lost for this block */ + /* set flag to disable the comparison */ + /* convert times to seconds */ + t1 = ((double) array->done - array->start) / MYDBLTIMEOUT; + t2 = ((double) array->vdone - array->vstart) / MYDBLTIMEOUT; + t3 = ((double) array->ready - array->start) / MYDBLTIMEOUT; + t4 = ((double) array->vready - array->vstart) / MYDBLTIMEOUT; + t5 = ((double) array->done - array->ready) / MYDBLTIMEOUT; + t6 = ((double) array->vdone - array->vready) / MYDBLTIMEOUT; + t7 = ((double) array->compute_done - array->compute_ready) / MYDBLTIMEOUT; + t8 = ((double) array->compute_vdone - array->compute_vready) + / MYDBLTIMEOUT; + + if (array->proffail != 0) + proffail = 1; + sumtotal = sumtotal + t1; /* incl. total time */ + sumlock = sumlock + t3; /* incl. sync. wait time */ +#ifdef BOUND + /* NOTE: + * for bound threads, sumCPU includes the synchronization + * CPU time; for unbound it does not + */ + sumCPU = sumCPU + t2; /* test incl. CPU time */ +#else + sumCPU = sumCPU + t6; /* test incl. CPU time */ +#endif + sumCompTotal = sumCompTotal + t7; /* compute incl. totaltime */ + sumCompCPU = sumCompCPU + t8; /* compute incl. CPU time */ + printf ("#%2d (t%3ld, il%3d, l%3d) %10.6f %10.6f %10.6f %10.6f%s\n", + array->index, array->tid, array->ilwpid, array->lwpid, t1, t3, + t5, t6, array->proffail == 0 ? "" : " *"); + if (t4 == 0) printf ("t4 == 0\n"); + assert (array->lwpid > 0); +#if defined(BOUND) + assert (array->lwpid == array->ilwpid); +#endif + } + + k = &scripttab[case_index]; + + printf ("%-25s %10.6f %10.6f %-9s %10.6f\n", k->test_name, sumtotal, + sumlock, k->called_name, sumCPU); + printf ("main %10.6f\n\n", + (double) real / MYDBLTIMEOUT); + + /* write accounting record for task */ + fprintf (fid, "X %7.3f %7.3f %7.3f %s%s\n", + sumtotal, sumCPU, sumlock, k->test_name, + (proffail == 0 ? "" : " *")); + /* write accounting record for task's compute function */ + fprintf (fid, "X %7.3f %7.3f 0. %s%s\n", + sumCompTotal, sumCompCPU, k->called_name, + (proffail == 0 ? "" : " *")); + fflush (fid); + fflush (stdout); + +} + +void +thread_work () +{ + int i; +#ifdef CLONE + pid_t ctid[NUM_OF_THREADS]; + for (i = 0; i < nthreads; i++) + ctid[i] = -1; +#endif + + /* create nthreads threads, having each start at do_work */ + for (i = 0; i < nthreads; i++) + { + int retval; +#ifdef BOUND + retval = pthread_create (&(tid[i]), &attr, do_work, 0); +#endif +#ifdef UNBOUND + retval = pthread_create (&(tid[i]), 0, do_work, 0); +#endif +#ifdef CLONE + tid[i] = retval = clone ((int (*)(void*))do_work, stack[i], + CLONE_FLAGS[i % sizeof (CLONE_FLAGS)], NULL, + &(ctid[i]), NULL, &(ctid[i])); + if (retval < 0) + { + perror ("Oops, clone failed"); + exit (1); + } +#else + if (retval != 0) + { + perror ("Oops, thr_create failed"); + exit (1); + } +#endif + } + + /* wait for all threads to complete their work and join */ + for (i = 0; i < nthreads; i++) + { +#ifdef CLONE + int counter = 0; + while (ctid[i] == -1) + counter++; + while (ctid[i] != 0) + syscall (__NR_futex, &(ctid[i]), FUTEX_WAIT, tid[i], NULL); +#else + pthread_join (tid[i], 0); +#endif + } +#ifdef CLONE + for (i = 0; i < nthreads / 2; i++) + { + int status; + waitpid (tid[i], &status, __WALL); + } +#endif +} + +/* do_work: process array's data with locking, based on array->strategy */ +void * +do_work (void *v) +{ + Workblk *array; + struct scripttab *k; + int i; + volatile double x; + +#ifdef CLONE + pid_t mytid = syscall (__NR_gettid); +#else + pthread_t mytid = pthread_self (); +#endif + + /* delay to ensure that a tick passes, so that the + * first profile packet doesn't show the thread startup time + * attributed to the accounting functions + */ + x = 0; + for (i = 0; i < 2000000; i++) + x = x + 1.0; + + for (;;) + { + /* fetch a workblk */ + array = fetch_work (); + if (array == NULL) /* we're done */ + break; + array->lock = mutex_initializer; + array->proffail = 0; + array->tid = mytid; + array->ilwpid = getpid () /* pthread_self()*/; + + array->lwpid = -1; /* initialize to inappropriate value */ + array->start = gethrtime (); + array->vstart = gethrvtime (); + + k = &scripttab[array->strategy]; + (k->test_func)(array, k); + + array->done = gethrtime (); + array->vdone = gethrvtime (); + array->lwpid = getpid () /* pthread_self()*/; + +#if defined(BOUND) + assert (array->lwpid == array->ilwpid); +#endif + } + +#ifdef CLONE + if (v == NULL) + syscall (__NR_exit); +#endif + return NULL; +} + +/* nothreads: process array's data with no locking; called without threads */ +void +nothreads (Workblk *array, struct scripttab *k) +{ + array->ready = gethrtime (); + array->vready = gethrvtime (); + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + +} + +/* lock_none: process array's data with no locking */ +void +lock_none (Workblk *array, struct scripttab *k) +{ + array->ready = array->start; + array->vready = array->vstart; + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + +} + +/* cache_trash_even: + * called for even numbered l1 cache lines + */ +void +cache_trash_even (Workblk *array, struct scripttab *k) +{ + /* use a datum that will share a cache line with others */ + (k->called_func)(&element[array->index]); +} + +/* cache_trash_odd: + * called for odd numbered l1 cache lines + */ +void +cache_trash_odd (Workblk *array, struct scripttab *k) +{ + /* use a datum that will share a cache line with others */ + (k->called_func)(&element[array->index]); +} + +/* cache_trash: multiple threads refer to adjacent words, + * causing false sharing of cache lines, and trashing + */ +void +cache_trash (Workblk *array, struct scripttab *k) +{ + array->ready = array->start; + array->vready = array->vstart; + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* use a datum that will share a cache line with others */ + if ((unsigned long) (&element[array->index]) / 32 & 1) + cache_trash_odd (array, k); + else + cache_trash_even (array, k); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); +} + +/* lock_global: use a global lock to process array's data */ +void +lock_global (Workblk *array, struct scripttab *k) +{ + /* acquire the global lock */ + pthread_mutex_lock (&global_lock); + + array->ready = gethrtime (); + array->vready = gethrvtime (); + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the global lock */ + pthread_mutex_unlock (&global_lock); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* trylock_global: busy-wait on a global lock to process array's data */ +void +trylock_global (Workblk *array, struct scripttab *k) +{ + int ret; + + /* set ready before starting, since this is a busy wait */ + array->ready = gethrtime (); + array->vready = gethrvtime (); + + /* busy wait to acquire the global lock */ + do + { + ret = pthread_mutex_trylock (&global_lock); + } + while (ret == EBUSY); + array->compute_ready = gethrtime (); + array->compute_vready = gethrvtime (); + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the global lock */ + pthread_mutex_unlock (&global_lock); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* lock_local: use a local lock to process array's data */ +void +lock_local (Workblk *array, struct scripttab *k) +{ + /* acquire the local lock */ + pthread_mutex_lock (&(array->lock)); + array->ready = gethrtime (); + array->vready = gethrvtime (); + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the local lock */ + pthread_mutex_unlock (&array->lock); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* cond_global: use a global condition variable to process array's data */ +void +cond_global (Workblk *array, struct scripttab *k) +{ + /* acquire the global condition lock */ + pthread_mutex_lock (&global_cond_lock); + + /* check to see if the condition flag is true, If not then wait + for that condition flag to become true. */ + while (global_cond_flag != TRUE) + pthread_cond_wait (&global_cond, &global_cond_lock); + /* Now, condition is true, and we have the global_cond_lock */ + + /* set the condition flag to be FALSE, so when a new thread + * is created, it should wait till this one is done. + */ + global_cond_flag = FALSE; + + /* free the global_cond_lock and acquire the global lock */ + pthread_mutex_unlock (&global_cond_lock); + pthread_mutex_lock (&global_lock); + + array->ready = gethrtime (); + array->vready = gethrvtime (); + + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the global lock */ + pthread_mutex_unlock (&global_lock); + + /* now set the condition, and signal any other threads */ + pthread_mutex_lock (&global_cond_lock); + + global_cond_flag = TRUE; + pthread_cond_signal (&global_cond); + pthread_mutex_unlock (&global_cond_lock); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* cond_timeout_global: use a global condition time wait variable to + process array's data */ +void +cond_timeout_global (Workblk *array, struct scripttab *k) +{ + int err; + struct timeval current_time; + + /* acquire the global condition lock */ + pthread_mutex_lock (&global_cond_lock); + gettimeofday (¤t_time, NULL); + time_out.tv_sec = current_time.tv_sec; + time_out.tv_nsec = current_time.tv_usec * 1000; + + /* check to see if the condition flag is true, If not then wait + * for that condition flag to become true + */ + + while (global_cond_flag != TRUE) + { + /* add MYTIMEOUT to current time for timeout */ + time_out.tv_nsec += MYTIMEOUT; + while (time_out.tv_nsec > 1000000000) + { + time_out.tv_nsec -= 1000000000; + time_out.tv_sec++; + } + err = pthread_cond_timedwait (&global_cond, &global_cond_lock, &time_out); + if (err == 0) + break; + } + /* Now, condition is true, and we have the global_cond_lock */ + + pthread_mutex_unlock (&global_cond_lock); + + pthread_mutex_lock (&global_cond_lock2); + global_cond_flag = FALSE; + pthread_mutex_unlock (&global_cond_lock2); + + /* acquire the global lock */ + pthread_mutex_lock (&global_lock); + + array->ready = gethrtime (); + array->vready = gethrvtime (); + + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the global lock */ + pthread_mutex_unlock (&global_lock); + + /* now set the condition, and signal any other threads */ + pthread_mutex_lock (&global_cond_lock2); + + global_cond_flag = TRUE; + pthread_cond_signal (&global_cond); + pthread_mutex_unlock (&global_cond_lock2); + + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* read_write: use a global Reader/Writer lock to process array's data */ +void +read_write (Workblk *array, struct scripttab *k) +{ + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* sema_global: use a global semaphore to process array's data */ +void +sema_global (Workblk *array, struct scripttab *k) +{ + sem_wait (&global_sema_lock); + array->ready = gethrtime (); + array->vready = gethrvtime (); + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + sem_post (&global_sema_lock); + + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* s5sema: use a global UNIX System V semaphore to process array's data */ +void +s5sem (Workblk *array, struct scripttab *k) +{ + static struct sembuf op_wait[] = { + { 0, -1, IPC_NOWAIT} + }; + static struct sembuf op_post[] = { + { 0, 1, 0} + }; + int sema_val; + + /* set ready before starting, since this is a busy wait */ + array->ready = gethrtime (); + array->vready = gethrvtime (); + do + { + sema_val = semop (s5_sema_id, op_wait, 1); + } + while (sema_val == -1); + + array->compute_ready = gethrtime (); + array->compute_vready = gethrvtime (); + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + if (semop (s5_sema_id, op_post, 1) == -1) + perror ("semop: post"); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* lock_local: use a local lock to process array's data */ +void +calladd (Workblk *array, struct scripttab *k) +{ + array->ready = array->start; + array->vready = array->vstart; + array->compute_ready = array->ready; + array->compute_vready = array->vready; + (k->called_func)(&array->list[0]); + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); +} + +/* compute*: several copies, each burns cpu time, incrementing a workStruct_t */ +static long long loop_count = 80000000; + +void +compute_set (volatile workStruct_t *x) +{ + double testtime = 3.0; + char *s = getenv ("SP_COLLECTOR_TEST_TIMER"); + if (s) + { + testtime = atof (s); + if (testtime < 1.0) + testtime = 1.0; + } + hrtime_t t = gethrtime (); + x->sum_ctr = 0; + loop_count = 10000; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; + t = gethrtime () - t; + loop_count *= testtime * 1e9 / t; + printf ("compute_set: loop_count=%lld\n", loop_count); +} + +void +compute (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeA (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeB (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeC (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeD (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeE (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +/* note that this one is different from the others, in that it calls + * a function to do the add + */ +void +computeF (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + addone (&x->sum_ctr); +} + +void +computeG (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeH (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeI (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeJ (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeK (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +addone (workCtr_t *x) +{ + *x = *x + 1.0; +} + +FILE * +open_output (char *filename) +{ + errno = 0; + FILE *f = fopen (filename, "w"); + if (f == NULL) + fprintf (stderr, "Open of %s for output failed: %s\n", + filename, strerror (errno)); + return f; +} + +int +close_file (FILE *f) +{ + if (f == NULL) + return 0; + errno = 0; + int s = fclose (f); + if (s == EOF) + perror ("Close failed"); + return s; +} + +void +scale_init (int argcc, char **argvv) +{ + int num; + int ii; + char *p; + struct scripttab *kk; + + if (argcc >= 2) /* run mttest with options */ + { + for (int i = 1; i < argcc; i++) + { + int j = i; + if (argvv[i][0] != '-') + Print_Usage (1); + if (argvv[i][1] == 'h' || argvv[i][1] == 'H') + Print_Usage (0); + if (argvv[i][1] == 'u') + { + uniprocessor++; + continue; + } + if (strlen (argvv[i]) == 2) + { + /* argument has blank separating key and number */ + j++; + if (argcc > j) + { + p = argvv[j]; + num = atoi (p); + } + else + Print_Usage (1); + } + else + { + /* argument has no blank separating key and number */ + p = argvv[i] + 2; + num = atoi (p); + } + + switch (argvv[i][1]) + { + case 't': + case 'T': + nthreads = num; + break; + case 'b': + case 'B': + narrays = num; + break; + case 'r': + case 'R': + repeat_count = num; + break; + case 'j': + case 'J': + /* argument is a job name; p points to string */ + for (ii = 0;; ii++) + { + kk = &scripttab[ii]; + if (kk->test_name == NULL) /* Oops, name not found */ + Print_Usage (2); + if (strcmp (kk->test_name, p) == 0) /* found it */ + break; + } + job_index = ii; + break; + default: + Print_Usage (1); + } + i = j; + } + } +} + +void +Print_Usage (int error) +{ + if (error == 1) + printf ("\nError: Incorrect option\n"); + else if (error == 2) + printf ("\nError: job name not found\n"); + printf ("Usage: mttest [-t num_of_threads] [-b num_of_blocks] " + "[-R repeat_count] [-u] [-j job_name]\n"); + printf (" -u implies binding all LWPs to one CPU with processor_bind\n"); + printf (" job_name is one of:\n"); + for (int ii = 0;; ii++) + { + struct scripttab *kk = &scripttab[ii]; + if (kk->test_name == NULL) + break; + printf ("\t%s\n", kk->test_name); + } + printf (" if job_name is omitted, each will be run in turn\n"); + exit (-1); +} + +void +resolve_symbols () +{ + global_cond_flag = TRUE; + pthread_mutex_lock (&queue_lock); + pthread_mutex_trylock (&queue_lock); + pthread_mutex_unlock (&queue_lock); + sem_post (&global_sema_lock); + sem_wait (&global_sema_lock); +#ifdef CLONE + sem_post (&fetch_sema_lock); + sem_wait (&fetch_sema_lock); +#endif +} + +/* prtime (ttime) + * returns a pointer to a static string in the form: + * Thu 01 Jan 00 00:00:00\0 + * 01234567890122345678901234 + * ttime is a pointer to a UNIX time in seconds since epoch + * library routine localtime() is used + */ +char * +prtime (time_t *ttime) +{ + static char *days[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static char *months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + static char cvbuf[26]; + + /* get the date and time */ + struct tm *tp = localtime (ttime); + + /* convert to string */ + sprintf (cvbuf, "%3s %02d %s %02d %02d:%02d:%02d", + days[tp->tm_wday], tp->tm_mday, months[tp->tm_mon], + tp->tm_year % 100, tp->tm_hour, tp->tm_min, tp->tm_sec); + return cvbuf; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/Makefile b/gprofng/testsuite/gprofng.display/synprog/Makefile new file mode 100644 index 0000000..7c50ea2 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/Makefile @@ -0,0 +1,66 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +TARGETS = synprog so_syn.so so_syx.so +TARGET = ./synprog +ACCT_FILE = synprog.acct + +srcdir = . +include $(srcdir)/../../lib/Makefile.skel + +SRCS = \ + $(srcdir)/../mttest/gethrtime.c \ + $(srcdir)/synprog.c \ + $(srcdir)/callso.c \ + $(srcdir)/callsx.c \ + $(srcdir)/endcases.c \ + $(srcdir)/fitos.c \ + $(srcdir)/iosyn.c \ + $(srcdir)/pagethrash.c \ + $(srcdir)/stopwatch.c \ + $(NULL) + +HDRS= \ + $(srcdir)/inc_body.h \ + $(srcdir)/inc_brace.h \ + $(srcdir)/inc_entry.h \ + $(srcdir)/inc_exit.h \ + $(srcdir)/inc_func.h \ + $(srcdir)/inc_inline.h \ + $(srcdir)/inc_macro.h \ + $(srcdir)/stopwatch.h \ + $(NULL) + + +$(TARGET): $(SRCS) $(HDRS) so_syx.so so_syn.so + @echo " ---- Build: $@ -----" + $(CC) $(CFLAGS) -o $@ $(SRCS) -ldl -lc -lrt + +so_syx.so: $(srcdir)/so_syx.c + @echo " ---- Build: $@ -----" + $(CC) $(CFLAGS) $(SHAREDOPT) -o $@ $(srcdir)/so_syx.c -lc + +so_syn.so: $(srcdir)/so_syn.c + @echo " ---- Build: $@ -----" + $(CC) $(CFLAGS) $(SHAREDOPT) -o $@ $(srcdir)/so_syn.c -lc + +$(EXPERIMENT): $(TARGETS) + rm -rf $@ + $(COLLECT) $(COLLECT_FLAGS) -o $@ $(TARGET) $(TARGET_FLAGS) + diff --git a/gprofng/testsuite/gprofng.display/synprog/callso.c b/gprofng/testsuite/gprofng.display/synprog/callso.c new file mode 100644 index 0000000..6ff5c99 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/callso.c @@ -0,0 +1,152 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <errno.h> +#include <string.h> +#include "stopwatch.h" + + +#define DYNSOROUTINE "so_cputime" +#define DYNSONAME "./so_syn.so" + +/* callso -- dynamically link a shared object, and call a routine in it */ + +#ifndef NONSHARED + +static void *so_object = NULL; +static void closeso (void); + +int +callso (int k) +{ + int i; + char buf[1024]; + char *p; + char *q = DYNSONAME; + int errnum; + + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of callso", NULL); + + /* see if already linked */ + if (so_object != NULL) + { + fprintf (stderr, "Execution error -- callso: so_object already linked\n"); + return 0; + } + + /* open the dynamic shared object */ + so_object = NULL; + while (so_object == NULL) + { + so_object = dlopen (DYNSONAME, RTLD_NOW); + if (so_object != NULL) + break; + p = dlerror (); + if (q == NULL) + q = "DYNSONAME"; + if (p == NULL) + p = "dlerror() returns NULL"; + errnum = errno; + if (errnum == EINTR) + continue; /* retry */ + else + { + fprintf (stderr, "Execution error -- callso: dlopen of %s failed--%s, errno=%d (%s)\n", + q, p, errnum, strerror (errnum)); + return (0); + } + } + + /* look up the routine name in it */ + int (*so_routine)() = (int (*)())dlsym (so_object, DYNSOROUTINE); + if (so_routine == NULL) + { + fprintf (stderr, "Execution error -- callso: dlsym %s not found\n", + DYNSOROUTINE); + return (0); + } + + /* invoke the routine */ + long long count = 0; + do + { + i = (*so_routine)(); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + closeso (); + sprintf (buf, "end of callso, %s returned %d", DYNSOROUTINE, i); + wlog (buf, NULL); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog ((gethrtime () - start), (gethrvtime () - vstart), "callso", NULL); + return 0; +} + +/* closeso -- close a DSO */ +void +closeso (void) +{ + /* Log the event */ + wlog ("start of closeso", NULL); + + /* ensure already linked */ + if (so_object == NULL) + { + fprintf (stderr, "Execution error -- closeso: so_object not linked\n"); + return; + } + + /* close the dynamic shared object */ + int rc = dlclose (so_object); + if (rc != 0) + { + fprintf (stderr, "Execution error -- closeso: dlclose() failed--%s\n", + dlerror ()); + return; + } + + /* clear the pointer */ + so_object = NULL; + wlog ("end of closeso", NULL); + return; +} + +#else /* NONSHARED */ + +int +callso (int i) +{ + return 0; +} + +void +closeso (void) +{ + return; +} +#endif /* NONSHARED */ diff --git a/gprofng/testsuite/gprofng.display/synprog/callsx.c b/gprofng/testsuite/gprofng.display/synprog/callsx.c new file mode 100644 index 0000000..5adcf90 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/callsx.c @@ -0,0 +1,152 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <errno.h> +#include <string.h> +#include "stopwatch.h" + + +#define DYNSOROUTINE "sx_cputime" +#define DYNSONAME "./so_syx.so" + +/* callsx -- dynamically link a shared object, and call a routine in it */ + +#ifndef NONSHARED + +static void *sx_object = NULL; +static void closesx (void); + +int +callsx (int k) +{ + int i; + char buf[1024]; + char *p; + char *q = DYNSONAME; + int errnum; + + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of callsx", NULL); + + /* see if already linked */ + if (sx_object != NULL) + { + fprintf (stderr, "Execution error -- callsx: sx_object already linked\n"); + return 0; + } + + /* open the dynamic shared object */ + /* open the dynamic shared object */ + sx_object = NULL; + while (sx_object == NULL) + { + sx_object = dlopen (DYNSONAME, RTLD_NOW); + if (sx_object != NULL) + break; + p = dlerror (); + if (q == NULL) q = "DYNSONAME"; + if (p == NULL) p = "dlerror() returns NULL"; + errnum = errno; + if (errnum == EINTR) + continue; /* retry */ + else + { + fprintf (stderr, "Execution error -- callso: dlopen of %s failed--%s, errno=%d (%s)\n", + q, p, errnum, strerror (errnum)); + return (0); + } + } + + /* look up the routine name in it */ + int (*sx_routine)() = (int (*)())dlsym (sx_object, DYNSOROUTINE); + if (sx_routine == NULL) + { + fprintf (stderr, "Execution error -- callsx: dlsym %s not found\n", + DYNSOROUTINE); + return (0); + } + + /* invoke the routine */ + long long count = 0; + do + { + i = (*sx_routine)(); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + closesx (); + sprintf (buf, "end of callsx, %s returned %d", DYNSOROUTINE, i); + wlog (buf, NULL); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog ((gethrtime () - start), (gethrvtime () - vstart), "callsx", NULL); + return 0; +} + +/* closesx -- close a DSO */ +void +closesx (void) +{ + /* Log the event */ + wlog ("start of closesx", NULL); + + /* ensure already linked */ + if (sx_object == NULL) + { + fprintf (stderr, "Execution error -- closesx: sx_object not linked\n"); + return; + } + +#if 0 + /* close the dynamic shared object */ + rc = dlclose (sx_object); + if (rc != 0) + { + fprintf (stderr, "Execution error -- closesx: dlclose() failed--%s\n", + dlerror ()); + return; + } + /* clear the pointer */ + sx_object = NULL; +#endif + wlog ("end of closesx", NULL); + return; +} + +#else /* NONSHARED */ + +int +callsx (int i) +{ + return 0; +} + +void +closesx (void) +{ + return; +} +#endif /* NONSHARED */ diff --git a/gprofng/testsuite/gprofng.display/synprog/check_results.pl b/gprofng/testsuite/gprofng.display/synprog/check_results.pl new file mode 100755 index 0000000..e77d074 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/check_results.pl @@ -0,0 +1,40 @@ +#!/bin/sh -- # This comment tells perl not to loop! + +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +eval 'exec ${PERL:=/usr/dist/exe/perl} -S $0 ${1+"$@"}' +if 0; + +use strict; +require "acct.pm"; + +my(@checkTime); + +if ("$ENV{DA_io}" eq "on") { + @checkTime = (); + acct::readAcct("synprog.acct2", @checkTime); +} else { + @checkTime = (1, 2); + acct::readAcct("synprog.acct", @checkTime); +} + +acct::read_er_print_out($ARGV[1], -1); +acct::createDiff(); +exit acct::set_retVal(0); diff --git a/gprofng/testsuite/gprofng.display/synprog/endcases.c b/gprofng/testsuite/gprofng.display/synprog/endcases.c new file mode 100644 index 0000000..5f9fb43 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/endcases.c @@ -0,0 +1,208 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include "stopwatch.h" + +/* endcases - examine some wierd endcases of programming style + * test cases for inlined code, macros, #included code, ... + */ +void inc_func (int); +void inc_brace (int); +void inc_body (int); +void inc_entry (int); +void inc_middle (int); +void inc_exit (int); +void macro_code (int); +void ext_macro_code (int); +void xinline_code (int); +static void s_inline_code (int); +void ext_inline_code (int); + +#ifndef NO_INLINE +void xinline_code () __attribute__ ((always_inline)); +void s_inline_code () __attribute__ ((always_inline)); +#endif + +#include "inc_inline.h" + +int n; +int x1M = 1000000; +int x2M = 2000000; +int x8M = 8000000; + +/* define a macro that burns CPU time */ +#define burncpu(nn) \ + x = 0; \ + for (j = 0; j < (nn * x8M); j++) { \ + x = x + 1; \ + } + +int +endcases (int n) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of endcases", NULL); + + if (n == 0) + n = 4; + + long long count = 0; + do + { + /* test inlines */ + xinline_code (n); + s_inline_code (n); + ext_inline_code (n); + + /* test macros */ + macro_code (n); + ext_macro_code (n); + + /* test various cases of #include'd code */ + inc_func (n); + inc_brace (n); + inc_body (n); + inc_entry (n); + inc_middle (n); + inc_exit (n); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, gethrvtime () - vstart, "endcases", NULL); + return 0; +} + +/* spend time in a inline locally-defined */ +void +xinline_code (int n) +{ + int jmax = n * x8M; + volatile long x = 0; + for (int j = 0; j < jmax; j++) + x = x + 1; + if (x < 0.0) + printf ("ERROR: inline_code(): x < 0 (x=%ld)\n", x); +} + +/* spend time in a static inline locally-defined */ +static void +s_inline_code (int n) +{ + int jmax = n * x8M; + volatile long x = 0; + for (int j = 0; j < jmax; j++) + x = x + 1; + if (x < 0.0) + printf ("ERROR: s_inline_code(): x < 0 (x=%ld)\n", x); +} + +/* spend time in a macro locally-defined */ +void +macro_code (int n) +{ + int j; + volatile long x = 0; + burncpu (n); + if (x < 0.0) + printf ("ERROR: macro_code(): x < 0 (x=%ld)\n", x); +} + +/* spend time in a macro externally-defined */ +#include "inc_macro.h" + +void +ext_macro_code (int n) +{ + volatile long x = 0; + int j; + extburncpu (n); + if (x < 0.0) + printf ("ERROR: ext_macro_code(): x < 0 (x=%ld)\n", x); +} + +#include "inc_func.h" + +void +inc_brace (int n) +#include "inc_brace.h" + +void +inc_body (int n) { +#include "inc_body.h" +} + +void +inc_entry (int n) +#include "inc_entry.h" +{ + int jmax = n * x8M; + volatile float x = 0.0; + for (int j = 0; j < jmax; j++) + x = x + 1.0; + if (x < 0.0) + printf ("ERROR: inc_entry(): x < 0 (x=%f)\n", x); +} +} + +void +inc_middle (int n) +{ + { + int jmax = n * x8M; + volatile float x = 0.0; + for (int j = 0; j < jmax; j++) + x = x + 1.0; + if (x < 0.0) + printf ("ERROR: inc_middle(): loop 1: x < 0 (x=%f)\n", x); + } +#include "inc_body.h" + { + int jmax = n * x8M; + volatile float x = 0.0; + for (int j = 0; j < jmax; j++) + x = x + 1.0; + if (x < 0.0) + printf ("ERROR: inc_middle(): loop 2: x < 0 (x=%f)\n", x); + } +} + +void +inc_exit (int n) +{ + { + int jmax = n * x8M; + volatile float x = 0.0; + for (int j = 0; j < jmax; j++) + x = x + 1.0; + if (x < 0.0) + printf ("ERROR: inc_exit(): x < 0 (x=%f)\n", x); + } + +#include "inc_exit.h" + diff --git a/gprofng/testsuite/gprofng.display/synprog/fitos.c b/gprofng/testsuite/gprofng.display/synprog/fitos.c new file mode 100644 index 0000000..f343876 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/fitos.c @@ -0,0 +1,78 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include "stopwatch.h" + +/* The random number generator below is adapted from Kernighan and Ritchie, + * "C Programming Language", Second Edition, p. 46. + */ + +#define IA 1103515245u +#define IC 12345u +#define IM 2147483648u + +static unsigned int current_random = 1; + +static int +my_irand (int imax) +{ + /* Create a random integer between 0 and imax, inclusive. i.e. [0..imax] */ + + /* Use overflow to wrap */ + current_random = current_random * IA + IC; + int ival = current_random & (IM - 1); /* Modulus */ + ival = (int) ((float) ival * (float) (imax + 0.999) / (float) IM); + return ival; +} + +#define N 6000000 + +int +fitos (int n) +{ + int i, k, retv; + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of fitos", NULL); + + if (n <= 0) + n = 1; + int max = N * n; + + long long count = 0; + do + { + for (current_random = 1, i = retv = k = 0; i < max; i++) + { + retv += my_irand (100); + k += (current_random & (IM - 1)) >= (1 << 22); + } + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, "\t\t%d out of a total of %d >= 2^22 (%d)\n", k, max, retv); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog ((gethrtime () - start), (gethrvtime () - vstart), "fitos", NULL); + return 0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_body.h b/gprofng/testsuite/gprofng.display/synprog/inc_body.h new file mode 100644 index 0000000..2126ea3 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_body.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +{ + volatile float x = 0.0; + int jmax = n * 2000000; + for (int j = 0; j < jmax; j++) + x = x + 1.0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_brace.h b/gprofng/testsuite/gprofng.display/synprog/inc_brace.h new file mode 100644 index 0000000..2126ea3 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_brace.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +{ + volatile float x = 0.0; + int jmax = n * 2000000; + for (int j = 0; j < jmax; j++) + x = x + 1.0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_entry.h b/gprofng/testsuite/gprofng.display/synprog/inc_entry.h new file mode 100644 index 0000000..fdf9eed --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_entry.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* opening brace of function, second-level include */ + +{ +#include "inc_body.h" diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_exit.h b/gprofng/testsuite/gprofng.display/synprog/inc_exit.h new file mode 100644 index 0000000..4ada146 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_exit.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* closing brace of function, second-level include */ + +#include "inc_body.h" + +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_func.h b/gprofng/testsuite/gprofng.display/synprog/inc_func.h new file mode 100644 index 0000000..7b14359 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_func.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +void +inc_func (int n) +{ + volatile float x = 0.0; + int jmax = n * 2000000; + for (int j = 0; j < jmax; j++) + x = x + 1.0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_inline.h b/gprofng/testsuite/gprofng.display/synprog/inc_inline.h new file mode 100644 index 0000000..219aee0 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_inline.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef NO_INLINE +void ext_inline_code() __attribute__ ((always_inline)); +#endif + +void +ext_inline_code (int n) +{ + volatile long x = 0; + int jmax = n * 2000000; + for (int j = 0; j < jmax; j++) + x = x + 1; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_macro.h b/gprofng/testsuite/gprofng.display/synprog/inc_macro.h new file mode 100644 index 0000000..c2093da --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_macro.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* define a macro that burns CPU time */ +#define extburncpu(n) \ + x = 0.0; \ + for(j=0; j<n*1000000; j++) { \ + x = x + 1.0; \ + } diff --git a/gprofng/testsuite/gprofng.display/synprog/iosyn.c b/gprofng/testsuite/gprofng.display/synprog/iosyn.c new file mode 100644 index 0000000..278ceea --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/iosyn.c @@ -0,0 +1,614 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/time.h> + +#include "stopwatch.h" + +/* parameters defining various tasks */ +#define BUFSIZE 16384 +#define NBLKS 1024 + +#define SIZE ((int)(16*1024*1024)) +unsigned buffer[SIZE]; +extern FILE *fid2; + +/* ioerror - do some erroneous file IO operations */ +int +ioerror () +{ + FILE *fp; /* FILE pointer for stdio */ + char *fname = NULL; + char *ptr = NULL; + int fd; /* file descriptor for raw IO */ + int fd2; /* file descriptor for raw IO */ + int stat; + char buf[BUFSIZE]; + unsigned long size = 0; + char sfn[23] = ""; + + /* Log the regular read */ + wlog ("start of ioerror", NULL); + + /* fname is set to NULL. + Use various calls to create + a file. + */ + + fd = creat (fname, 0666); + fd = open (fname, 0666); + fd2 = 0; + fd = openat (fd2, fname, 0666); + fp = fopen (fname, "w"); + fp = fopen ("/iotest", "w"); + fp = NULL; + stat = fflush (fp); + stat = chmod (fname, 755); + stat = access (fname, 755); + fname = "/tmp/synprogXXXXXX"; + strncpy (sfn, fname, sizeof (sfn)); + fd = mkstemp (sfn); + stat = unlink (sfn); + stat = rename (fname, NULL); + unlink (fname); + fp = fopen (fname, "w"); + stat = fclose (fp); + stat = fread (buf, 100, 2, fp); + stat = fwrite (buf, 100, 2, fp); + ptr = fgets (buf, size, fp); + read (10000, buf, 100); + write (10000, buf, 100); + stat = unlink (fname); + fname = NULL; + stat = mkdir (fname, 755); + stat = unlink (fname); + /* + These functions cannot be executed + if the File Pointer (fp) is set + to NULL. They generate segv failure + in actual call not inside of + the wrapper. + + stat = fread(buf, size, 2, fp); + stat = fwrite(buf, size, 2, fp); + ptr = fgets(buf, size, fp); + stat = fputs(buf, fp); + stat = fprintf(fp, "%d\n", size); + stat = fseek(fp, size, size); + rewind(fp); + ftell(fp); + fpos_t pos; + stat = fsetpos(fp, &pos); + stat = fgetpos(fp, &pos); + */ + return 0; +} + +/*=======================================================*/ + +/* iofile - do some file io operations */ +int +iofile () +{ + FILE *fp; /* FILE pointer for stdio */ + int k; /* temp value for loop */ + int i; + char *buf; + hrtime_t start; + hrtime_t vstart; + char sfn[23] = ""; + char *fname = "/tmp/synprogXXXXXX"; + int ret; + int readCnt = 0; + int bRead = 0; + int writeCnt = 0; + int bWritten = 0; + int otherIOCnt = 0; + int bytes = 0; + + start = gethrtime (); + vstart = gethrvtime (); + + /* Log the event */ + bytes = wlog ("start of iofile -- stdio", NULL); + bWritten += bytes; + writeCnt++; + + strncpy (sfn, fname, sizeof (sfn)); + ret = mkstemp (sfn); + otherIOCnt++; + if (ret == -1) + { + fprintf (stderr, "Unable to make a temporary name\n"); + exit (1); + } + bytes = fprintf (stderr, "\tUsing %s as scratch file\n", sfn); + bWritten += bytes; + writeCnt++; + + /* allocate a buffer for the reading */ + /* note that this buffer is leaked! */ + buf = (char *) malloc (BUFSIZE); + + /* open the file */ + fp = fdopen (ret, "w"); + otherIOCnt++; + if (fp == NULL) + { + fprintf (stderr, "++ERROR opening %s, error %d\n", sfn, errno); + exit (1); + } + + /* loop, writing the buffer to the file... */ + for (i = 0; i < NBLKS; i++) + { + k = fwrite (buf, sizeof (char), BUFSIZE, fp); + writeCnt++; + if (k != BUFSIZE) + { + fprintf (stderr, "++ERROR writing %s, error %d\n", sfn, errno); + exit (1); + } + bWritten += k; + } + + fclose (fp); + fp = NULL; + otherIOCnt++; + + sprintf (buf, "fwrite: %d blocks of %d", i, BUFSIZE); + bytes = whrvlog (gethrtime () - start, gethrvtime () - vstart, buf, NULL); + bWritten += bytes; + writeCnt++; + + + /* now reopen the file, and read it */ + start = gethrtime (); + vstart = gethrvtime (); + + fp = fopen (sfn, "r"); + otherIOCnt++; + if (fp == NULL) + { + fprintf (stderr, "++ERROR opening %s, error %d\n", sfn, errno); + exit (1); + } + i = 0; + for (;;) + { + k = fread (buf, sizeof (char), BUFSIZE, fp); + readCnt++; + if (k < 0) + fprintf (stderr, "++ERROR reading %s, error %d\n", sfn, errno); + + + if (k == 0) + { + /* close the file */ + fclose (fp); + fp = NULL; + otherIOCnt++; + break; + + } + else if (k != BUFSIZE) + { + /* short read */ + sprintf (buf, "\tunexpecter short read %d on %s\n", k, sfn); + fprintf (stderr, buf); + bRead += k; + break; + } + else + { + /* bump the block counter */ + i++; + bRead += k; + } + } + + if (fp != NULL) + { + fclose (fp); + fp = NULL; + } + sprintf (buf, "fread: %d blocks of %d", i, BUFSIZE); + bytes = whrvlog (gethrtime () - start, gethrvtime () - vstart, buf, NULL); + bWritten += bytes; + writeCnt++; + + bWritten += 99; /* the number of bytes are written by the next fprintf */ + writeCnt++; + + unlink (sfn); + otherIOCnt++; + fprintf (fid2, "X %14d %14d %17d %15d %17d iofile\n", + bRead, readCnt, bWritten, writeCnt, otherIOCnt); + return 0; +} + +/* iotest - do various io syscalls */ +int +iotest () +{ + char *fname = "/tmp/foobar"; + int fd; /* file descriptor for raw IO */ + int fd2; /* file descriptor for raw IO */ + int k; /* temp value for loop */ + char buf[BUFSIZE]; + unsigned long size = 0; + int readCnt = 0; + int bRead = 0; + int writeCnt = 0; + int bWritten = 0; + int otherIOCnt = 0; + int bytes = 0; + + /* Log the regular read */ + bytes = wlog ("start of iotest", NULL); + bWritten += bytes; + writeCnt++; + + /* create an empty file */ + fd = creat (fname, 0666); + otherIOCnt++; + + /* dup the file descriptor */ + fd2 = dup (fd); + otherIOCnt++; + close (fd2); + otherIOCnt++; + close (fd); + otherIOCnt++; + + /* now open the empty file */ + fd = open (fname, O_RDONLY); + otherIOCnt++; + + /* loop, reading into the buffer */ + size = 0; + for (;;) + { + k = read (fd, buf, BUFSIZE); + readCnt++; + if (k < 0) + fprintf (stderr, "++ERROR reading %s, error %d\n", fname, errno); + else + { + size = size + k; + bRead += k; + } + if (k != BUFSIZE) + { + /* close the file */ + close (fd); + fd = -1; + otherIOCnt++; + bRead += k; + + /* short eread = EOF */ + break; + } + } + if (fd != -1) + { + close (fd); + fd = -1; + } + bWritten += 99; /* the number of bytes are written by the next fprintf */ + writeCnt++; + + /* remove the file */ + unlink (fname); + otherIOCnt++; + fprintf (fid2, "X %14d %14d %17d %15d %17d iotest\n", + bRead, readCnt, bWritten, writeCnt, otherIOCnt); + + return 0; +} + +/* + * Memory mapping routines- + * + * Allocate and deallocate memory using mmap and malloc. + * + * There is one parameter--the total number of megabytes to write, + * written in as many 16 megabyte files as are needed + */ + +unsigned char *start = (unsigned char*) 0x80000000; +unsigned char *stop; +int nblocks; + +void +memorymap (int megabytes) +{ + int readCnt = 0; + int bRead = 0; + int writeCnt = 0; + int bWritten = 0; + int otherIOCnt = 0; + int bytes; + + /* + * First, see how much time it takes to mmap all the files. + * + * Second, pull in just a few pages of information to see how much + * time the "How much IBM do I hold?" question would take. + * + * Next, compare updating the database shared with updating it private + * and then recopying the changed segments. + + * (We could catch the pages that we have altered by mapping the + * entire BIS read-only and then punching holes in it via an + * mprotect call as we catch segfaults. This gives us a list + * of the pages that we need to write, at the added expense of + * handling lots of interrupts.) + * (Notice that we don't test the case where we are adding to + * the BIS files. This is an interesting situation as we either + * have to open the last page past the last write point or reopen + * extendable in some way. We could do that by opening /dev/zero + * with MAP_ANON for addresses above our current usage point. + */ + + int i; + stop = start + 1024 * 1024 * (long long) megabytes; + + printf ("Creating %d random numbers\n", SIZE); + for (i = 0; i < SIZE; i++) + buffer[i] = random (); // set pseudo-bis to noise + printf ("Done creating random numbers\n"); + + + /* + * Write a database consisting of 16 megabyte files. + * Each filename contains the memory address into which + * the file should be reloaded. + */ + + printf ("Writing pseudo-bis files\n"); + unsigned char* base = start; + nblocks = 0; + for (i = 0; i < megabytes; i += 16) + { + nblocks++; + // write data in 16MB files + char filename[256]; + sprintf (filename, "bistest.%p.%d", base, i); + int fd = open (filename, O_CREAT | O_TRUNC | O_WRONLY, 0660); + otherIOCnt++; + if (fd == -1) + { + printf ("open of %s failed: %s\n", filename, strerror (errno)); + exit (0); + } + bytes = write (fd, buffer, SIZE); + bWritten += bytes; + writeCnt++; + close (fd); + otherIOCnt++; + printf ("\twrote %d megabytes\n", i + 16); + base += 16 * 1024 * 1024; + } + printf ("Done writing files from %p to %p\n", start, stop); + + int j; + + printf ("Memory map all the files (private)\n"); + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + char filename[256]; + sprintf (filename, "bistest.%p.%d", base, i); + int fd = open (filename, O_RDWR); + otherIOCnt++; + if (fd < 0) + printf ("open of %s failed: %s\n", filename, strerror (errno)); + unsigned char *mp = (unsigned char*) mmap ((char*) base, + SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0); + if (mp == MAP_FAILED || mp != base) + { + printf ("mmap of %s failed: %s\n", filename, strerror (errno)); + exit (1); + } + + printf ("mapped %d bytes at %p\n", SIZE, base); + close (fd); // mmap will hold the file open for us + otherIOCnt++; + } + + printf ("Mapping done\n"); + fflush (stdout); + otherIOCnt++; + + int ranlimit = 1000; + printf ("Access %d bytes at random\n", ranlimit); + int sum = 0; + for (i = 0; i < ranlimit; i++) + { + unsigned char *where = start + + (((unsigned long) random ()) % (stop - start)); + sum += (int) *where; + } + printf ("Random byte access done\n"); + + ranlimit = 1000; + int ranrange = 256; + printf ("Alter %d random locations, %d bytes each (private)\n", + ranlimit, ranrange); + + for (i = 0; i < ranlimit; i++) + { + unsigned char *where = start + + (((unsigned long) random ()) % (stop - start)); + for (j = 0; j < ranrange; j++) + *where++ = j; + } + + printf ("Memory alteration done\n"); + fflush (stdout); + otherIOCnt++; + + printf ("Copy all memory back to disk\n"); + + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + char filename[256]; + sprintf (filename, "bistest2.%p.%d", base, i); + int fd = open (filename, O_RDWR | O_CREAT | O_TRUNC, 0660); + otherIOCnt++; + if ((bytes = write (fd, base, SIZE)) == -1) + { + printf ("write of %s failed: %s\n", filename, strerror (errno)); + exit (1); + } + bWritten += bytes; + writeCnt++; + close (fd); + otherIOCnt++; + } + + printf ("Disk copy complete\n"); + fflush (stdout); + otherIOCnt++; + + printf ("Unmap all segments\n"); + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + if (munmap ((char*) base, SIZE) == -1) + { + printf ("munmap failed: %s\n", strerror (errno)); + exit (1); + } + printf ("unmapped %d bytes at %p\n", SIZE, base); + } + printf ("Segment unmapping complete\n"); + fflush (stdout); + otherIOCnt++; + + printf ("Remap all segments as shared\n"); + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + char filename[256]; + sprintf (filename, "bistest.%p.%d", base, i); + int fd = open (filename, O_RDWR); + otherIOCnt++; + char* mp = mmap ((char*) base, SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, fd, 0); + if (mp == MAP_FAILED || (unsigned char*) mp != base) + { + printf ("re mmap of %s failed: %s\n", filename, strerror (errno)); + exit (1); + } + printf ("remapped %d bytes at %p\n", SIZE, base); + close (fd); // mmap will hold the file open for us + otherIOCnt++; + } + printf ("Remapping complete\n"); + fflush (stdout); + otherIOCnt++; + + ranlimit = 1000; + ranrange = 256; + printf ("Alter %d random locations, %d bytes each (shared)\n", + ranlimit, ranrange); + for (i = 0; i < ranlimit; i++) + { + unsigned char* where = start + + (((unsigned long) random ()) % (stop - start)); + for (j = 0; j < ranrange; j++) + *where++ = j; + } + printf ("Memory alteration done\n"); + fflush (stdout); + otherIOCnt++; + + printf ("Unmap all segments\n"); + for (i = 0; i < megabytes; i += 16) + { + unsigned char *base = start; + base += i * 1024 * 1024; + if (munmap ((char*) base, SIZE) == -1) + { + printf ("munmap failed: %s\n", strerror (errno)); + exit (1); + } + printf ("unmapped %d bytes at %p\n", SIZE, base); + } + printf ("Segment unmapping complete\n"); + fflush (stdout); + otherIOCnt++; + + base = start; + + for (i = 0; i < megabytes; i += 16) + { + // write data in 16MB files + char filename[256]; + sprintf (filename, "bistest.%p.%d", base, i); + if (unlink (filename) != 0) + { + printf ("unlink of %s failed: %s\n", filename, strerror (errno)); + } + base += 16 * 1024 * 1024; + otherIOCnt++; + } + + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + char filename[256]; + sprintf (filename, "bistest2.%p.%d", base, i); + if (unlink (filename) != 0) + { + printf ("unlink of %s failed: %s\n", filename, strerror (errno)); + } + otherIOCnt++; + } + bWritten += 102; /* the number of bytes are written by the next fprintf */ + writeCnt++; + + fflush (fid2); + otherIOCnt++; + + /* Record accounting record */ + fprintf (fid2, "X %14d %14d %17d %15d %17d memorymap\n", + bRead, readCnt, bWritten, writeCnt, otherIOCnt); + printf ("Deleted scratch files\n"); +} diff --git a/gprofng/testsuite/gprofng.display/synprog/pagethrash.c b/gprofng/testsuite/gprofng.display/synprog/pagethrash.c new file mode 100644 index 0000000..a909600 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/pagethrash.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include "stopwatch.h" + +/*=======================================================*/ + +/* pagethrash - allocate some memory, and thrash around in it */ +int +pagethrash (int thrashmb) +{ + char buf[1024]; + + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of pagethrash", NULL); + + /* Start a stopwatch */ + stopwatch_t *w = stpwtch_alloc ("pagethrash", 0); + + /* compute the size */ + unsigned long size = thrashmb * 1024 * 1024; + int pagesize = getpagesize (); + void *space = malloc (size + pagesize); + if (space == NULL) + { + fprintf (stderr, "\tpagethrash failed; can't get %ld bytes.\n", size); + exit (1); + } + + /* round address to page boundary */ + unsigned long loc = (((unsigned long) space + pagesize - 1) & ~(pagesize - 1)); + long npages = size / pagesize; + + /* touch all the pages to force them in */ + for (long i = 0; i < npages; i++) + { + stpwtch_start (w); + *(int *) (loc + i * pagesize) = i; + stpwtch_stop (w); + } + + /* now free up the space */ + free (space); + + /* print the timing results */ + stpwtch_print (w); + free ((void *) w); + + sprintf (buf, "pagethrash: %ld pages", npages); + whrvlog (gethrtime () - start, gethrvtime () - vstart, buf, NULL); + return 0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/so_syn.c b/gprofng/testsuite/gprofng.display/synprog/so_syn.c new file mode 100644 index 0000000..6fc5aa6 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/so_syn.c @@ -0,0 +1,69 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include "stopwatch.h" + +static void so_burncpu (); + +int +so_cputime () +{ + wlog ("start of so_cputime", NULL); + + /* put a memory leak in here */ + (void) malloc (13); + + fprintf (stderr, "so_burncpu @ 0x%08x\n", (unsigned int) so_burncpu); + so_burncpu (); + + wlog ("end of so_cputime", NULL); + return 13; +} + +void so_init () __attribute__ ((constructor)); + +void +so_init () +{ + fprintf (stderr, "so_init executed\n"); +} + +/* so_burncpu - loop to use a bunch of user time */ +void +so_burncpu () +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + volatile float x = 0.; /* temp variable for f.p. calculation */ + long long count = 0; + do + { + x = 0.0; + for (int j = 0; j < 100000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, gethrvtime () - vstart, "so_burncpu", NULL); +} diff --git a/gprofng/testsuite/gprofng.display/synprog/so_syx.c b/gprofng/testsuite/gprofng.display/synprog/so_syx.c new file mode 100644 index 0000000..ae7da6f --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/so_syx.c @@ -0,0 +1,68 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include "stopwatch.h" + +static void sx_burncpu (); + +int +sx_cputime () +{ + wlog ("start of sx_cputime", NULL); + + /* put a memory leak in here */ + (void) malloc (13); + + fprintf (stderr, "sx_burncpu @ 0x%08x\n", (unsigned int) sx_burncpu); + sx_burncpu (); + wlog ("end of sx_cputime", NULL); + return 13; +} + +void sx_init () __attribute__ ((constructor)); + +void +sx_init () +{ + fprintf (stderr, "sx_init executed\n"); +} + +/* sx_burncpu - loop to use a bunch of user time */ +void +sx_burncpu () +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + volatile float x = 0.; /* temp variable for f.p. calculation */ + long long count = 0; + do + { + x = 0.0; + for (int j = 0; j < 100000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, gethrvtime () - vstart, "sx_burncpu", NULL); +} diff --git a/gprofng/testsuite/gprofng.display/synprog/stopwatch.c b/gprofng/testsuite/gprofng.display/synprog/stopwatch.c new file mode 100644 index 0000000..5cb5281 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/stopwatch.c @@ -0,0 +1,294 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "stopwatch.h" + +static char *prhrdelta (hrtime_t); +static char *prhrvdelta (hrtime_t); +void init_micro_acct (); + +/* stopwatch routines */ +void +stpwtch_calibrate () +{ + struct timeval ttime; + char buf[1024]; + + (void) gettimeofday (&ttime, NULL); + time_t secs = (time_t) ttime.tv_sec; + sprintf (buf, "%s Stopwatch calibration", prtime (&secs)); + wlog (buf, NULL); + + init_micro_acct (); + stopwatch_t *inner = stpwtch_alloc ("inner", 0); + stopwatch_t *outer = stpwtch_alloc ("outer", 0); + for (int i = 0; i < 1000; i++) + { + stpwtch_start (outer); + stpwtch_start (inner); + stpwtch_stop (inner); + stpwtch_stop (outer); + } + stpwtch_print (inner); + stpwtch_print (outer); + free ((void *) inner); + free ((void *) outer); +} + +stopwatch_t * +stpwtch_alloc (char *name, int histo) +{ + stopwatch_t *w = (stopwatch_t *) malloc (sizeof (stopwatch_t)); + if (w == NULL) + { + fprintf (stderr, "stpwtch_alloc(%s, %d): malloc failed\n", name, histo); + return NULL; + } + w->sum = 0.; + w->sumsq = 0.; + w->count = 0; + w->min = 0; + w->last = 0; + w->name = strdup (name); + stpwtch_start (w); + w->begin = w->start; + + return w; +} + +void +stpwtch_start (stopwatch_t *w) +{ + w->start = gethrtime (); +} + +void +stpwtch_stop (stopwatch_t *w) +{ + if (w->start == 0) /* if never started, ignore the call */ + return; + + /* get stopping high-res time */ + w->tempus = gethrtime (); + + /* bump count of stops */ + w->count++; + + /* compute the delta for this call */ + w->delta = w->tempus - w->start; + + /* add in this one */ + w->last = (double) (w->delta); + w->sum = w->sum + w->last; + w->sumsq = w->sumsq + w->last * w->last; + + if (w->max == 0) + w->max = w->last; + else if (w->max < w->last) + w->max = w->last; + if (w->min == 0) + w->min = w->last; + else if (w->min > w->last) + w->min = w->last; + + /* show stopwatch stopped */ + w->start = 0; +} + +void +stpwtch_print (stopwatch_t *w) +{ + char cvdbuf[1024]; + + /* get stopping high-res time */ + w->tempus = gethrtime (); + double duration = (double) (w->tempus - w->begin); + + if (w->count == 0) + sprintf (cvdbuf, " 0. s. ( 0. %% of %12.6f s.) -- %s\n", + (duration / 1000000000.), w->name); + else if (w->count == 1) + sprintf (cvdbuf, " %12.6f s. (%4.1f %%%% of %.6f s.) -- %s\n", + w->sum / 1000000000., (100. * w->sum) / duration, + duration / 1000000000., w->name); + else + sprintf (cvdbuf, + " %12.6f s. (%4.1f %%%% of %.6f s.) -- %s\n\tN = %d," + " avg = %.3f us., min = %.3f, max = %.3f\n", + w->sum / 1000000000., (100. * w->sum) / duration, + duration / 1000000000., w->name, w->count, + w->sum / 1000. / ((double) (w->count > 0 ? w->count : 1)), + ((double) w->min / 1000.), ((double) w->max / 1000.)); + fprintf (stderr, cvdbuf); +} + +/* hrtime routines */ +int +whrlog (hrtime_t delta, char *event, char *string) +{ + char buf[1024]; + if (string == NULL) + sprintf (buf, " %s secs. in %s\n", prhrdelta (delta), event); + else + sprintf (buf, " %s secs. in %s\n\t%s\n", prhrdelta (delta), event, string); + int bytes = fprintf (stderr, "%s", buf); + return bytes; +} + +/* hrtime routines */ +int +whrvlog (hrtime_t delta, hrtime_t vdelta, char *event, char *string) +{ + char buf[1024]; + if (string == NULL) + sprintf (buf, " %s wall-secs., %s CPU-secs., in %s\n", + prhrdelta (delta), prhrvdelta (vdelta), event); + else + sprintf (buf, " %s wall-secs., %s CPU-secs., in %s\n\t%s\n", + prhrdelta (delta), prhrvdelta (vdelta), event, string); + int bytes = fprintf (stderr, "%s", buf); + return bytes; +} + +/* prhrdelta (hrtime_t delta) + * returns a pointer to a static string in the form: + * sec.micros + * 1.123456 + * 0123456789 + * + * prhrvdelta is the same, but uses a different static buffer + */ +static char * +prhrdelta (hrtime_t delta) +{ + static char cvdbuf[26]; + + /* convert to seconds */ + double tempus = ((double) delta) / (double) 1000000000.; + sprintf (cvdbuf, "%10.6f", tempus); + return cvdbuf; +} + +static char * +prhrvdelta (hrtime_t delta) +{ + static char cvdbuf[26]; + + /* convert to seconds */ + double tempus = ((double) delta) / (double) 1000000000.; + sprintf (cvdbuf, "%10.6f", tempus); + return cvdbuf; +} + +/* time of day routines */ + +/* starting time - first timestamp; initialized on first call */ +static struct timeval starttime = {0, 0}; +static struct timeval ttime; /* last-recorded timestamp */ +static struct timeval deltatime; /* delta of last-rec'd timestamp */ + +static void +snaptod () +{ + (void) gettimeofday (&ttime, NULL); + if (starttime.tv_sec == 0) + starttime = ttime; + + deltatime.tv_sec = ttime.tv_sec - starttime.tv_sec; + deltatime.tv_usec = ttime.tv_usec - starttime.tv_usec; + while (deltatime.tv_usec < 0) + { + deltatime.tv_sec--; + deltatime.tv_usec += 1000000; + } +} + +int +wlog (char *event, char *string) +{ + char buf[1024]; + + snaptod (); + if (string == NULL) + sprintf (buf, "%s ===== (%d) %s\n", prdelta (deltatime), + (int) getpid (), event); + else + sprintf (buf, "%s ===== (%d) %s\n\t%s\n", prdelta (deltatime), + (int) getpid (), event, string); + int bytes = fprintf (stderr, "%s", buf); + return bytes; +} + +/* prtime (ttime) + * returns a pointer to a static string in the form: + * Thu 01 Jan 90 00:00:00\0 + * 01234567890122345678901234 + * + * ttime is a pointer to a UNIX time in seconds since epoch + * library routine localtime() is used + */ +char * +prtime (time_t *ttime) +{ + static char *days[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static char *months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + static char cvbuf[26]; + + /* get the date and time */ + struct tm *tp = localtime (ttime); + + /* convert to string */ + sprintf (cvbuf, "%3s %02d %s %02d %02d:%02d:%02d", days[tp->tm_wday], + tp->tm_mday, months[tp->tm_mon], tp->tm_year % 100, + tp->tm_hour, tp->tm_min, tp->tm_sec); + return cvbuf; +} + +char * +prdelta (struct timeval tempus) +{ + static char cvdbuf[26]; + while (tempus.tv_usec < 0) + { + tempus.tv_sec--; + tempus.tv_usec += 1000000; + } + long seconds = tempus.tv_sec % 60; + long minutes = tempus.tv_sec / 60; + long hours = minutes / 60; + minutes = minutes % 60; + sprintf (cvdbuf, "%02ld:%02ld:%02ld.%03ld ", + hours, minutes, seconds, (long) tempus.tv_usec / 1000); + return cvdbuf; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/stopwatch.h b/gprofng/testsuite/gprofng.display/synprog/stopwatch.h new file mode 100644 index 0000000..704b165 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/stopwatch.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _STOPWATCH_ +#define _STOPWATCH_ + +#include <time.h> +#include <sys/time.h> + +typedef int processorid_t; +typedef long long hrtime_t; +typedef struct timespec timespec_t; +extern hrtime_t gethrtime (); +extern hrtime_t gethrvtime (); + +extern double testtime; +char *prtime (time_t *t); +char *prdelta (struct timeval t); +int wlog (char *, char *); +int whrlog (hrtime_t delta, char *, char *); +int whrvlog (hrtime_t delta, hrtime_t vdelta, char *, char *); + +typedef struct stopwatch +{ + double sum; /* in nanoseconds */ + double sumsq; /* in (nanoseconds ** 2) */ + double last; /* in nanoseconds */ + hrtime_t begin; + hrtime_t start; + hrtime_t tempus; + hrtime_t delta; + hrtime_t min; + hrtime_t max; + int count; + char *name; +} stopwatch_t; + +stopwatch_t *stpwtch_alloc (char *, int); +void stpwtch_calibrate (); +void stpwtch_start (stopwatch_t *); +void stpwtch_stop (stopwatch_t *); +void stpwtch_print (stopwatch_t *); + +#endif diff --git a/gprofng/testsuite/gprofng.display/synprog/synprog.c b/gprofng/testsuite/gprofng.display/synprog/synprog.c new file mode 100644 index 0000000..a5361a4 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/synprog.c @@ -0,0 +1,1823 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + 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, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* synprog.c - synthetic program to use for testing performance tools */ +#define _GNU_SOURCE +#include <sched.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/wait.h> +#include <sys/ucontext.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <sched.h> +#include <sys/resource.h> +#include <string.h> +#include <sys/mman.h> +#include <dlfcn.h> +#include <fcntl.h> +#include "stopwatch.h" + +int get_ncpus (); +int get_clock_rate (); +void acct_init (char *); /* initialize accounting */ +void iotrace_init (char *); /* initialize IO trace accounting */ +void commandline (char *); /* routine execute a scenario */ +void forkcopy (char *, int); /* fork copy of self to run string */ +int clonecopy (void *); +#define CLONE_STACK_SIZE 8388608 +#define CLONE_TLS_SIZE 4096 +#define CLONE_RED_SIZE 4096 +#define CLONE_IO 0x80000000 /* Clone io context */ +void forkchild (char *); /* fork child to run string */ +void reapchildren (void); /* reap all children */ +void reapchild (int); /* reap a child after getting SIGCLD */ +void check_sigmask (); /* check that SIGPROF and SIGEMT are not masked */ +void masksig (); /* real code to mask SIGPROF and SIGEMT */ + +hrtime_t progstart; +hrtime_t progvstart; +hrtime_t gethrustime (); +static int include_system_time = 0; + +static hrtime_t +getmyvtime () +{ + if (include_system_time == 0) + return gethrvtime (); + return gethrustime (); +} + +void (*sigset (int sig, void (*disp)(int)))(int); +#define ITIMER_REALPROF ITIMER_REAL +/* Linux needs to have this defined for RTLD_NEXT and RTLD_DEFAULT */ +/* If the first argument of `dlsym' or `dlvsym' is set to RTLD_NEXT */ +#define RTLD_NEXT ((void *) -1l) +/* If the first argument to `dlsym' or `dlvsym' is set to RTLD_DEFAULT */ +#define RTLD_DEFAULT ((void *) 0) + +FILE *fid; +FILE *fid2; +double testtime = 3.0; +static char acct_file[128]; +static char new_name[128]; +static char child_name[128]; + +/* descendant process tracking */ +static unsigned syn_fork = 0; +static unsigned syn_exec = 0; +static unsigned syn_combo = 0; + +/* various behavior routines */ +int bounce (int); /* bounce with a->b->a->b-> ... */ +int callso (int); /* so load test */ +int callsx (int); /* alternate so load test */ +int correlate (int); /* test correlation with profiling */ +int cputime (int); /* use a bunch of user cpu time (fp) */ +int doabort (int); /* force a SEGV by dereferencing NULL */ +int dousleep (int); /* loop with a usleep call */ +int endcases (int); /* test various code construct endcases */ +int fitos (int); /* test various code construct endcases */ +int gpf (int); /* show gprof fallacy */ +int hrv (int); /* gethrvtime calls */ +int icputime (int); /* use a bunch of user cpu time (long) */ +int iofile (int); /* do operations on a temporary file */ +int iotest (int); /* do various io system calls */ +int ioerror (int); /* do various erroneous io system calls */ +int ldso (int); /* use a bunch of time in ld.so */ +int masksignals (int); /* mask the SIGEMT and SIGPROF signals */ +int memorymap (int); /* do mmap operation for io tracing */ +int muldiv (int); /* do integer multiply/divide for a time */ +int naptime (int); /* sleep for a time */ +int pagethrash (int); /* thrash around in memory */ +int recurse (int); /* recursion test */ +int recursedeep (int); /* deep recursion test */ +int sched (int); /* loop over sched_yield calls */ +int sigtime (int); /* use a bunch of time in a signal handler */ +int synccall (int); /* loop over sync() system calls */ +int systime (int); /* use a bunch of system time */ +int tailcallopt (int); /* tail call optimization test */ +int underflow (int); /* force underflow arithmetic */ +int unwindcases (int); /* test various unwind corner cases */ + +int itimer_realprof (int); /* mess with itimer ITIMER_REALPROF */ +int sigprof (int); /* mess with SIGPROF sigaction */ +int sigprofh (int); /* mess with SIGPROF handler */ +int do_chdir (int); /* do a chdir() */ +int do_exec (int); /* do an exec() call */ +int do_popen (int); /* do a popen() call */ +int do_system (int); /* do a system() call */ +int do_forkexec (int); /* do a fork()+exec() call combo */ +int +do_vforkexec (int); /* do a vfork()+exec() call combo */ + +/* lookup table for behavior scripts */ +struct scripttab +{ + char *name; + int (*function)(int); + char *acctname; + int param; + int noverify; +}; + +static int CLONE_FLAGS[] = { + SIGCHLD, + CLONE_FILES | CLONE_FS | CLONE_SYSVSEM | CLONE_IO | SIGCHLD +}; + +/* the default script */ +static char DEFAULT_COMMAND[] = + "icpu.md.cpu.rec.recd.dousl.gpf.fitos.ec.tco.b.nap.uf." + "sys.sig.so.sx.so.sched.uwdc"; + +struct scripttab scripttab[] = { + {"abt", doabort, "doabort", 0, 0}, + {"b", bounce, "bounce", 0, 0}, + {"c", correlate, "correlate", 0, 0}, + {"chdir", do_chdir, "chdir", 0, 0}, + {"chdirX", do_chdir, "chdir", -1, 0}, + {"cpu", cputime, "cputime", 0, 0}, + {"dousl", dousleep, "dousleep", 0, 1}, + {"ec", endcases, "endcases", 0, 0}, + {"exec", do_exec, "exec", 0, 0}, + {"execX", do_exec, "do_exec", -1, 0}, + {"fitos", fitos, "fitos", 0, 1}, + {"gpf", gpf, "gpf", 0, 0}, + {"hrv", hrv, "hrv", 0, 0}, + {"icpu", icputime, "icputime", 0, 0}, + {"iofile", iofile, "iofile", 0, 0}, + {"iotest", iotest, "iotest", 0, 0}, + {"ioerror", ioerror, "ioerror", 0, 0}, + {"itimer", itimer_realprof, "itimer", 1, 0}, + {"itimer0", itimer_realprof, "itimer", 0, 0}, + {"ldso", ldso, "ldso", 0, 0}, + {"masksig", masksignals, "masksig", 0, 0}, + {"md", muldiv, "muldiv", 0, 0}, + {"memorymap", memorymap, "memorymap", 100, 0}, + {"nap", naptime, "naptime", 0, 0}, + {"pg", pagethrash, "pagethrash", 32, 0}, + {"popen", do_popen, "popen", 0, 0}, + {"popenX", do_popen, "popen", -1, 0}, + {"rec", recurse, "recurse", 50, 0}, + {"recd", recursedeep, "<Truncated-stack>", 500, 0}, + {"sched", sched, "sched", 0, 1}, + {"so", callso, "callso", 0, 0}, + {"sx", callsx, "callsx", 0, 0}, + {"sig", sigtime, "sigtime", 0, 1}, + {"sigprof", sigprof, "sigprof", 1, 0}, + {"sigprof0", sigprof, "sigprof", 0, 0}, + {"sigprofh", sigprofh, "sigprofh", 1, 0}, + {"sigprofh0", sigprofh, "sigprofh", 0, 0}, + {"sync", synccall, "synccall", 0, 1}, + {"sys", systime, "systime", 0, 1}, + {"system", do_system, "system", 0, 0}, + {"systemX", do_system, "do_system", -1, 0}, + {"tco", tailcallopt, "tailcallopt", 0, 0}, + {"uf", underflow, "underflow", 0, 1}, + {"forkexec", do_forkexec, "forkexec", 0, 0}, + {"vforkexec", do_vforkexec, "vforkexec", 0, 0}, + {"uwdc", unwindcases, "unwindcases", 0, 0}, + {NULL, NULL, NULL, 0, 0} +}; + +int +main (int argc, char **argv) +{ + int i; + hrtime_t start; + hrtime_t vstart; + char *name; + char buf[1024]; + char arglist[4096]; + + // need a more robust test of whether system HWC events are being counted + if (getenv ("TILDECLAUSE")) + include_system_time = 1; + progstart = gethrtime (); + progvstart = getmyvtime (); + name = getenv ("SP_COLLECTOR_TEST_TIMER"); + if (name) + { + testtime = atof (name); + if (testtime <= 0) + testtime = 1.0; + } + name = getenv ("_SP_NAME"); + if (name == NULL || strlen (name) == 0) + strcpy (acct_file, "synprog.acct"); + else + strcpy (acct_file, name); + + strcpy (arglist, argv[0]); + for (i = 1; i < argc; i++) + { + strcat (arglist, " "); + strcat (arglist, argv[i]); + } + + sprintf (buf, "%s run", argv[0]); + wlog (buf, NULL); + + int ncpus = get_ncpus (); + acct_init (acct_file); + iotrace_init ("synprog.acct2"); + + /* Start a timer */ + start = gethrtime (); + vstart = getmyvtime (); + +#ifndef NO_MS_ACCT + stpwtch_calibrate (); +#endif + + if (argc == 1) + commandline (DEFAULT_COMMAND); + else + { + i = 2; + while (i < argc) + { + forkcopy (argv[i], i - 1); + i++; + } + + /* do the last one ourself */ + commandline (argv[1]); + } + reapchildren (); + whrvlog (gethrtime () - start, getmyvtime () - vstart, buf, NULL); + fflush (fid); + fflush (fid2); + fclose (fid); + fclose (fid2); + return 0; +} + +/* acct_init: initialize accounting */ +void +acct_init (char *acct_file) +{ + fid = fopen (acct_file, "w"); + if (fid == NULL) + { + fprintf (stderr, "Open of %s for output failed: %s\n", + acct_file, strerror (errno)); + exit (1); + } + fprintf (fid, "MHz: %d\n", get_clock_rate ()); + fprintf (fid, "X Incl. Total Incl. CPU Name\n"); + fflush (fid); + + /* write a record for <Unknown>, which should have zero times */ + fprintf (fid, "X %6.3f %6.3f %s\n", 0.0, 0.0, "<Unknown>"); + + /* set up to reap any children */ + (void) sigset (SIGCHLD, reapchild); + /* verify the signal mask */ +} + +/* iotrace_init: initialize IO trace accounting */ +void +iotrace_init (char *acct_file) +{ + fid2 = fopen (acct_file, "w"); + if (fid2 == NULL) + { + fprintf (stderr, "Open of %s for output failed: %s\n", + acct_file, strerror (errno)); + exit (1); + } + fprintf (fid2, "X Incl.BytesRead Incl.ReadCount "); + fprintf (fid2, "Incl.BytesWritten Incl.WriteCount "); + fprintf (fid2, "Incl.OtherIOCount Name\n"); + fflush (fid2); +} + +/* commandline -- process a command line string: + * verbs are separated by a . character; each verb is looked-up + * in a table, and the routine to process it, and argument fetched. + * the routine is called. + */ +void +commandline (char *cmdline) +{ + char *p; + char *j; + char prevj; + struct scripttab *k; + char buf[1024]; + hrtime_t pstart; + hrtime_t pvstart; + hrtime_t pend; + hrtime_t pvend; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog (" Begin commandline", cmdline); + + p = cmdline; + while (*p != 0) + { + /* find the terminator for this verb (a . or NULL) */ + j = p; + while (*j != 0 && *j != '.') + j++; + prevj = *j; + *j = 0; + + /* now look up the phase in the table */ + for (k = &scripttab[0];; k++) + { + if (k->name == NULL) + break; + if (strcmp (p, k->name) == 0) + { + /* found a match */ + pstart = gethrtime (); + pvstart = getmyvtime (); + (k->function)(k->param); + pend = gethrtime (); + pvend = getmyvtime (); + fprintf (fid, "%c %6.3f %6.3f %s\n", + k->noverify == 0 ? 'X' : 'Y', + (double) (pend - pstart) / (double) 1000000000., + (double) (pvend - pvstart) / (double) 1000000000., + k->acctname); + fflush (fid); + break; + } + } + if (k->name == NULL) + { + sprintf (buf, "++ ignoring `%s'\n", p); + fprintf (stderr, buf); + } + + /* continue processing */ + *j = prevj; + p = j; + if (prevj != 0) + p++; + } + whrvlog (gethrtime () - start, getmyvtime () - vstart, "commandline", cmdline); +} + +int +clonecopy (void * script) +{ + syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */ + + strcpy (acct_file, child_name); + /*printf("_SP_NAME=\"%s\" (for clone-child)\n", acct_file);*/ + acct_init (acct_file); + + /* execute the script */ + commandline ((char *) script); + + /* reap the child's descendants */ + reapchild (0); + exit (0); +} + +/* forkcopy -- fork a copy to run a script */ +void +forkcopy (char *script, int child) +{ + int child_pid; + if (strncmp ("clone", script, 5) == 0) + { + //clone instead of fork + /* Log the event */ + wlog ("cloning copy ... ", script); + + sprintf (child_name, "%s_C%d", acct_file, ++syn_fork); + /* clone a new process */ + void * stack; + void * stack_space; + int stack_size = CLONE_STACK_SIZE; + + stack_space = mmap (NULL, stack_size, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS, -1, 0); + if ((void*) - 1 == stack_space) + { + fprintf (stderr, "Error: mmap returned -1\n"); + exit (1); + } + mprotect (stack_space, CLONE_RED_SIZE, PROT_NONE); + stack = (char *) stack_space + stack_size - CLONE_TLS_SIZE; // stack grows back + child_pid = clone (clonecopy, stack, CLONE_FLAGS[(child + 1) % 2], + (void *) (script + sizeof ("clone") - 1)); + if (child_pid < 0) + { + /* error, could not fork */ + fprintf (stderr, "forkcopy: clone failed--error %d\n", errno); + exit (1); + } + + fprintf (stderr, "child process %d cloned by %d.\n", + child_pid, (int) getpid ()); + return; + } + + /* Log the event */ + wlog ("forking copy ... ", script); + sprintf (child_name, "%s_f%d", acct_file, ++syn_fork); + + /* fork a new process */ + child_pid = fork (); + if (child_pid < 0) + { + /* error, could not fork */ + fprintf (stderr, "forkcopy: fork failed--error %d\n", errno); + exit (1); + + } + else if (child_pid == 0) + { + syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */ + strcpy (acct_file, child_name); + acct_init (acct_file); + + /* execute the script */ + commandline (script); + + /* reap the child's descendants */ + reapchild (0); + exit (0); + } + fprintf (stderr, "child process %d forked by %d.\n", + child_pid, (int) getpid ()); +} + +void +forkchild (char * cmdline) +{ + stopwatch_t *prog; + char mbuf[1024]; + + /* Start a stopwatch */ + sprintf (mbuf, "%s pid[%d]", "Synprog child", (int) getpid ()); + prog = stpwtch_alloc (mbuf, 0); + + /* process this child's command-line */ + commandline (cmdline); + + /* reap the child's descendants */ + reapchild (0); + + /* Stop print, and free the stopwatch */ + stpwtch_stop (prog); + stpwtch_print (prog); + free (prog); + + exit (0); +} + +/* reap a child process, called in response to SIGCLD */ +void +reapchild (int sig) +{ + int status; + int ret = wait (&status); + sigset (SIGCLD, reapchild); +} + +/* reap all child processes prior to exit */ +void +reapchildren () +{ + int status; + int ret; + + /* wait for all children to exit */ + for (;;) + { + while ((ret = wait (&status)) != (pid_t) - 1) + fprintf (stderr, "synprog: reap child %x\n", ret); + if (errno == EINTR) + continue; + if (errno == ECHILD) + return; + fprintf (stderr, "synprog: unexpected errno from wait() syscall -- %s\n", + strerror (errno)); + } +} + +/* doabort -- force a SEGV */ +int +doabort (int k) +{ + char *nullptr = NULL; + char c; + + /* Log the event */ + wlog ("start of doabort", NULL); + + /* and dereference a NULL */ + c = *nullptr; + + /* this should never be reached */ + return (int) c; +} + +/* =============================================================== */ + +/* dousleep -- loop with a usleep */ +int +dousleep (int k) +{ + volatile double x; + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of dousleep", NULL); + do + { + x = 0.0; + for (int j = 0; j < 1000000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "dousleep", NULL); + /* this should never be reached */ + return (int) 0; +} + +/* =============================================================== */ +/* correlate -- generate CPU use, correlated with profiling clock */ + +static void csig_handler (int); + +int +correlate (int k) +{ + volatile float x; /* temp variable for f.p. calculation */ + struct itimerval tval; + int retval; + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of correlate", NULL); + + /* set up the signal handler */ + sigset (SIGALRM, csig_handler); + + /* set an itimer, to break out of the sleep loop */ + tval.it_value.tv_sec = 0; + tval.it_value.tv_usec = 10000; + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 10000; + + retval = setitimer (ITIMER_REAL, &tval, 0); + if (retval != 0) + fprintf (stderr, "setitimer(ITIMER_REAL) got %d returned: %s\n", + retval, strerror (errno)); + do + { + x = 0.0; + for (int j = 0; j < 1000000; j++) + x = x + 1.0; + sleep (1); /* relying on the timer to break out */ + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + /* now disable the itimer */ + tval.it_value.tv_sec = 0; + tval.it_value.tv_usec = 0; + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 0; + + retval = setitimer (ITIMER_REAL, &tval, 0); + if (retval != 0) + fprintf (stderr, "setitimer(ITIMER_REAL) got %d returned: %s\n", + retval, strerror (errno)); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "correlate", NULL); + return 0; +} + +static void +csig_handler (int sig) +{ + return; +} + +/* cputime -- loop to use a bunch of user time (f.p.) */ +int +cputime (int k) +{ + volatile float x; /* temp variable for f.p. calculation */ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of cputime", NULL); + do + { + x = 0.0; + for (int j = 0; j < 1000000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "cputime", NULL); + return 0; +} + +/* icputime -- loop to use a bunch of user time (long) */ +int +icputime (int k) +{ + volatile long x; /* temp variable for long calculation */ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of icputime", NULL); + do + { + x = 0; + for (int j = 0; j < 1000000; j++) + x = x + 1; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "icputime", NULL); + return 0; +} + +/* hrv -- loop to do lots of gethrvtime calls */ +int +hrv (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of hrv", NULL); + do + { + for (int j = 0; j < 10000; j++) + (void) gethrvtime (); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "hrv", NULL); + return 0; +} + +/* =============================================================== */ + +/* ldso -- use up time in ld.so */ + +int +ldso (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of ldso", NULL); + do + { + for (int j = 0; j < 10000; j++) + (void) dlsym (RTLD_DEFAULT, "nosuchfoo"); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "ldso", NULL); + return 0; +} + +/* masksignals -- debug aid -- call routine to mask SIGPROF and SIGEMT */ +int +masksignals (int n) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + whrvlog (gethrtime () - start, getmyvtime () - vstart, "masksignals", NULL); + return 0; +} + +/* =============================================================== */ +/* muldiv -- loop to do a bunch of integer multiply and divide */ + +volatile int tmp_ival = 0; + +int +muldiv (int n) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of muldiv", NULL); + do + { + for (int i = 0; i < 1000; i++) + { + for (int j = 0; j < 1000; j++) + tmp_ival = j * i / (i + 1.0); + } + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "muldiv", NULL); + return 0; +} + + +/* =============================================================== */ +/* underflow -- loop triggering arithmetic underflow */ +volatile float tmp_fval; + +int +underflow (int k) +{ + float x, y; + long long count = 0; + + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of underflow", NULL); + do + { + x = 1.e-20; + y = 1.e-20; + for (int j = 0; j < 50000; j++) + tmp_fval = x * y; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "underflow", NULL); + return 0; +} + +/* naptime -- spend time in the system sleeping */ +int +naptime (int k) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of naptime", NULL); + if (k == 0) + { + k = testtime; + if (k < 1) + k = 1; + } + for (int i = 0; i < k; i++) + sleep (1); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "naptime", NULL); + return 0; +} + +/* recurse -- loop to show recursion */ +int real_recurse (int, int); /* real routine to do recursion */ + +int +recurse (int k) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of recurse", NULL); + if (k == 0) + k = 80; + (void) real_recurse (0, k); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "recurse", NULL); + return 0; +} + +int +recursedeep (int k) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of recursedeep", NULL); + if (k == 0) + k = 500; + (void) real_recurse (0, k); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "recursedeep", NULL); + return 0; +} + +static int rec_count = 0; + +int +real_recurse (int i, int imax) +{ + if (i == imax) + { + volatile float x; + long long count = 0; + hrtime_t start = gethrtime (); + do + { + x = 0.0; + for (int j = 0; j < 10000000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + return rec_count; + } + else + { + real_recurse (i + 1, imax); + rec_count++; + return rec_count; + } +} + +/* gpf -- simple example showing the gprof fallacy */ +float gpf_a (void); +float gpf_b (void); +float gpf_work (int); + +#define MAX_GPF_WORK_COUNT 1000 + +int +gpf (int k) +{ + long long count = 0; + float x = -1.0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of gpf", NULL); + + do + { + x = gpf_a (); + x += gpf_b (); + count++; + if (count == MAX_GPF_WORK_COUNT) + fprintf (stderr, "Execution error -- %lld iterations of gpf_[ab]; possible compiler bug\n", + count); + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + if (x < 0.0) + fprintf (stderr, "Execution error -- x < 0.0; possible compiler bug (x = %f)\n", x); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "gpf - total", NULL); + return 0; +} + +float +gpf_a () +{ + float x = -1.0; + for (int i = 0; i < 9; i++) + x += gpf_work (1); + return x; +} + +float +gpf_b () +{ + float x = -1.0; + x = gpf_work (10); + return x; +} + +float +gpf_work (int amt) +{ + volatile float x = 0.0; + int imax = 4 * amt * amt; + for (int i = 0; i < imax; i++) + { + x = 0.0; + for (int j = 0; j < 200000; j++) + x = x + 1.0; + } + return x; +} + +/* bounce -- example of indirect recursion */ +void bounce_a (int, int); +void bounce_b (int, int); + +int +bounce (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of bounce", NULL); + if (k == 0) + k = 20; + do + { + bounce_a (0, k); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "bounce", NULL); + return 0; +} + +void +bounce_a (int i, int imax) +{ + if (i == imax) + { + volatile float x = 0.0; + for (int k = 0; k < 8; k++) + { + for (int j = 0; j < 2000000; j++) + x = x + 1.0; + } + return; + } + else + bounce_b (i, imax); +} + +void +bounce_b (int i, int imax) +{ + bounce_a (i + 1, imax); + return; +} + +/* =============================================================== */ + +/* sched -- spend time calling sched_yield() */ + +int +sched (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of sched", NULL); + if (k == 0) + { + k = testtime; + if (k < 1) + k = 1; + } + do + { + for (int i = 0; i < 1000000; i++) + sched_yield (); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "sched", NULL); + return 0; +} + +/* synccall -- spend time calling sync() */ +int +synccall (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of synccall", NULL); + if (k == 0) + { + k = testtime; + if (k < 1) + k = 1; + } + do + { + sync (); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld sync() calls\n", count); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "synccall", NULL); + return 0; +} + +/* sigtime -- spend time in a signal handler */ +static void sigtime_handler (int); + +int +sigtime (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of sigtime", NULL); + + /* set up the signal handler */ + sigset (SIGHUP, sigtime_handler); + do + { + kill (getpid (), SIGHUP); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + sigset (SIGHUP, SIG_DFL); + fprintf (stderr, " Sent %lld SIGHUP signals\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "sigtime", NULL); + return 0; +} + +static void +sigtime_handler (int sig) +{ + volatile int x; + for (int i = 0; i < 50; i++) + { + x = 0; + for (int j = 0; j < 1000000; j++) + x = x + 1; + } + return; +} + +/* systime -- spend time in a few system calls */ +int +systime (int k) +{ + struct timeval ttime; + int j; + long long count = 0; + double t = testtime / 5; + if (t < 1.0) + t = 1.0; + hrtime_t start = gethrtime (); + hrtime_t rstart = start; + hrtime_t vstart = getmyvtime (); + hrtime_t rvstart = vstart; + + /* Log the event */ + wlog ("start of systime", NULL); + + /* do gettimeofday calls */ + do + { + for (j = 0; j < 30000; j++) + { + (void) gettimeofday (&ttime, NULL); + } + count++; + } + while (start + t * 1e9 > gethrtime ()); + + hrtime_t end = gethrtime (); + hrtime_t vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations gettimeofday\n", count); + whrvlog (end - start, vend - vstart, "systime -- 10**6 gettimeofday", NULL); + + /* do gethrtime calls */ + start = gethrtime (); + vstart = getmyvtime (); + count = 0; + do + { + (void) gethrtime (); + count++; + } + while (start + t * 1e9 > gethrtime ()); + + end = gethrtime (); + vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations gethrtime\n", count); + whrvlog ((end - start), (vend - vstart), "systime -- 10**6 gethrtime", NULL); + + /* do pairs of gethrtime calls */ + start = gethrtime (); + vstart = getmyvtime (); + + count = 0; + do + { + for (j = 0; j < 30000; j++) + { + (void) gethrtime (); + (void) gethrtime (); + } + count++; + } + while (start + t * 1e9 > gethrtime ()); + + end = gethrtime (); + vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations pairs of gethrtime\n", + count); + whrvlog (end - start, vend - vstart, "systime -- 10**6 pairs of gethrtime", + NULL); + + /* do gethrvtime calls */ + start = gethrtime (); + vstart = getmyvtime (); + + count = 0; + do + { + for (j = 0; j < 30000; j++) + { + (void) gethrvtime (); + } + count++; + } + while (start + t * 1e9 > gethrtime ()); + + end = gethrtime (); + vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations gethrvtime\n", count); + whrvlog (end - start, vend - vstart, "systime -- 10**6 gethrvtime", NULL); + + /* do getrusage calls */ + start = gethrtime (); + vstart = getmyvtime (); + + count = 0; + do + { + for (j = 0; j < 30000; j++) + { + struct rusage rusage; + (void) getrusage (RUSAGE_SELF, &rusage); + } + count++; + } + while (start + t * 1e9 > gethrtime ()); + + end = gethrtime (); + vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations getrusage\n", count); + whrvlog ((end - start), (vend - vstart), "systime -- 10**6 getrusage", NULL); + whrvlog ((gethrtime () - rstart), (getmyvtime () - rvstart), "systime", NULL); + return 0; +} + +/* unwindcases -- test various unwind corner cases */ +static void unwindcases_handler (int); + +int +unwindcases (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of unwindcases", NULL); + + /* set up the signal handler */ + sigset (SIGHUP, unwindcases_handler); + + /* initialize the new signal mask */ + sigset_t new_mask; + sigset_t old_mask; + sigfillset (&new_mask); + sigdelset (&new_mask, SIGHUP); + + /* block all signals except SIGHUP*/ + sigprocmask (SIG_SETMASK, &new_mask, &old_mask); + do + { + kill (getpid (), SIGHUP); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + sigprocmask (SIG_SETMASK, &old_mask, NULL); + sigset (SIGHUP, SIG_DFL); + fprintf (stderr, " Sent %lld SIGHUP signals\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "unwindcases", NULL); + return 0; +} + +#define unwindcases_memcpy memcpy +#define unwindcases_memset memset +#define unwindcases_memnum (4096) + +static char unwindcases_array[4097]; +static volatile int srcind = 1024; + +static void +unwindcases_handler (int sig) +{ + for (int i = 0; i < 1000; i++) + { + for (int j = 0; j < 1000; j++) + { + unwindcases_memset ((void*) unwindcases_array, 0, unwindcases_memnum); + for (int k = 0; k < 10; k++) + { + unwindcases_array[k] = unwindcases_array[srcind]; + unwindcases_array[k + srcind / 4] = 0; + unwindcases_array[k] = unwindcases_array[strlen (unwindcases_array + k) + 1]; + } + unwindcases_memcpy ((void*) unwindcases_array, + (void*) (unwindcases_array + 4096 / 2), + unwindcases_memnum / 2); + } + } + return; +} + +/* tailcallopt -- call routines that would be tail-call optimized when + * compiled with optimization + */ +void tailcall_a (void); +void tailcall_b (void); +void tailcall_c (void); + +int +tailcallopt (int n) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of tailcallopt", NULL); + do + { + tailcall_a (); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "tailcallopt", NULL); + return 0; +} + +void +tailcall_a () +{ + volatile float x = 0.0; + for (int j = 0; j < 4000000; j++) + x = x + 1.0; + tailcall_b (); +} + +void +tailcall_b () +{ + volatile float x = 0.0; + for (int j = 0; j < 4000000; j++) + x = x + 1.0; + tailcall_c (); +} + +void +tailcall_c () +{ + volatile float x = 0.0; + for (int j = 0; j < 4000000; j++) + x = x + 1.0; +} + +int +itimer_realprof (int k) /* mess with itimer ITIMER_REALPROF */ +{ + struct itimerval tval; + int retval; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* set an itimer */ + if (k != 0) + { + wlog ("start of itimer_realprof", NULL); + tval.it_interval.tv_sec = 1; + tval.it_interval.tv_usec = 300000; + tval.it_value = tval.it_interval; + } + else + { + wlog ("start of itimer_realprof(0)", NULL); + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 0; + tval.it_value = tval.it_interval; + } + retval = setitimer (ITIMER_REALPROF, &tval, 0); + if (retval != 0) + fprintf (stderr, "setitimer(ITIMER_REALPROF) got %d returned: %s\n", + retval, strerror (errno)); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "itimer_realprof", + NULL); + return 0; +} + +static struct sigaction old_sigprof_handler; +static void sigprof_handler (int sig); +static void sigprof_sigaction (int sig, siginfo_t *sip, ucontext_t *uap); + +int +sigprof (int k) +{ + struct sigaction act; + + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of sigprof", NULL); + + /* query current handler */ + if (sigaction (SIGPROF, NULL, &act) == -1) + printf ("\tFailed current sigaction query: %s\n", strerror (errno)); + else + printf ("\tCurrently installed sigaction 0x%p\n", act.sa_sigaction); + + sigemptyset (&act.sa_mask); + act.sa_flags = SA_RESTART | SA_SIGINFO; + act.sa_sigaction = (void(*)(int, siginfo_t*, void*))sigprof_sigaction; + + if (k != 0) + { + /* install with deferral to original handler (if set) */ + if (sigaction (SIGPROF, &act, &old_sigprof_handler) == -1) + printf ("\tFailed to install sigprof_sigaction: %s\n", strerror (errno)); + if (old_sigprof_handler.sa_sigaction == (void (*)(int, siginfo_t *, void *))SIG_DFL) + { + old_sigprof_handler.sa_sigaction = (void (*)(int, siginfo_t *, void *))SIG_IGN; + printf ("\tReplaced default sigprof handler with 0x%p\n", + act.sa_sigaction); + } + else + printf ("\tReplaced sigprof handler 0x%p with 0x%p\n", + old_sigprof_handler.sa_sigaction, act.sa_sigaction); + } + else + { + /* installed without deferral to any original handler */ + old_sigprof_handler.sa_sigaction = (void (*)(int, siginfo_t *, void *))SIG_IGN; + if (sigaction (SIGPROF, &act, NULL) == -1) + printf ("\tFailed to install sigprof_sigaction: %s\n", strerror (errno)); + else + printf ("\tInstalled sigprof_sigaction 0x%p\n", act.sa_sigaction); + } + + whrvlog (gethrtime () - start, getmyvtime () - vstart, "sigprof", NULL); + return 0; +} + +int +sigprofh (int k) +{ + struct sigaction act; + + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of sigprofh", NULL); + + /* query current handler */ + if (sigaction (SIGPROF, NULL, &act) == -1) + printf ("\tFailed current sigaction query: %s\n", strerror (errno)); + else + printf ("\tCurrently installed handler 0x%p\n", act.sa_handler); + + sigemptyset (&act.sa_mask); + act.sa_flags = SA_RESTART; + act.sa_handler = sigprof_handler; + if (k != 0) + { + /* install with deferral to original handler (if set) */ + if (sigaction (SIGPROF, &act, &old_sigprof_handler) == -1) + printf ("\tFailed to install sigprof_handler: %s\n", strerror (errno)); + if (old_sigprof_handler.sa_handler == SIG_DFL) + { + old_sigprof_handler.sa_handler = SIG_IGN; + printf ("\tReplaced default sigprof handler with 0x%p\n", + act.sa_handler); + } + else + printf ("\tReplaced sigprof handler 0x%p with 0x%p\n", + old_sigprof_handler.sa_handler, act.sa_handler); + } + else + { + /* installed without deferral to any original handler */ + old_sigprof_handler.sa_handler = SIG_IGN; + if (sigaction (SIGPROF, &act, NULL) == -1) + printf ("\tFailed to install sigprof_handler: %s\n", strerror (errno)); + else + printf ("\tInstalled sigprof_handler 0x%p\n", act.sa_handler); + } + + whrvlog (gethrtime () - start, getmyvtime () - vstart, "sigprofh", NULL); + return 0; +} + +static void +sigprof_handler (int sig) +{ + int j; + volatile int x; + + hrtime_t now = gethrtime (); + if (old_sigprof_handler.sa_handler == SIG_IGN) + { + whrvlog (now, 0, "sigprof_handler (ign)", NULL); + for (j = 0, x = 0; j < 1000000; j++) + x = x + 1; + } + else + { + whrvlog (now, 0, "sigprof_handler (fwd)", NULL); + for (j = 0, x = 0; j < 1000000; j++) + x = x + 1; + /* forward signal to original handler */ + if (old_sigprof_handler.sa_flags & SA_SIGINFO) + (old_sigprof_handler.sa_sigaction)(sig, NULL, NULL); + else + (old_sigprof_handler.sa_handler)(sig); + printf ("\tReturned from original sigprof handler!\n"); + } + + return; +} + +static void +sigprof_sigaction (int sig, siginfo_t *sip, ucontext_t *uap) +{ + int j; + volatile int x; + + hrtime_t now = gethrtime (); + if (old_sigprof_handler.sa_sigaction == (void (*)(int, siginfo_t *, void *))SIG_IGN) + { + whrvlog (now, 0, "sigprof_sigaction (ign)", NULL); + for (j = 0, x = 0; j < 1000000; j++) + x = x + 1; + } + else + { + whrvlog (now, 0, "sigprof_sigaction (fwd)", NULL); + for (j = 0, x = 0; j < 1000000; j++) + x = x + 1; + /* forward signal to original handler */ + if (old_sigprof_handler.sa_flags & SA_SIGINFO) + (old_sigprof_handler.sa_sigaction)(sig, sip, uap); + else + (old_sigprof_handler.sa_handler)(sig); + printf ("\tReturned from original sigprof sigaction!\n"); + } + return; +} + +#if 0 +Need to consider various signal handler / sigaction scenarios : + +1. A handler is already installed, and a new handler is being installed. +(The original handler may be one of the defaults.) +2. A handler is already installed, and a sigaction is being installed. +3. A sigaction is already installed, and a new sigaction is being installed. +4. A sigaction is already installed, and a handler is being installed. +#endif + +int +do_chdir (int k) /* switch to a new working directory */ +{ + char *workdir; + char *workdir0 = "/tmp"; + char *workdir1 = "/"; + char currworkdir[MAXPATHLEN]; + + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + if (k != 0) + { + wlog ("start of do_chdir(X)", NULL); + workdir = workdir1; + } + else + { + wlog ("start of do_chdir", NULL); + workdir = workdir0; + } + + if (getcwd (currworkdir, sizeof (currworkdir)) == NULL) + fprintf (stderr, "old getcwd failed: %s\n", strerror (errno)); + else + printf ("old getcwd returned \"%s\"\n", currworkdir); + + if (chdir (workdir) != 0) + fprintf (stderr, "chdir(\"%s\") failed: %s\n", workdir, strerror (errno)); + + if (getcwd (currworkdir, sizeof (currworkdir)) == NULL) + fprintf (stderr, "new getcwd failed: %s\n", strerror (errno)); + else + printf ("new getcwd returned \"%s\"\n", currworkdir); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "do_chdir", NULL); + return 0; +} + +int +do_exec (int k) /* do an exec() call */ +{ + sprintf (new_name, "_SP_NAME=%s_x%d", acct_file, ++syn_exec); + if (putenv (new_name)) + fprintf (stderr, "Failed to name child! %s\n", strerror (errno)); + if (k >= 0) + { + wlog ("about to exec", NULL); + execl ("./synprog", "synprog", "gpf.cpu.sx", NULL); + wlog ("exec failed!!!", NULL); + } + else + { + wlog ("about to execX", NULL); + execl ("./no-such-file", "no-such-file", "gpf.cpu.sx", NULL); + wlog ("execX failed (as expected)", NULL); + } + return 0; +} + +/* preloading libcollector to a setuid executable will fail! */ +const char *cmdX = "/random/crash_n_burn"; +const char *cmd0 = "/bin/uptime"; +const char *cmd1 = "/bin/echo hello world!"; +const char *cmd2 = "/usr/bin/sleep 5"; +const char *cmd3 = "/usr/bin/sleep 5; /bin/echo hello world!"; +const char *cmd4 = "/usr/bin/sleep 2; /bin/echo hello world!; /usr/bin/sleep 2"; +const char *cmd5 = "/bin/date; /bin/sleep 2; /bin/date; /bin/sleep 2; /bin/date"; +const char *cmd6 = "w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w"; +const char *cmd7 = "synprog"; +const char *cmd8 = "synprog icpu.sx 2>&1"; + +int +do_popen (int k) /* do a popen() call */ +{ + int ret; + FILE *fd; + char buf[BUFSIZ]; + const char *mode = "r"; + + /* XXXX popen() will temporarily vfork+exec() a new child */ + /* but there will be no accounting for it, unless it's synprog! */ + sprintf (new_name, "_SP_NAME=%s_c%d", acct_file, ++syn_combo); + if (putenv (new_name)) + fprintf (stderr, "Failed to name child! %s\n", strerror (errno)); + + /* ignore reapchild to catch child here */ + (void) sigset (SIGCHLD, 0); + if (k >= 0) + { + wlog ("about to popen", NULL); + fd = popen (cmd8, mode); + } + else + { + wlog ("about to popenX!", NULL); + fd = popen (cmdX, mode); + } + if (fd == NULL) + printf ("do_popen failed: %s\n", strerror (errno)); + else + printf ("do_popen succeeded: fileno=%d\n", fileno (fd)); + + /* restore pre-popen environment */ + sprintf (new_name, "_SP_NAME=%s", acct_file); + if (putenv (new_name)) + fprintf (stderr, "Failed to restore name! %s\n", strerror (errno)); + + if (fd != NULL) + { + while (fgets (buf, BUFSIZ, fd) != NULL) + printf ("& %s", buf); + + if ((ret = pclose (fd)) == -1) + printf ("do_popen pclose error: %s\n", strerror (errno)); + else + printf ("do_popen pclose returned %d\n", ret); + } + + /* set up to reap any children */ + (void) sigset (SIGCHLD, reapchild); + return 0; +} + +int +do_system (int k) /* do a system() call */ +{ + int ret; + + /* XXXX system() will temporarily vfork+exec() a new child */ + /* but there will be no accounting for it, unless it's synprog! */ + sprintf (new_name, "_SP_NAME=%s_c%d", acct_file, ++syn_combo); + if (putenv (new_name)) + fprintf (stderr, "Failed to name child! %s\n", strerror (errno)); + + if (k >= 0) + { + wlog ("about to system", NULL); + ret = system (cmd8); + } + else + { + wlog ("about to systemX!", NULL); + ret = system (cmd0); + } + if (ret < 0) + printf ("do_system failed: %s\n", strerror (errno)); + else + printf ("do_system succeeded, ret=%d\n", ret); + + /* restore pre-system environment */ + sprintf (new_name, "_SP_NAME=%s", acct_file); + if (putenv (new_name)) + fprintf (stderr, "Failed to restore name! %s\n", strerror (errno)); + return 0; +} + +int +do_forkexec (int k) /* do a fork()+exec() call combo */ +{ + int ret, pid; + int status = -1; + char arg0[128], arg1[128]; + arg1[0] = (char) 0; + + /* ignore reapchild to catch child here */ + (void) sigset (SIGCHLD, 0); + + sprintf (child_name, "%s_f%d", acct_file, ++syn_fork); + if ((pid = fork ()) == 0) + { + syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */ + strcpy (acct_file, child_name); + acct_init (acct_file); + sprintf (new_name, "_SP_NAME=%s_x%d", acct_file, ++syn_exec); + if (putenv (new_name)) + { + fprintf (stderr, "Failed to name fork child! %s\n", strerror (errno)); + } + (void) execl (arg0, "fork+exec", arg1[0] ? arg1 : NULL, NULL); + fprintf (stderr, "fork execl failed! %s\n", strerror (errno)); + _exit (127); + } + else if (pid == -1) + fprintf (stderr, "fork failed! %s\n", strerror (errno)); + else + { + do + { + ret = waitpid (pid, &status, WNOHANG | WUNTRACED); + } + while ((ret == -1) && (errno == EINTR)); + + if (ret == -1) + fprintf (stderr, "waitpid failed: %s\n", strerror (errno)); +#if 0 + else + { + if (WIFEXITED (status)) + printf ("WEXITSTATUS=%d\n", WEXITSTATUS (status)); + if (WIFSTOPPED (status)) + printf ("WSTOPSIG=%d\n", WSTOPSIG (status)); + if (WIFSIGNALED (status)) + printf ("WTERMSIG=%d\n", WTERMSIG (status)); + if (WIFCONTINUED (status)) + printf ("WIFCONTINUED=%d\n", WIFCONTINUED (status)); + } +#endif + if (WIFEXITED (status)) + printf ("do_forkexec succeeded: child exit status=%d\n", + WEXITSTATUS (status)); + else + printf ("do_forkexec failed! status=%d\n", status); + } + + /* set up to reap any children */ + (void) sigset (SIGCHLD, reapchild); + return 0; +} + +int +do_vforkexec (int k) /* do a vfork()+exec() call combo */ +{ + int ret, pid; + int status = 1; + char arg0[128], arg1[128]; + arg1[0] = (char) 0; + /* ignore reapchild to catch child here */ + (void) sigset (SIGCHLD, 0); + + sprintf (child_name, "%s_f%d", acct_file, ++syn_fork); + + if ((pid = vfork ()) == 0) + { + syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */ + strcpy (acct_file, child_name); + acct_init (acct_file); + sprintf (new_name, "_SP_NAME=%s_x%d", acct_file, ++syn_exec); + if (putenv (new_name)) + fprintf (stderr, "Failed to name vfork child! %s\n", strerror (errno)); + (void) execl (arg0, "vfork+exec", arg1[0] ? arg1 : NULL, NULL); + printf ("vfork execl failed! %s\n", strerror (errno)); + _exit (127); + } + else if (pid == -1) + fprintf (stderr, "vfork failed! %s\n", strerror (errno)); + else + { + do + { + ret = waitpid (pid, &status, WNOHANG | WUNTRACED); + } + while (ret == -1 && errno == EINTR); + + if (ret == -1) + fprintf (stderr, "waitpid failed: %s\n", strerror (errno)); +#if 0 + else + { + if (WIFEXITED (status)) + printf ("WEXITSTATUS=%d\n", WEXITSTATUS (status)); + if (WIFSTOPPED (status)) + printf ("WSTOPSIG=%d\n", WSTOPSIG (status)); + if (WIFSIGNALED (status)) + printf ("WTERMSIG=%d\n", WTERMSIG (status)); + if (WIFCONTINUED (status)) + printf ("WIFCONTINUED=%d\n", WIFCONTINUED (status)); + } +#endif + if (WIFEXITED (status)) + printf ("do_vforkexec succeeded: child exit status=%d\n", + WEXITSTATUS (status)); + else + printf ("do_vforkexec failed! status=%d\n", status); + } + + /* set up to reap any children */ + (void) sigset (SIGCHLD, reapchild); + return 0; +} diff --git a/gprofng/testsuite/lib/Makefile.skel b/gprofng/testsuite/lib/Makefile.skel new file mode 100644 index 0000000..7134c27 --- /dev/null +++ b/gprofng/testsuite/lib/Makefile.skel @@ -0,0 +1,61 @@ +# Skeleton makefile for display tests +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +CC = gcc +CFLAGS = -g -Wall +SHAREDOPT = -fpic -shared + +#JAVABIN = /usr/java/latest/bin +JAVABIN = $(shell dirname `which java`) +JAVA = $(JAVABIN)/java +JAVAC = $(JAVABIN)/javac + +COLLECT_FLAGS = -p on +TARGET_FLAGS = +DISPLAY_FLAGS = -func +GPROFNG_OPT = -func + +GPROFNG = gprofng +COLLECT = $(GPROFNG) collect app +DISPLAY = $(GPROFNG) display text + +EXPERIMENT = test.er +DISPLAY_LOG = display.log + + +export LD_LIBRARY_PATH := $(shell dirname $$(find ../root -name libgprofng.so.0 | head -1)) + +.PHONY: all collect compare clobber clean + +all: compare + +# We intentionally use incomplete dependencies here, because we don't want to +# regenerate test.er during the later display/compare phases. +collect: $(EXPERIMENT) + +$(DISPLAY_LOG): $(EXPERIMENT) + $(DISPLAY) $(DISPLAY_FLAGS) $(EXPERIMENT) > $@ + +compare: $(DISPLAY_LOG) + perl -I $(srcdir)/../../lib $(srcdir)/check_results.pl $(ACCT_FILE) $(DISPLAY_LOG) + +clobber clean: + rm -rf *.er + rm -f *.acct *.acct2 *.log core* *.class *.o $(TARGETS) *.out diff --git a/gprofng/testsuite/lib/acct.pm b/gprofng/testsuite/lib/acct.pm new file mode 100644 index 0000000..1b0168e --- /dev/null +++ b/gprofng/testsuite/lib/acct.pm @@ -0,0 +1,774 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +use strict; +package acct; +use vars qw(%Acct $Erp); +my($debug_f, $retVal, $OpenDis, $OpenFsingle, $Read_rules_txt); +my(@Comparison, @hashSample, @acctHeader); +my(%RANGE, %Rules); +my($ERROR_ACCT_MISMATCH, $ERROR_NEGATIVE_TIME, $ERROR_PERL_ERROR, + $ERROR_DIFF_RANGE, $ERROR_ZERO_METRIC, $ERROR_HIGH_UNKNOWN, + $ERROR_CALLER_VERIF, $ERROR_SIGNAL_LOST); + +BEGIN { +# use Exporter (); +# @ISA = 'Exporter'; +# @EXPORT_OK = ('&readAcct', '%Acct'); + $debug_f = $ENV{PERL_DEBUG}; + $retVal = 0; + $OpenDis = 0; + $OpenFsingle = 0; + $#Comparison = -1; + $Read_rules_txt = 0; + $Erp = {}; + @hashSample = []; + + %RANGE = ( + Count => { P_RANGE => 0, P_RATE => 0, + N_RANGE => 0, N_RATE => 0, FMT => "%d" + }, + Total => { P_RANGE => 0.20, P_RATE => 3, + N_RANGE => -0.20, N_RATE => -3, FMT => "%6.3f" + }, + Cpu => { P_RANGE => 0.5, P_RATE => 10, + N_RANGE => -0.5, N_RATE => -10, FMT => "%6.3f" + ,P_RANGE_2AVG => 0.5, P_RATE_2AVG => 10, + N_RANGE_2AVG => -0.5, N_RATE_2AVG => -10 + }, + Cycles => { P_RANGE => 0.5, P_RATE => 10, + N_RANGE => -0.5, N_RATE => -10, FMT => "%6.3f" + ,P_RANGE_2AVG => 0.5, P_RATE_2AVG => 10, + N_RANGE_2AVG => -0.5, N_RATE_2AVG => -10 + }, + Cycles1 => { P_RANGE => 0.5, P_RATE => 10, + N_RANGE => -0.5, N_RATE => -10, FMT => "%6.3f" + ,P_RANGE_2AVG => 0.5, P_RATE_2AVG => 10, + N_RANGE_2AVG => -0.5, N_RATE_2AVG => -10 + }, + Sync => { P_RANGE => 0.5, P_RATE => 3, + N_RANGE => -0.5, N_RATE => -3, FMT => "%6.3f" + }, + Unkn => { P_RANGE => 0.10, P_RATE => 0.5, FMT => "%6.3f" } + ); + + $ERROR_SIGNAL_LOST = 44; + $ERROR_DIFF_RANGE = 84; + $ERROR_HIGH_UNKNOWN = 85; + $ERROR_PERL_ERROR = 86; + $ERROR_ACCT_MISMATCH = 87; + $ERROR_CALLER_VERIF = 88; + $ERROR_ZERO_METRIC = 94; + $ERROR_NEGATIVE_TIME = 103; +} + +sub debug +{ + my ($lineN, $fmt); + if ( $debug_f == 0 ) { + return; + } + $lineN = shift @_; + $fmt = shift @_; + if ( $debug_f == 2 ) { + warn "DEBUG:#$lineN:\n"; + } + warn sprintf($fmt, @_); +} + +sub set_retVal +{ + if ( $retVal == 0 ) { + $retVal = $_[0]; + if ($retVal != 0 ) { + warn sprintf("DEBUG: retVal=%d\n", $retVal); + } + } + return $retVal; +} + +sub diffRule +{ + # The format of the comparison rule is: + # <Name>, <Column number in *.acct>, <Column number in erprint.out>, <message> + # Cpu, 3, 1 + # Total, 2, 3 + my ($str) = @_; + my (@arr); + + @arr = split (/,/, $str); + if ($#arr == 2) { + # Old version + push @arr, $arr[0]; + } + push @Comparison, [@arr]; +} + +sub read_rules +{ + my ($name, $rule, $line, @arr); + return if ( $Read_rules_txt == 1); + $Read_rules_txt = 1; + open(FP, "<rules.txt") or return; + while ($line = <FP>) { + chomp ($line); + $line =~ s/\s*//g; # Remove all blanks + $line =~ s/\\s/ /g; # Replace \s with space + next if ( $line =~ m/^$/ ); + next if ( $line =~ m/^#/ ); + + if ( $line =~ m/=/ ) { + # Set a calculation rule + ($name, $rule) = split (/=/, $line); + $Rules{$name} = [split(/\+/, $rule)]; + next; + } + + # Set a comparison rule + &diffRule($line); + } + close(FP); +} + +sub dump_acct() +{ + my ($i, $n, $key, $fmt, @fmt_head); + printf "dump_acct:\n"; + foreach $i ( @acctHeader ) { + $fmt = sprintf("%%%ds ", length($i)); + push @fmt_head, $fmt; + printf $fmt, $i; + } + printf "\n"; + foreach $key (sort keys %Acct) { + $n = 0; + foreach $i ( @{$Acct{$key}} ) { + $fmt = $n <= $#fmt_head ? $fmt_head[$n] : " %10s"; + $n++; + printf $fmt, $i; + } + printf " '%s'", $key; + if ( exists $Rules{$key} ) { + printf " := %s", join(" + ", @{$Rules{$key}}); + } + printf "\n"; + } +} + +sub readAcct +{ + # Read the *.acct file into hash $Acct with the function name as key. + # The format of *.acct is : + # X <time1> ... <timeN> <func_name> + my ($fileName, @checkTime) = @_; + my ($name, $i, $key, $line, @arr); + + # file *.acct is generated while the test program is running. + if (!open(FP, "<$fileName")) { + printf "acct::readAcct: Cannot open '%s'\n\n", $fileName; + exit($ERROR_ACCT_MISMATCH); + } + while ($line = <FP>) { # Skip the first lines (header) + last if ( $line =~ m/^X\s+/ ); + } + @acctHeader = split (/\s+/, $line); + push @acctHeader, "Comment"; + while ($line = <FP>) { + chomp($line); + $line =~ s/^\s*//; # Delete leading spaces + next if ( $line =~ m/^$/ ); + @arr = split (/\s+/, $line); + $name = pop(@arr); + if (defined $Acct{$name}) { + for ($i = 1; $i <= $#arr; $i++ ) { + $Acct{$name}[$i] += $arr[$i]; + } + } else { + $Acct{$name} = [ @arr ]; + } + + foreach $i ( @checkTime ) { + next if ($i > $#arr); + if ( $arr[$i] < 0 ) { + &set_retVal($ERROR_NEGATIVE_TIME); + last; + } + } + } + close(FP); + + &read_rules; + # &checkCallersCallees; + + if ( $debug_f != 0 ) { + printf "\nreadAcct: '%s'\n", $fileName; + printf "checkTime: "; + if( $#checkTime == -1 ) { + printf "<None>\n"; + } else { + print "[ ", join(", ", @checkTime), " ]\n"; + } + foreach $i ( @Comparison ) { + print "Comparison rule: ", join(", ", @{$i}), "\n"; + } + &dump_acct; + printf "\n"; + } +} + + +sub read_er_print_out +{ + my ($fileName, $colName) = @_; + my ($name, @arr, $head_f, $line, $key, $i); + + $Erp = {}; + $head_f = 1; + open(FP, "<$fileName") or return; + while ($line = <FP>) { + chomp($line); + $line =~ s/^\s*//; # Delete leading spaces + next if ( $line =~ m/^$/ ); + if ($head_f == 1) { + # Skip the first lines (header) + next unless ( $line =~ m/^\d/ ); + next unless ( ($line =~ m/<Total>\s*$/) || + ($line =~ m/<Stack-unwind-failed>\s*$/) ); + $head_f = 0; + if ($colName == -1) { + @arr = split (/\s+/, $line); + $colName = $#arr + 1; + } + } + @arr = split (/\s+/, $line, $colName); + $name = pop(@arr); + if (defined $Erp->{$name}) { + for ($i = 0; $i <= $#arr; $i++ ) { + $Erp->{$name}[$i] += $arr[$i]; + } + } else { + $Erp->{$name} = [ @arr ]; + } + + $i = index($name, "("); + if ($i > 0) { + my $funcName = substr($name, 0, $i); + if (defined $Erp->{$funcName}) { + for ($i = 0; $i <= $#arr; $i++ ) { + $Erp->{$funcName}[$i] += $arr[$i]; + } + } else { + $Erp->{$funcName} = [ @arr ]; + } + } + } + close(FP); + + if ( $debug_f != 0 ) { + printf "read_er_print_out:\n"; + foreach $key (sort keys %{$Erp}) { + foreach $i ( @{$Erp->{$key}} ) { + printf " %10s", $i; + } + printf " %-10s", "'$key'"; + if ( exists $Rules{$key} ) { + printf " += %s", join(" + ", @{$Rules{$key}}); + } + printf "\n"; + } + } +} + + +sub createKDiff +{ + my ($colSample) = @_; + my ($key, $str, $i, $head_str); + + open(DIFF_fp, ">diff.out"); + $head_str = "X"; + for $i ( 0..$#Comparison ) { + $head_str .= &get_head_str($i); + } + $head_str .= " Name"; + printf DIFF_fp "%s\n", $head_str; + foreach $key (sort keys %Acct) { + # Restore a hash 'Erp' + $Erp = $hashSample[$Acct{$key}[$colSample]]; + $str = &doComp($key, $head_str); + printf DIFF_fp "%s (Sample %d)\n", $str,$Acct{$key}[$colSample]; + } + close(DIFF_fp); + &closeDisFile(); +} + +sub commandToScr1_fp() +{ + my ($str) = @_; + printf Scr1_fp "#\n#%s\n%s\n", $str, $str; +} + +sub openFsingleScr +{ + return if ($OpenFsingle == 1); + open(Scr1_fp, ">>erp_fsingle.scr"); + $OpenFsingle = 1; +} + +sub closeFsingleScr +{ + return if ($OpenFsingle != 1); + $OpenFsingle = 2; + close(Scr1_fp); +} + +sub openDisFile +{ + &openFsingleScr(); + return if ($OpenDis == 1); + open(Dis_fp, ">>discrepancy.out"); + $OpenDis = 1; +} + +sub closeDisFile +{ + &closeFsingleScr(); + return if ($OpenDis != 1); + $OpenDis = 2; + close(Dis_fp); +} + +sub with_diff +{ + my ($i) = @_; + my ($key); + + $key = $Comparison[$i][0]; + if( ! exists $RANGE{$key} ) { + printf "acct::with_diff: '$key' is a wrong key\n\n"; + exit $ERROR_PERL_ERROR; + } + if ($RANGE{$key}->{FMT} !~ m/^%d/) { + return 1; + } + return 0; +} + +sub get_head_str() +{ + my ($i) = @_; + my ($str); + $str = $Comparison[$i][3]; + while (length($str) < 16) { + $str = "*" . $str . "*"; + } + if (with_diff($i)) { + return sprintf("| %17s %7s %7s %s", $str, "Diff", "%", "x"); + } else { + return sprintf("| %17s %s", $str, "x"); + } +} + +sub doComp +{ + my ($fname, $head_str) = @_; + my ($key, $R, $r1, $r2, $diff, $rate, $flagX, $x, $i, + $retStr, $discrepancy, $err_diff_range, $err_zero_metric, $err_acct_mismatch); + + sub setRate + { + my ($val, $diff) = @_; + return sprintf("%6.1f", ($diff/$val)*100) if ( $val != 0 ); + return sprintf("%6.1f", "0.0") if ( $diff >= -0.05 && $diff <= 0.05); + return sprintf("%6.1f", "100") if ( $diff > 0 ); + return sprintf("%6.1f", "-100"); + } + + $err_diff_range = 0; + $err_zero_metric = 0; + $err_acct_mismatch = 0; + $discrepancy = " "; + $flagX = " "; + $retStr = ""; + for $i ( 0..$#Comparison ) { + $r1 = $Acct{$fname}[$Comparison[$i][1]]; + $r2 = 0; + if ( ! exists $Rules{$fname} ) { + if ( exists $Erp->{$fname} ) { + $r2 = $Erp->{$fname}[$Comparison[$i][2]]; + } + } else { + foreach my $key1 ( @{$Rules{$fname}} ) { + my $sign = 1; + $key = $key1; + if (substr($key1, 0, 1) eq '-') { + $key = substr($key1, 1); + $sign = -1; + } + if ( exists $Erp->{$key} ) { + $r2 += $sign * $Erp->{$key}[$Comparison[$i][2]]; + } + } + } + + $key = $Comparison[$i][0]; + if( ! exists $RANGE{$key} ) { + printf "acct::doComp: '$key' is a wrong key\n\n"; + exit $ERROR_PERL_ERROR; + } + $R = $RANGE{$key}; + $r1 = sprintf($R->{FMT}, $r1); + $r2 = sprintf($R->{FMT}, $r2); + $diff = sprintf($R->{FMT}, $r1 - $r2); + $rate = &setRate($r1, $diff); + if ((( $diff > $R->{P_RANGE} ) && ( $rate >= $R->{P_RATE} )) + || ( ( $fname ne '<Unknown>') && ( $diff < $R->{N_RANGE} ) && ( $rate <= $R->{N_RATE} ))) { + $x = ($Acct{$fname}[0] eq "Y") ? "y" : "x"; + if ( $x ne "y" ) { + $flagX = "X"; + &openDisFile(); + printf Dis_fp "%s/ %s\n", $fname, $Comparison[$i][3]; + + $discrepancy .= " $Comparison[$i][3]"; + if (with_diff($i)) { + if ( $r2 > 0 ) { + $err_diff_range = $ERROR_DIFF_RANGE; + } else { + $err_zero_metric = $ERROR_ZERO_METRIC; + } + } else { + $err_acct_mismatch = $ERROR_ACCT_MISMATCH; + } + } + } else { + $x = " "; + } + + if (with_diff($i)) { + $retStr .= sprintf("| %8s %8s %7s %7s %s", $r1, $r2, $diff, $rate, $x); + } else { + $retStr .= sprintf("| %8s %8s %s", $r1, $r2, $x); + } + } + $retStr = $flagX . $retStr . sprintf(" %-10s", $fname); + if ( exists $Rules{$fname} ) { + $retStr .= sprintf " := %s", join(" + ", @{$Rules{$fname}}); + } + if ($discrepancy ne " ") { + if ($err_acct_mismatch != 0) { + $retVal = $err_acct_mismatch; + } + &set_retVal($err_zero_metric); + &set_retVal($err_diff_range); + printf Scr1_fp "#%s\n#%s\n", $head_str, $retStr; + &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', $fname)); + &commandToScr1_fp(sprintf("%s %s 1", 'csingle', $fname)); + } + return ($retStr); +} + +sub doComp2AVG +{ + my ($fname, $head_str, @avg) = @_; + my ($key, $R, $r1, $r2, $diff, $rate, $flagX, $x, $i, + $retStr, $discrepancy, $err_diff_range, $err_zero_metric, $err_acct_mismatch); + + sub setRate + { + my ($val, $diff) = @_; + return sprintf("%6.1f", ($diff/$val)*100) if ( $val != 0 ); + return sprintf("%6.1f", "0.0") if ( $diff >= -0.05 && $diff <= 0.05); + return sprintf("%6.1f", "100") if ( $diff > 0 ); + return sprintf("%6.1f", "-100"); + } + + $err_diff_range = 0; + $err_zero_metric = 0; + $err_acct_mismatch = 0; + $discrepancy = " "; + $flagX = " "; + $retStr = ""; + for $i ( 0..$#Comparison ) { + $r1 = $avg[$i]; + $r2 = 0; + if ( ! exists $Rules{$fname} ) { + if ( exists $Erp->{$fname} ) { + $r2 = $Erp->{$fname}[$Comparison[$i][2]]; + } + } else { + foreach my $key1 ( @{$Rules{$fname}} ) { + my $sign = 1; + $key = $key1; + if (substr($key1, 0, 1) eq '-') { + $key = substr($key1, 1); + $sign = -1; + } + if ( exists $Erp->{$key} ) { + $r2 += $sign * $Erp->{$key}[$Comparison[$i][2]]; + } + } + } + + $key = $Comparison[$i][0]; + if( ! exists $RANGE{$key} ) { + printf "acct::doComp: '$key' is a wrong key\n\n"; + exit $ERROR_PERL_ERROR; + } + $R = $RANGE{$key}; + $r1 = sprintf($R->{FMT}, $r1); + $r2 = sprintf($R->{FMT}, $r2); + $diff = sprintf($R->{FMT}, $r1 - $r2); + $rate = &setRate($r1, $diff); + if ((( $diff > $R->{P_RANGE_2AVG} ) && ( $rate >= $R->{P_RATE_2AVG} )) + || ( ( $fname ne '<Unknown>') && ( $diff < $R->{N_RANGE_2AVG} ) && ( $rate <= $R->{N_RATE_2AVG} ))) { + $flagX = "X"; + $x = "x"; + $discrepancy .= " $Comparison[$i][3]"; + if (with_diff($i)) { + if ( $r2 > 0 ) { + $err_diff_range = $ERROR_DIFF_RANGE; + } else { + $err_zero_metric = $ERROR_ZERO_METRIC; + } + } else { + $err_acct_mismatch = $ERROR_ACCT_MISMATCH; + } + } else { + $x = " "; + } + + if (with_diff($i)) { + $retStr .= sprintf("| %8s %8s %7s %7s %s", $r1, $r2, $diff, $rate, $x); + } else { + $retStr .= sprintf("| %8s %8s %s", $r1, $r2, $x); + } + } + $retStr = $flagX . $retStr . sprintf(" %-10s", $fname); + if ( exists $Rules{$fname} ) { + $retStr .= sprintf " := %s", join(" + ", @{$Rules{$fname}}); + } + if ($discrepancy ne " ") { + if ($err_acct_mismatch != 0) { + $retVal = $err_acct_mismatch; + } + &set_retVal($err_zero_metric); + &set_retVal($err_diff_range); + &openDisFile(); + printf Scr1_fp "#%s\n#%s\n", $head_str, $retStr; + &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', $fname)); + printf Dis_fp "%s/%s\n", $fname, $discrepancy; + } else { + } + return ($retStr); +} + + +sub checkUnknown() +{ + my ($total, $i, $R); + + sub checkUnknRate() + { + my ($name, $N) = @_; + my ($val, $rate, $fmt); + + $val = $Erp->{$name}[$Comparison[$N][2]]; + $val = sprintf($R->{FMT}, $val); + $rate = sprintf($R->{FMT},($val / $total) * 100); + + if (($val > $R->{'P_RANGE'}) && ($rate > $R->{'P_RATE'})) { + &set_retVal($ERROR_HIGH_UNKNOWN); + &openFsingleScr(); + $fmt = "#%-8s %10s %10s %s\n"; + printf Scr1_fp $fmt, $Comparison[$N][0], '%', '<Total>', $name; + printf Scr1_fp $fmt, ' ', $rate, $total, $val; + &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', '<Total>')); + &commandToScr1_fp(sprintf("%s %s 1", 'csingle', '<Total>')); + &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', $name)); + &commandToScr1_fp(sprintf("%s %s 1", 'csingle', $name)); + &closeFsingleScr(); + return 1; + } + return 0; + } + + return if ( ! exists $Erp->{'<Total>'} ); + return if ( $ENV{NOJAVA} ); + $R = $RANGE{'Unkn'}; + for $i ( 0..$#Comparison ) { + $total = $Erp->{'<Total>'}[$Comparison[$i][2]]; + next if ( $total == 0 ); + $total = sprintf($R->{FMT}, $total); +# last if &checkUnknRate('<Stack-unwind-failed>', $i); + last if &checkUnknRate('<Unknown>', $i); + last if &checkUnknRate('<no', $i); + } +} + +sub createDiff +{ + my ($key, $str, $i, $head_str); + + &checkUnknown(); + open(DIFF_fp, ">diff.out"); + $head_str = " "; + for $i ( 0..$#Comparison ) { + printf DIFF_fp "Comparison[%d]: %s,%d,%d\n", $i, + $Comparison[$i][0], $Comparison[$i][1], $Comparison[$i][2], $Comparison[$i][3]; + $head_str .= &get_head_str($i); + } + printf DIFF_fp "\nX| Compare the acct file (first column) with the er_print output (second column):\n"; + $head_str .= " Name"; + printf DIFF_fp "%s\n", $head_str; + foreach $key (sort keys %Acct) { + $str = &doComp($key, $head_str); + printf DIFF_fp "%s\n", $str; + } + &checkCallersCallees; + close(DIFF_fp); + &closeDisFile(); + return -s "discrepancy.out" +} + +sub createDiff2AVG +{ + my ($key, $str, $i, $n, $head_str, @avg, $temp, $fname); + + &checkUnknown(); + open(DIFF_fp, ">>diff.out"); + printf DIFF_fp "\n==================\n"; + $head_str = " "; + for $i ( 0..$#Comparison ) { + printf DIFF_fp "Comparison[%d]: %s,%d\n", $i, + $Comparison[$i][0], $Comparison[$i][2]; + $head_str .= &get_head_str($i); + } + printf DIFF_fp "\n#| Compare the avg value (first column) with the er_print output (second column):\n"; + $head_str .= " Name"; + printf DIFF_fp "%s\n", $head_str; + for $i ( 0..$#Comparison ) { + $avg[$i] = 0; + } + $n=0; + foreach $fname (sort keys %Acct) { + $n++; + for $i ( 0..$#Comparison ) { + if ( ! exists $Rules{$fname} ) { + if ( exists $Erp->{$fname} ) { + $temp = $Erp->{$fname}[$Comparison[$i][2]]; + } + } else { + foreach my $key1 ( @{$Rules{$fname}} ) { + my $sign = 1; + $key = $key1; + if (substr($key1, 0, 1) eq '-') { + $key = substr($key1, 1); + $sign = -1; + } + if ( exists $Erp->{$key} ) { + $temp += $sign * $Erp->{$key}[$Comparison[$i][2]]; + } + } + } + $avg[$i] += $temp; + } + } + for $i ( 0..$#Comparison ) { + $avg[$i] /= $n; + } + + foreach $key (sort keys %Acct) { + $str = &doComp2AVG($key, $head_str, @avg); + printf DIFF_fp "%s\n", $str; + } + close(DIFF_fp); + &closeDisFile(); +} + +sub sumOutlinedCode +{ # Add a time of the outlined code. + my ($name, $eName); + foreach $name (keys %Acct) { + foreach $eName (keys %$Erp) { + next if ("$eName" !~ m/^($name)\s--/); + if (defined $Rules{$name}) { + push @{$Rules{$name}}, $eName; + } else { + $Rules{$name} = [$eName]; + } + } + } +} + +sub checkCallersCallees +{ + my (@arr, $name, $colName, $line, $nline, %Calls); + + open(FP, "<caller_callee.out") or return; + while ($line = <FP>) { + last if ( $line =~ m/\s+sec.\s+/ ); + } + $nline = 0; + while ($line = <FP>) { + chomp($line); + $line =~ s/^\s*//; # Delete leading spaces + next if ( $line =~ m/^$/ ); + @arr = split (/\s+/, $line, $colName); + $name = pop(@arr); + # New Callers-Callees format does not have * in the Stack Fragment section + # - translate old format to new format for compatibility + if ($name eq "*MAIN") { $name = "MAIN"; }; + last if ($name eq "MAIN"); + $nline += 1; + } + if ($nline == 0) { + printf "checkCallersCallees: No Callers of MAIN\n"; + &set_retVal($ERROR_CALLER_VERIF); + close(FP); + return; + } + while ($line = <FP>) { + chomp($line); + $line =~ s/^\s*//; # Delete leading spaces + next if ( $line =~ m/^$/ ); + @arr = split (/\s+/, $line, $colName); + $name = pop(@arr); + $Calls{$name} = 1; + if ( $line =~ /Parallel/ ) { #f90synprog M_EXPERT or M_MACHINE + @arr = split (/\s\s+/, $line, $colName); + $name = pop(@arr); + @arr = split (/\s/, $name); + $Calls{$arr[0]} = 1; + } + } + close(FP); + + foreach $name (sort keys %Acct) { + next if ( $name eq '<Total>' ) ; + next if ( $name eq '<Unknown>' ) ; + next if (defined $Calls{$name}) ; + printf "checkCallersCallees: '$name' is not inside callees\n"; + &set_retVal($ERROR_CALLER_VERIF); + } +} + + +return 1; +END{} + diff --git a/gprofng/testsuite/lib/display-lib.exp b/gprofng/testsuite/lib/display-lib.exp new file mode 100644 index 0000000..38ecb8d --- /dev/null +++ b/gprofng/testsuite/lib/display-lib.exp @@ -0,0 +1,105 @@ +# Support routines for display-testing machinery in gprofng testsuite. +# Copyright (C) 1994-2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +# Run the COMMAND on the host and return a list of the form +# { exit-status OUTPUT }. +proc run_native_host_cmd { command } { + global link_output + global ld + + verbose -log "$command" + set run_output "" + try { + set run_output [exec "sh" "-c" "$command" "2>@1"] + set status 0 + } trap CHILDSTATUS {results options} { + set status [lindex [dict get $options -errorcode] 2] + set run_output $results + } + regsub "\n$" $run_output "" run_output + if { [lindex $status 0] != 0 && [string match "" $run_output] } then { + append run_output "child process exited abnormally" + } + + if [string match "" $run_output] then { + return "" + } + + return [list [lindex $status 0] $run_output] +} + +# Run a display test in DIR. +# Unanswered questions: do we want to cycle through compilation flags, +# display options, collect flags, app options? Do we want these to be +# set on a per-app basis? (If so, they should probably be driven by a +# file in the test dir.) +proc run_display_test { dir cflags gprofflags } { + global srcdir MAKE CC CFLAGS LDFLAGS LIBS BUILDDIR + set stripped [string map {" " ""} $dir] + set testdir [string map {" " ""} "$dir.$cflags,$gprofflags"] + set sdir "$srcdir/gprofng.display/$dir" + set tdir "tmpdir/$testdir" + send_log "create dir: $tdir\n" + set output [run_native_host_cmd "mkdir -p $tdir"] + set gprofng [exec find $BUILDDIR/tmpdir -type f -name gprofng -perm -u+x | head -1] + + set fd [open "$tdir/rules.txt" "w"] + switch -regexp -- $testdir { + {-p,on.*-h,on} { + set DISPLAY_FLAGS "-metrics i.totalcpu:i.cycles -func" + puts $fd "Cpu, 2, 0\n" + puts $fd "Cycles, 2, 1\n" + } + {-h,on} { + set DISPLAY_FLAGS "-metrics i.cycles -func" + puts $fd "Cycles, 2, 0\n" + } + default { + set DISPLAY_FLAGS "-metrics i.totalcpu -func" + puts $fd "Cpu, 2, 0\n" + } + } + close $fd + + set make_args "-f $sdir/Makefile srcdir=\"$sdir\" builddir=\"$BUILDDIR\" \ + VPATH=\"$dir\" CC=\"$CC\" CFLAGS=\"$cflags\" LDFLAGS=\"$LDFLAGS\" \ + DISPLAY_FLAGS=\"$DISPLAY_FLAGS\" \ + COLLECT_FLAGS=\"$gprofflags\" GPROFNG=\"$gprofng\" MAKE=\"$MAKE\"" + set output [run_native_host_cmd "cd $tdir && $MAKE $make_args all"] +# send_log "run_native_host_cmd output:\n$output\n" + if { [lindex $output 0] != 0 } then { + set out [lindex $output 1] + if {[file exists "$tdir/diff.out"]} then { + send_log "comparison of results in $dir failed:\n$out\n" + set pltf [exec uname -i] + if { $pltf == "aarch64" } { + xfail $dir + return 0 + } + perror "comparison of results in $dir failed" + } else { + send_log "compilation of test program in $dir failed:\n$out\n" + perror "compilation of test program in $dir failed" + } + fail $dir + return 0 + } + pass $dir +} |