1#!/usr/bin/env bash 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright (C) 2020 Intel Corporation 4# All rights reserved. 5# 6# We don't want to tell kernel to include %e or %E since these 7# can include whitespaces or other funny characters, and working 8# with those on the cmdline would be a nightmare. Use procfs for 9# the remaining pieces we want to gather: 10# |$rootdir/scripts/core-collector.sh %P %s %t $output_dir 11 12rootdir=$(readlink -f "$(dirname "$0")/../") 13 14maps_to_json() { 15 local _maps=("${maps[@]}") 16 local mem_regions=() mem 17 18 mem_regions=("/proc/$core_pid/map_files/"*) 19 20 for mem in "${!mem_regions[@]}"; do 21 _maps[mem]=\"${_maps[mem]}@${mem_regions[mem]##*/}\" 22 done 23 24 local IFS="," 25 echo "${_maps[*]}" 26} 27 28core_meta() { 29 jq . <<- CORE 30 { 31 "$exe_comm": { 32 "ts": "$core_time", 33 "size": "$core_size bytes", 34 "PID": $core_pid, 35 "signal": "$core_sig ($core_sig_name)", 36 "path": "$exe_path", 37 "cwd": "$cwd_path", 38 "statm": "$statm", 39 "filter": "$(coredump_filter)", 40 "mapped": [ $(maps_to_json) ] 41 } 42 } 43 CORE 44} 45 46bt() { hash gdb && gdb -batch -ex "thread apply all bt full" "$1" "$2" 2>&1; } 47 48stderr() { 49 exec 2> "$core.stderr.txt" 50 set -x 51} 52 53coredump_filter() { 54 local bitmap bit 55 local _filter filter 56 57 bitmap[0]=anon-priv-mappings 58 bitmap[1]=anon-shared-mappings 59 bitmap[2]=file-priv-mappings 60 bitmap[3]=file-shared-mappings 61 bitmap[4]=elf-headers 62 bitmap[5]=priv-hp 63 bitmap[6]=shared-hp 64 bitmap[7]=priv-DAX 65 bitmap[8]=shared-DAX 66 67 _filter=0x$(< "/proc/$core_pid/coredump_filter") 68 69 for bit in "${!bitmap[@]}"; do 70 ((_filter & 1 << bit)) || continue 71 filter=${filter:+$filter,}${bitmap[bit]} 72 done 73 74 echo "$filter" 75} 76 77filter_process() { 78 # Did the process sit in our repo? 79 [[ $cwd_path == "$rootdir"* ]] && return 0 80 81 # Did we load our fio plugins? 82 [[ ${maps[*]} == *"$rootdir/build/fio/spdk_nvme"* ]] && return 0 83 [[ ${maps[*]} == *"$rootdir/build/fio/spdk_bdev"* ]] && return 0 84 85 # Do we depend on it? 86 local crit_binaries=() bin 87 88 crit_binaries+=("nvme") 89 crit_binaries+=("qemu-system*") 90 # Add more if needed 91 92 for bin in "${crit_binaries[@]}"; do 93 # The below SC is intentional 94 # shellcheck disable=SC2053 95 [[ ${exe_path##*/} == $bin ]] && return 0 96 done 97 98 return 1 99} 100 101args+=(core_pid) 102args+=(core_sig) 103args+=(core_ts) 104 105read -r "${args[@]}" <<< "$*" 106 107exe_path=$(readlink -f "/proc/$core_pid/exe") 108cwd_path=$(readlink -f "/proc/$core_pid/cwd") 109exe_comm=$(< "/proc/$core_pid/comm") 110statm=$(< "/proc/$core_pid/statm") 111core_time=$(date -d@"$core_ts") 112core_sig_name=$(kill -l "$core_sig") 113mapfile -t maps < <(readlink -f "/proc/$core_pid/map_files/"*) 114 115# Filter out processes that we don't care about 116filter_process || exit 0 117 118core=$(< "$rootdir/.coredump_path")/${exe_path##*/}_$core_pid.core 119stderr 120 121# RLIMIT_CORE is not enforced when core is piped to us. To make 122# sure we won't attempt to overload underlying storage, copy 123# only the reasonable amount of bytes (systemd defaults to 2G 124# so let's follow that). 125rlimit=$((1024 * 1024 * 1024 * 2)) 126 127# Clear path for lz 128rm -f "$core"{,.{bin,bt,gz,json}} 129 130# Slurp the core 131head -c "$rlimit" <&0 > "$core" 132core_size=$(wc -c < "$core") 133 134# Compress it 135gzip -c "$core" > "$core.gz" 136 137# Save the binary 138cp "$exe_path" "$core.bin" 139 140# Save the backtrace 141bt "$exe_path" "$core" > "$core.bt.txt" 142 143# Save the metadata of the core 144core_meta > "$core.json" 145 146# Nuke the original core 147rm "$core" 148