#!/bin/sh -eu # # Copyright © 2024 Samuel Lidén Borell # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # if [ $# = 0 ]; then cat <&2 exit 1 fi times=${line#*times=\"} times=${times%%\"*};; "^error"*) msg=${line#*msg=\"} msg=${msg%\"} echo "Error from GDB: $msg" >&2 exit 1;; "~\"#"*) if [ $show_bt = 1 ]; then btline=${line#~\"} btline=${btline%\\n\"} filter_bt "$btline" fi esac done } wait_for_stop() { while [ $status = running ]; do read_response done } cmd() { echo "$@" >&3 read_response } status() { #printf '\033[1K%s' "$1" 2>&1 printf '%s\n' "$1" 2>&1 } handle_commands() { status=none show_bt=0 found_tracked_func=0 status "First run (analysis)..." read_response cmd -exec-run wait_for_stop if [ $status = exited ]; then echo "No crash" exit 0 fi show_bt=1 # TODO should probably use `-stack-list-frames` for parsing info (but `bt` for output) cmd bt show_bt=0 if [ $found_tracked_func = 0 ]; then echo "Tracked function '$TRACKED_FUNC' not found" >&2 exit 2 fi cmd -break-insert -i "$very_large_number" --function "$TRACKED_FUNC" status "Second run (to breakpoint)..." cmd -exec-run wait_for_stop #status "" cmd -break-info cmd -gdb-exit printf "%s %s\n" $times $TRACKED_FUNC > "$resultfile" } cleanup() { [ ! -e "$resultfile" ] || rm "$resultfile" [ ! -e "$fifo" ] || rm "$fifo" [ ! -d "$fifodir" ] || rmdir "$fifodir" } fifodir=$(mktemp -d beforecrash_XXXXXX --tmpdir) fifo="$fifodir/gdb_input_fifo" resultfile="$fifodir/result" trap 'cleanup' EXIT mkfifo "$fifo" || { echo "Failed to create fifo '$fifo'" >&2 exit 1 } # Determine number of times that the TRACKED_FUNC function # was executed before the crash. gdb -q --interpreter=mi2 --args "$@" <"$fifo" | handle_commands 3>"$fifo" if [ ! -e "$resultfile" ]; then # E.g. a non-crashing program. Abort early exit fi read -r bp_times TRACKED_FUNC <"$resultfile" #echo "times: $bp_times" bp_times=$(($bp_times - $STEPS_BEFORE)) # Run until the last function call before the crash gdb -q -ex "break $TRACKED_FUNC" -ex "ignore 1 $bp_times" -ex "run" --args "$@"