diff --git a/Dockerfile.irods_runner.centos7 b/Dockerfile.irods_runner.centos7 index 5fbaec3..518e0fb 100644 --- a/Dockerfile.irods_runner.centos7 +++ b/Dockerfile.irods_runner.centos7 @@ -54,6 +54,7 @@ RUN \ rm -rf /var/cache/yum /tmp/* ADD ICAT.sql / +ADD koutheir-pretty-printers.sh / ADD keep_alive.sh /keep_alive.sh RUN chmod +x /keep_alive.sh ENTRYPOINT ["/keep_alive.sh"] diff --git a/Dockerfile.irods_runner.ubuntu16 b/Dockerfile.irods_runner.ubuntu16 index d2451b3..2a61239 100644 --- a/Dockerfile.irods_runner.ubuntu16 +++ b/Dockerfile.irods_runner.ubuntu16 @@ -48,6 +48,7 @@ RUN \ rm -rf /var/lib/apt/lists/* /tmp/* ADD ICAT.sql / +ADD koutheir-pretty-printers.sh / ADD keep_alive.sh /keep_alive.sh RUN chmod +x /keep_alive.sh ENTRYPOINT ["/keep_alive.sh"] diff --git a/Dockerfile.irods_runner.ubuntu18 b/Dockerfile.irods_runner.ubuntu18 index c2c03b2..1739d10 100644 --- a/Dockerfile.irods_runner.ubuntu18 +++ b/Dockerfile.irods_runner.ubuntu18 @@ -59,6 +59,7 @@ RUN \ rm -rf /var/lib/apt/lists/* /tmp/* ADD ICAT.sql / +ADD koutheir-pretty-printers.sh / ADD keep_alive.sh /keep_alive.sh RUN chmod +x /keep_alive.sh ENTRYPOINT ["/keep_alive.sh"] diff --git a/README.md b/README.md index 5c44324..7e31a27 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ This repository contains tools for the running and troubleshooting of a Docker-c 1. [`valgrind`](#valgrind) 1. [`scan-build`](#scan-build) 1. ( `cppcheck`, ... ?) + 1. [Enabling GDB-style pretty printers](#enabling-gdb-style-pretty-printers) --- ## General Setup @@ -265,3 +266,51 @@ The clang static analyzer can be used when building iRODS. cmake -DCLANG_STATIC_ANALYZER=ON -GNinja .. scan-build ninja ``` +--- + +## Enabling GDB-style pretty printers + +Run the provided script to enable debug libraries and pretty printing for the libcxx versions of STL containers. + +``` +root@399b4ef584a7:~# bash /koutheir-pretty-printers.sh + ... +--> Checking out llvm and building debug libraries for cxx and cxxabi. + ... +--> Writing debug shared objects. + ... +--> Creating /root/.gdbinit for gdb and rr. + ... +``` +Then, to verify, using this file, `demo.cc`: +``` +#include +#include +int main() +{ + std::map mymap { + {"key1","value1"}, {"key2","value2"} + }; + return 0; // In rr or gdb, place breakpoint here and examine `mymap' +} +``` +compile: +``` +root@399b4ef584a7:/# /opt/irods-externals/clang6.0-0/bin/clang++ -Wl,-rpath -Wl,/opt/irods-externals/clang-runtime6.0-0/lib -stdlib=libc++ demo.cc -g +``` +and in GDB or RR, run the program. After breaking at the `return 0;` statement, look at the **map** variable - first using the pretty printers, +and then raw - to observe the difference. +``` +root@399b4ef584a7:/# /opt/debug_tools/bin/gdb a.out +(gdb) b +(gdb) r +(gdb) p mymap +$1 = std::__1::map (count=2) = {[0] "key1" = "value1", [1] "key2" = "value + +(gdb) p /r mymap +$2 = {__tree_ = {__begin_node_ = 0x608010, + __pair1_ = {*>, 0, false>> = {__value_ = { __left_ = 0x608010}}, , ... + ( ... more gobbledygook ...) + }, }}} +``` +Fortunately, the pretty printers support the great majority of data structures commonly used in iRODS code (that is: map, list, vector, string, etc.). diff --git a/koutheir-pretty-printers.sh b/koutheir-pretty-printers.sh new file mode 100755 index 0000000..cf2af64 --- /dev/null +++ b/koutheir-pretty-printers.sh @@ -0,0 +1,169 @@ +#!/bin/bash + +# ***** While CHECK_* macros are still here, we are still testing :) . + +if [ -n "${CHECK_USER-1}" ] ; then # TODO - remove this if clause + [ $(id -u) = 0 ] || { echo "Please run this script as root." >&2; exit 1; } +fi + +# -- Configuration Values. + +CLANG_VERSION_HINT="6.*" +CMAKE_PREFIX="/opt/irods-externals/cmake" +CLANG_PREFIX="/opt/irods-externals/clang" +CLANG_RUNTIME_PREFIX="/opt/irods-externals/clang-runtime" + +declare -A LLVM_TO_KOUTHEIR_VERSION_LOOKUP=( + ["6.0.0"]="5ffbf2487bf8da7f08bc1c8650a4396d2ff15403" +) + +# -- Process Command Line Options. + +eval "set --$(/usr/bin/getopt --longoptions "clang-version-hint:" --options "c:" -- "$@")" +for option in "$@"; do + case $option in + --clang-version-hint|-c) CLANG_VERSION_HINT=$2 ; shift 2 ;; + *) shift; break;; + esac +done + +# +# -- Function package_max_version +# To determine the maximum of several versions of a package in iRODS externals +# +package_max_version() ( # deliberate subshell + [ -z "$1" ] && exit + PACKAGE_PREFIX=$1 + shopt -s nullglob + if [ -n "$2" ]; then + version_string_pattern="$2" + else + version_string_pattern="[0-9][-.0-9]*" + fi + max="" + paths=( ${PACKAGE_PREFIX}${version_string_pattern} ) + [ ${#paths[*]} -gt 0 ] && \ + for y in ${paths[*]}; do + max=$(get_max "${y#${PACKAGE_PREFIX}}" "$max") + done + echo "$max" +) + +get_max() { # -- get max of two (X,Y,Z) tuples -- + local z y=0 gt="" + local IFS=".-" # for splitting version numbers of the form "X[-.]Y..." into (X,Y,...) + read -a z <<<"$2" + for x in ${1}; do [ "${x:-0}" -gt "${z[$((y++))]:-0}" ] && gt=1; done + if [ "$gt" ]; then echo "$1" + else echo "$2"; fi +} + +CLANG_VERSION=$(package_max_version $CLANG_PREFIX "$CLANG_VERSION_HINT") + +if [ -z "$CLANG_VERSION" ] ; then + echo >&2 "ERROR - no Clang compiler found among installed irods-externals*" + exit 2 +fi + +LLVM_VERSION=${CLANG_VERSION//[-.]/.} + +LIBCXX_PRETTY_PRINTERS_COMMIT=${LLVM_TO_KOUTHEIR_VERSION_LOOKUP[$LLVM_VERSION]} + +if [ -z "$LIBCXX_PRETTY_PRINTERS_COMMIT" ]; then + echo >&2 "WARNING - no optimal version of libc++ Pretty-Printers found." + echo >&2 " Using the default branch" +fi + +if [ -x /usr/bin/zypper ] ;then + pkgtool=zypper # SuSE +elif [ -x /usr/bin/yum ]; then + pkgtool=yum # CentOS, RHEL + yum install -y epel-release +else + pkgtool=apt # Debian, Ubuntu + apt update +fi + +# Need: +# - python so that GDB and RR can load pretty printers +# - git to fetch LLVM source +# - make to build debug libraries + +${pkgtool} install -y make git python python3 + +# Build debug versions of stdc++ lib shared objects. + +echo >&2 -e "\n--> Checking out llvm and building debug libraries for cxx and cxxabi.\n" + +CMAKE_VERSION=$(package_max_version $CMAKE_PREFIX) +CLANG_PATH=${CLANG_PREFIX}${CLANG_VERSION} +CLANG_RUNTIME_PATH=${CLANG_RUNTIME_PREFIX}${CLANG_VERSION} + + +if [ ! -d "$CLANG_RUNTIME_PATH" ]; then + echo >&2 "Warning - No clang runtime corresponding to clang '${CLANG_VERSION}'." + echo >&2 " Please install it before running this script." + exit 3 +fi + +CMAKE_VERSION=$(package_max_version $CMAKE_PREFIX) + +# +# Print debug values and quit if requested by CHECK_QUIT != "" - TODO - remove this +# +if [ -n "${CHECK_QUIT}" ] ; then + echo CMAKE_VERSION "($CMAKE_VERSION)" + echo CLANG_PATH "($CLANG_PATH)" + echo CLANG_RUNTIME_PATH "($CLANG_RUNTIME_PATH)" + echo LIBCXX_PRETTY_PRINTERS_COMMIT "($LIBCXX_PRETTY_PRINTERS_COMMIT)" + exit 10 +fi + +if [ -n "${CMAKE_VERSION}" ]; then + cd ~ ; git clone http://github.com/llvm/llvm-project + cd llvm-project && \ + git checkout "llvmorg-${LLVM_VERSION}" && \ + mkdir build && \ + cd build && \ + "${CMAKE_PREFIX}${CMAKE_VERSION}/bin/cmake" \ + -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS="libcxx;libcxxabi" ../llvm && \ + make -j7 cxx cxxabi +else + echo >&2 "Error - Need at least one irods-externals-cmake* package installed" + exit 4 +fi + +# -- Backup existing shared objects and copy debug versions in to replace them. + +echo >&2 -e "\n--> Writing debug shared objects.\n" +for SHLIB in lib/libc++*.so*; do + for DIR in /opt/irods-externals/clang{,-runtime}${CLANG_VERSION}/; do + [ -f "$DIR/$SHLIB" -o -L "$DIR/$SHLIB" ] && mv "$DIR/$SHLIB"{,.orig} + cp -rp "$SHLIB" "$DIR"/lib/. + done +done + +# -- Install & configure pretty-printers + +if [ -z "${LIBCXX_PRETTY_PRINTERS_COMMIT}" ]; then + GIT_PPRINTER_CHECKOUT_CMD=":" +else + GIT_PPRINTER_CHECKOUT_CMD="git checkout '${LIBCXX_PRETTY_PRINTERS_COMMIT}'" +fi + +echo >&2 -e "\n--> Creating $HOME/.gdbinit for gdb and rr.\n" + +cd ~ ; git clone https://github.com/koutheir/libcxx-pretty-printers + +if cd libcxx-pretty-printers +then + if ! eval "$GIT_PPRINTER_CHECKOUT_CMD" ; then + echo >&2 "Error - Could not find pretty printers repository directory." + exit 5 + fi + PP_SRC_DIR=~/libcxx-pretty-printers/src + cp $PP_SRC_DIR/gdbinit ~/.gdbinit && sed -i -e "s@@$PP_SRC_DIR@" ~/.gdbinit +else + echo >&2 "Error - Could not find pretty printers repository directory." + exit 6 +fi