#!/bin/sh # # %CopyrightBegin% # # Copyright Ericsson AB 2003-2020. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # %CopyrightEnd% # # # This is a script to start Erlang/OTP for debugging. PATH is set to # include this script so if slave nodes are started they will use this # script as well. # # usage: cerl [ OPTIONS ] [ ARGS ] # # The OPTIONS are # # -rootdir $MYROOTDIR # Run an installed emulator built from this source # -debug Run debug compiled emulator # -gdb Run the debug compiled emulator in emacs and gdb. # You have to start beam in gdb using "run". # -rgdb Run the debug compiled emulator in gdb. # You have to start beam in gdb using "run". # -lldb Run the debug compiled emulator in lldb. # You have to start beam in lldb using "run". # -dump Dump the bt of all threads in a core. # -break F Run the debug compiled emulator in emacs and gdb and set break. # The session is started, i.e. "run" is already don for you. # -xxgdb FIXME currently disabled # -gcov Run emulator compiled for gcov # -valgrind Run emulator compiled for valgrind # -asan Run emulator compiled for address-sanitizer # -lcnt Run emulator compiled for lock counting # -icount Run emulator compiled for instruction counting # -rr Run emulator under "rr record" # Can be combined with compile targets (like -debug) except valgrind. # -nox Unset the DISPLAY variable to disable us of X Windows # # FIXME For GDB you can also set the break point using "-break FUNCTION". # FIXME For GDB you can also point out your own .gdbini...... # These are marked for export export ROOTDIR export PROGNAME export EMU export BINDIR export PATH cargs= xargs= cxargs_add() { while [ $# -gt 0 ]; do cargs="$cargs $1" xargs="$xargs $1" shift done } core= GDB= GDBBP= GDBARGS= TYPE= FLAVOR= run_valgrind=no run_asan=no run_rr=no skip_erlexec=no # Default rootdir ROOTDIR=%SRC_ROOTDIR% BINDIR="$ROOTDIR/bin/`$ROOTDIR/erts/autoconf/config.guess`" TARGET=%TARGET% #BINDIR="$ROOTDIR/bin/%TARGET%" PROGNAME=$ROOTDIR/bin/cerl EMU=beam while [ $# -gt 0 ]; do case "$1" in +*) # A system parameter! cxargs_add $1 shift # If next argument does not begin with a hyphen or a plus, # it is used as the value of the system parameter. if [ $# -gt 0 ]; then case $1 in -*|+*) ;; *) cxargs_add $1 shift;; esac fi;; "-instr") cxargs_add $1 shift ;; "-target") shift BINDIR="$ROOTDIR/bin/$1" shift ;; "-rootdir") shift cargs="$cargs -rootdir $1" ROOTDIR="$1" BINDIR=$ROOTDIR/erts-%VSN%/bin shift ;; "-display") shift DISPLAY="$1" export DISPLAY shift ;; "-nox") shift unset DISPLAY ;; "-lcnt") shift cargs="$cargs -lcnt" TYPE=.lcnt ;; "-gprof") shift cargs="$cargs -gprof" TYPE=.gprof ;; "-debug") shift cargs="$cargs -debug" TYPE=.debug ;; "-frmptr") shift cargs="$cargs -frmptr" TYPE=.frmptr ;; "-icount") shift cargs="$cargs -icount" TYPE=.icount ;; "-dump") shift GDB=dump core="$1" shift ;; "-gdb") shift GDB=egdb ;; "-rgdb") shift GDB=gdb ;; "-lldb") shift GDB=lldb ;; "-break") shift GDB=gdb GDBBP="$GDBBP (insert-string \"break $1\") (comint-send-input)" shift ;; "-core") shift GDB=egdb core="$1" shift ;; "-rcore") shift GDB=gdb core="$1" shift ;; # "-xxgdb") # shift # GDB=xxgdb # ;; "-gcov") shift cargs="$cargs -gcov" TYPE=.gcov ;; "-valgrind") shift cargs="$cargs -valgrind" TYPE=.valgrind run_valgrind=yes skip_erlexec=yes ;; "-asan") shift cargs="$cargs -asan" run_asan=yes TYPE=.asan ;; "-emu_type") shift cargs="$cargs -emu_type $1" TYPE=.$1 shift ;; "-emu_flavor") shift cargs="$cargs -emu_flavor $1" FLAVOR=$1 shift ;; "-rr") shift cargs="$cargs -rr" run_rr=yes case "$1" in "replay"|"ps") ;; *) skip_erlexec=yes ;; esac ;; *) break ;; esac done if [ ! -f $BINDIR/erlexec -a -f $ROOTDIR/bin/$TARGET/erlexec ]; then # We are in a strange target (I'm looking at you openbsd) where # TARGET != config.guess BINDIR=$ROOTDIR/bin/$TARGET fi PATH=$BINDIR:$ROOTDIR/bin:$PATH EXEC=$BINDIR/erlexec PROGNAME="$PROGNAME$cargs" EMU="$EMU$TYPE" if [ "x$FLAVOR" = "x" ]; then EMU_NAME=`$EXEC -emu_name_exit` else EMU_NAME=`$EXEC -emu_name_exit -emu_flavor $FLAVOR` xargs="$xargs -emu_flavor $FLAVOR" fi if [ $skip_erlexec = yes ]; then emu_xargs=`echo $xargs | sed "s|+|-|g"` beam_args=`$EXEC -emu_args_exit ${1+"$@"}` # Prepare for some argument passing voodoo: # $beam_args is a list of command line arguments separated by newlines. # Make "$@" represent those arguments verbatim (including spaces and quotes). SAVE_IFS="$IFS" IFS=' ' set -- $beam_args IFS="$SAVE_IFS" fi if [ $run_asan = yes ]; then # Leak sanitizer options if [ "x${LSAN_OPTIONS#*suppressions=}" = "x$LSAN_OPTIONS" ]; then export LSAN_OPTIONS if [ "x$ERL_TOP" != "x" ]; then LSAN_OPTIONS="$LSAN_OPTIONS:suppressions=$ERL_TOP/erts/emulator/asan/suppress" else echo "No leak-sanitizer suppression file found in \$LSAN_OPTIONS" echo "and \$ERL_TOP not set." fi fi # Address sanitizer options export ASAN_OPTIONS if [ "x$ASAN_LOG_DIR" != "x" ]; then if [ "x${ASAN_OPTIONS#*log_path=}" = "x$ASAN_OPTIONS" ]; then ASAN_OPTIONS="$ASAN_OPTIONS:log_path=$ASAN_LOG_DIR/$EMU_NAME-$ASAN_LOGFILE_PREFIX-0" fi fi if [ "x${ASAN_OPTIONS#*halt_on_error=}" = "x$ASAN_OPTIONS" ]; then ASAN_OPTIONS="$ASAN_OPTIONS:halt_on_error=false" fi fi if [ "x$GDB" = "x" ]; then if [ $run_valgrind = yes ]; then valversion=`valgrind --version` valmajor=`echo $valversion | sed 's,[a-z]*\-\([0-9]*\).*,\1,'` valminor=`echo $valversion | sed 's,[a-z]*\-[0-9]*.\([0-9]*\).*,\1,'` valint=`echo "$valmajor * 1000 + $valminor" | bc` if [ "x$VALGRIND_LOG_XML" = "x" ]; then valgrind_xml= log_file_prefix="--log-file=" else export VALGRIND_LOG_XML valgrind_xml="--xml=yes" if [ $valint -gt 3004 ]; then log_file_prefix="--xml-file=" else log_file_prefix="--log-file=" fi fi if [ "x$VALGRIND_LOG_DIR" = "x" ]; then valgrind_log= else if [ $valint -gt 3004 ]; then valgrind_log_file="$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU_NAME.log.$$" else valgrind_log_file="$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU_NAME.log" fi valgrind_log="$log_file_prefix$valgrind_log_file" fi # Add default flags vgflags=$VALGRIND_MISC_FLAGS if [ "x${vgflags#*--show-possibly-lost}" = "x$vgflags" ]; then vgflags="$vgflags --show-possibly-lost=no" fi if [ "x${vgflags#*--child-silent-after-fork}" = "x$vgflags" ]; then vgflags="$vgflags --child-silent-after-fork=yes" fi if [ "x${vgflags#*--suppressions}" = "x$vgflags" ]; then if [ "x$ERL_TOP" != "x" ]; then vgflags="$vgflags --suppressions=$ERL_TOP/erts/emulator/valgrind/suppress.standard" else echo "No valgrind suppression file found in \$VALGRIND_MISC_FLAGS and \$ERL_TOP not set." fi fi if which taskset > /dev/null && test -e /proc/cpuinfo; then # We only let valgrind utilize one core with "taskset 1" as it can be very slow # on multiple cores (especially with async threads). Valgrind only run one pthread # at a time anyway so there is no point letting it utilize more than one core. # Use $sched_arg to force all schedulers online to emulate multicore. ncpu=`cat /proc/cpuinfo | grep -w processor | wc -l` # Choose a random core in order to not collide with any other valgrind # run on the same machine. cpu=`shuf -i 1-$ncpu -n 1` cpu=`expr $cpu - 1` taskset1="taskset --cpu-list $cpu" sched_arg="-S $ncpu:$ncpu -SDcpu $ncpu:$ncpu" else taskset1= sched_arg= fi if [ $EMU_NAME = "beam.valgrind.asm" ]; then # Always enable `perf` support as we use the same symbol map emu_xargs="$emu_xargs -JPperf true " set -m $taskset1 valgrind $valgrind_xml $valgrind_log $vgflags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" & VG_PID=$! fg VG_EXIT=$? set +m if [ -f /tmp/perf-$VG_PID.map ]; then $ERL_TOP/scripts/valgrind_beamasm_update.escript $valgrind_log_file /tmp/perf-$VG_PID.map fi exit $VG_EXIT else exec $taskset1 valgrind $valgrind_xml $valgrind_log $vgflags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" fi elif [ $run_rr = yes ]; then if [ $1 = replay ]; then shift cmdfile="/tmp/.cerlgdb.$$" echo "set \$etp_beam_executable = \"$BINDIR/$EMU_NAME\"" > $cmdfile if [ "$1" = "-p" ]; then echo 'set $etp_rr_run_until_beam = 1' >> $cmdfile fi cat $ROOTDIR/erts/etc/unix/etp-commands >> $cmdfile exec rr replay -x $cmdfile $* elif [ $1 = ps ]; then shift rr ps $* | head -1 ChildSetup=`rr ps $* | grep 'erl_child_setup' | awk '{ print $2 }'` for CS in $ChildSetup; do rr ps $* | grep -E "^$CS" done exit 0 else # RR version >= 5.4 has replaced the `--ignore-nested` # flag with `--nested=ignore`, so try to auto detect this. if rr help record | grep -q -- '--nested=ignore'; then RR_IGNORE_NESTED="--nested=ignore" else RR_IGNORE_NESTED="--ignore-nested" fi exec rr record $RR_IGNORE_NESTED $BINDIR/$EMU_NAME $emu_xargs "$@" fi else exec $EXEC $xargs ${1+"$@"} fi elif [ "x$GDB" = "xgdb" ]; then case "x$core" in x) # Get emu args to use from erlexec... beam_args=`$EXEC -emu_args_exit ${1+"$@"}` gdbcmd="--args $EMU_NAME $beam_args" ;; x/*) gdbcmd="$EMU_NAME ${core}" GDBBP= ;; *) dir=`pwd` gdbcmd="$EMU_NAME ${dir}/${core}" GDBBP= ;; esac cmdfile="/tmp/.cerlgdb.$$" echo "source $ROOTDIR/erts/etc/unix/etp-commands" > $cmdfile # Fire up gdb in emacs... exec gdb $GDBBP -x $cmdfile $gdbcmd elif [ "x$GDB" = "xlldb" ]; then case "x$core" in x) beam_args=`$EXEC -emu_args_exit ${1+"$@"}` lldbcmd="-- $beam_args" ;; *) lldbcmd="--core ${core}" ;; esac cmdfile="/tmp/.cerllldb.$$" echo "env TERM=dumb" > $cmdfile echo "command script import $ROOTDIR/erts/etc/unix/etp.py" >> $cmdfile exec lldb -s $cmdfile $EMU_NAME $lldbcmd elif [ "x$GDB" = "xegdb" ]; then if [ "x$EMACS" = "x" ]; then EMACS=emacs fi case "x$core" in x) # Get emu args to use from erlexec... beam_args=`$EXEC -emu_args_exit ${1+"$@"} | tr '\n' ' '` gdbcmd="(insert-string \"set args $beam_args\") \ (comint-send-input)" ;; x/*) gdbcmd="(insert-string \"core ${core}\") (comint-send-input)" GDBBP= ;; *) dir=`pwd` gdbcmd="(insert-string \"core ${dir}/${core}\") \ (comint-send-input)" GDBBP= ;; esac if [ "$EMACS_ANNOTATE_LEVEL" != "" ]; then GDBARGS="--annotate=$EMACS_ANNOTATE_LEVEL" else # Set annotation level for gdb in emacs 22 and higher. Seems to # be working with level 1 for emacs 22 and level 3 for emacs 23... emacs_major=`$EMACS --version | head -1 | sed 's,^[^0-9]*\([0-9]*\).*,\1,g'` if [ '!' -z "$emacs_major" -a $emacs_major -gt 23 ]; then GDBARGS="-i=mi " elif [ '!' -z "$emacs_major" -a $emacs_major -gt 22 ]; then GDBARGS="--annotate=3 " elif [ '!' -z "$emacs_major" -a $emacs_major -gt 21 ]; then GDBARGS="--annotate=1 " fi fi gdbcmd="$gdbcmd $GDBBP \ (insert-string \"source $ROOTDIR/erts/etc/unix/etp-commands\") \ (comint-send-input)" # Fire up gdb in emacs... exec $EMACS --eval "(progn (gdb \"gdb $GDBARGS$EMU_NAME\") $gdbcmd)" elif [ "x$GDB" = "xdump" ]; then cmdfile="/tmp/.cerlgdb.$$" case "x$core" in x/*) ;; *) dir=`pwd` core="${dir}/${core}" ;; esac case `uname` in Darwin) echo " thread backtrace all quit " > $cmdfile exec lldb -s $cmdfile -c ${core} $EMU_NAME ;; *) echo "set width 0 set height 0 set verbose off source $ROOTDIR/erts/etc/unix/etp-commands thread apply all bt " > $cmdfile exec gdb --batch --command=$cmdfile $EMU_NAME $core ;; esac fi