xref: /spdk/scripts/core-collector.sh (revision 6a6e9354060f8c91c76ba46642c71c1a89c2c549)
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