-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from scality/improvement/archive-cores
Adds archive core action.
- Loading branch information
Showing
2 changed files
with
165 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |