Skip to content

Commit

Permalink
Merge pull request #46 from scality/improvement/archive-cores
Browse files Browse the repository at this point in the history
Adds archive core action.
  • Loading branch information
liaud authored Feb 15, 2023
2 parents f631ae3 + e22d61f commit 08d39cd
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 0 deletions.
31 changes: 31 additions & 0 deletions archive-cores/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: "archive cores"
description: 'Archive all cores corresponding to a given set of core_patterns'

inputs:
dest:
description: |
Destination directory to archive cores to. A subdirectory
'cores' will be created under that destination directory
required: true

core_patterns:
description: |
List of paths where the cores might be searched. Glob paths
are supported to filter core files from the others.
required: true

ignore_filters:
description: |
Filters to ignore a found core (space separated, globs supported) applied
against the full backtrace.
This can for example be the function name that will always be present
in the relevant call stack.
default: ""

runs:
using: "composite"
steps:
- name: Archive found cores
shell: bash
run: ${GITHUB_ACTION_PATH}/archive-cores.sh "${{ inputs.dest }}" "${{ inputs.ignore_filters }}" ${{ inputs.core_patterns }}
134 changes: 134 additions & 0 deletions archive-cores/archive-cores.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/bin/bash
set -euo pipefail

# Archive all cores corresponding to a given set of core_patterns to
# $dest_directory/cores.
#
# When possible, the binary that generated the core is also archived, and
# backtraces are generated and archived.
#
# Ignore filters are applied against each found backtrace to determine if the
# core should be archived or not.
#
# Example:
# ./archive_cores.sh $ARTIFACT_DIR "daemontest" ./core.* /tmp/core.*

if [ "$#" -lt 2 ]; then
echo "usage: $0 <dest_directory> <ignore_filters> <core_patterns...>"
exit 1
fi

DEST="$1"
IGNORE_FILTERS="$2"
shift
shift
CORE_PATTERNS="$@"

OUTPUT_DIR="${DEST}/cores"

# Piping into xargs is sometime necessary when globs are single quoted
# by the caller / environment.
CORES="$(echo $CORE_PATTERNS | xargs ls -dUN || /bin/true 2> /dev/null)"

is_expected_crash()
{
local bt_full="$1"
local bin="$2"

for match in $FILTER_PATTERNS; do
if grep -q "$match" "$bt_full"; then
echo "Known and expected crash of $bin: $match, skipping"
return 0
fi
done

# Ignores externally sent SIGSEGV signals. This might be used to test behavior
# of the process when encountering this exception.
if grep '^#3 <signal handler called>' -A2 "$bt_full" | grep -q '^#4 .*\(epoll_wait\|libpthread\)'; then
echo "Known and expected crash of $bin: daemon externally killed with SIGSEGV, skipping"
return 0
fi

return 1
}

# Retrieve the path of the binary from a core
get_bin_path()
{
local core="$1"
local bin=

# First try: get the core name with file
bin="$(file "$core" | grep 'execfn: ' | sed "s/.*execfn: '\([^']\+\)'.*/\1/")"
test -n "$bin" && bin="$(command -v "$bin")"

# This may fail if there are too many section headers in the core, newer
# versions of `file` can deal with this
if [ ! -e "$bin" ]; then
bin="$(file -Pelf_phnum=20000 "$core" 2>/dev/null \
| grep 'execfn: ' \
| sed "s/.*execfn: '\([^']\+\)'.*/\1/")"
test -n "$bin" && bin="$(command -v "$bin")"
fi

# Last try with gdb
if [ ! -e "$bin" ]; then
bin="$(gdb -n --batch "$core" 2>/dev/null \
| grep "^Core was generated by" \
| sed "s/^Core was generated by \`\([^ \`']\+\).*/\1/")"
test -n "$bin" && bin="$(command -v "$bin")"
fi

if [ -e "$bin" ]; then
echo "$bin"
return 0
else
return 1
fi
}

if [ -z "$CORES" ]; then
echo "no core to collect"
exit 0
fi

mkdir -p "${OUTPUT_DIR}"
for core in $CORES; do
if ! file "$core" | grep -q 'core file'; then
echo "$core is not a core file"
file "$core"
continue
fi

core_name="$(basename "$core")"
bin="$(get_bin_path "$core")"

echo "archive core '$core' for binary '$bin' to $OUTPUT_DIR"

if [ ! -e "$bin" ]; then
echo "failed to parse binary path or does not exist, got: '$bin'"
file "$core"
else
# Generate backtraces
core_name="$(basename "$bin")-$(basename "$core")"
prefix="${OUTPUT_DIR}/$core_name-backtrace"
bt_full="$prefix-full.txt"
bt_all="$prefix-all.txt"

gdb -batch -ex 'bt full' "$bin" "$core" > "$bt_full"

# Known and expected crashes
if is_expected_crash "$bt_full" "$bin"; then
rm "$bt_full"
continue
fi

gdb -batch -ex 'thread apply all bt' "$bin" "$core" > "$bt_all"

# Archive binary
cp "$bin" "$OUTPUT_DIR"
fi

# Archive core
tar -czSf "${OUTPUT_DIR}/$core_name.tar.gz" "$core"
done

0 comments on commit 08d39cd

Please sign in to comment.