mirror of
https://github.com/flatcar/scripts.git
synced 2025-08-08 21:46:58 +02:00
Currently, the scripts in src/scripts have multiple implementations for handling when common.sh fails to load, some of which are buggy. To simplify the boilerplate, these scripts now just exit if common.sh fails to load. The shell itself will print the following message if common.sh is not found: /usr/lib/crosutils/common.sh: No such file or directory BUG=chromium-os:32442 TEST=Run these scripts with and without common.sh installed. Change-Id: Ie54420b6c649774f9cb039c14c80f4cf6c6ebc07 Reviewed-on: https://gerrit.chromium.org/gerrit/27058 Reviewed-by: David James <davidjames@chromium.org> Tested-by: David James <davidjames@chromium.org> Commit-Ready: David James <davidjames@chromium.org>
297 lines
9.0 KiB
Bash
Executable File
297 lines
9.0 KiB
Bash
Executable File
#!/bin/bash
|
|
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
# Script to generate minidump symbols in the format required by
|
|
# minidump_stackwalk to dump stack information.
|
|
#
|
|
# NOTE: This script must be run from the chromeos build chroot environment.
|
|
#
|
|
|
|
SCRIPT_ROOT=$(dirname $(readlink -f "$0"))
|
|
. "${SCRIPT_ROOT}/common.sh" || exit 1
|
|
|
|
# Script must be run inside the chroot
|
|
restart_in_chroot_if_needed "$@"
|
|
|
|
get_default_board
|
|
|
|
# Flags
|
|
DEFINE_string board "$DEFAULT_BOARD" "The board to build packages for."
|
|
DEFINE_string minidump_symbol_root "" \
|
|
"Symbol root (defaults to /usr/lib/debug/breakpad for board)"
|
|
DEFINE_boolean verbose ${FLAGS_FALSE} "Be verbose."
|
|
|
|
DUMP_SYMS="dump_syms"
|
|
DUMP_SYMS32="dump_syms.32"
|
|
|
|
ERROR_COUNT=0
|
|
|
|
debug() {
|
|
if [ ${FLAGS_verbose} -eq ${FLAGS_TRUE} ]; then
|
|
info "$@"
|
|
fi
|
|
}
|
|
|
|
# Each job sets this on their own; we declare it
|
|
# globally so the exit trap can always see the last
|
|
# setting of it w/in a job worker.
|
|
SYM_FILE=
|
|
JOB_FILE=
|
|
NOTIFIED=
|
|
|
|
# The master process sets these up, which each worker
|
|
# than uses for communicating once they've finished.
|
|
CONTROL_PIPE=
|
|
CONTROL_PIPE_FD=
|
|
|
|
_worker_finished() {
|
|
if [ -z "${NOTIFIED}" ]; then
|
|
debug "Sending notification of $BASHPID ${1-1}"
|
|
echo "$BASHPID ${1-1}" > /dev/fd/${CONTROL_PIPE_FD}
|
|
NOTIFIED=1
|
|
fi
|
|
}
|
|
|
|
_cleanup_worker() {
|
|
rm -f "${SYM_FILE}" "${ERR_FILE}"
|
|
_worker_finished 1
|
|
}
|
|
|
|
_cleanup_master() {
|
|
set +eu
|
|
rm -f "${CONTROL_PIPE}"
|
|
if [ ${#JOBS_ARRAY[@]} != 0 ]; then
|
|
kill -s SIGINT "${!JOBS_ARRAY[@]}" &> /dev/null
|
|
wait
|
|
# Clear the array.
|
|
JOBS_ARRAY=( )
|
|
fi
|
|
}
|
|
|
|
declare -A JOBS_ARRAY
|
|
|
|
finish_job() {
|
|
local finished result
|
|
read -r -u ${CONTROL_PIPE_FD} finished result
|
|
# Bash doesn't allow for zombies, but tell it to clean up its intenral
|
|
# bookkeeping. Note bash can be buggy here- if a new process has slipped
|
|
# into that pid, bash doesn't use its internal accounting first, and
|
|
# can throw an error; doesn't matter, thus this form.
|
|
! wait ${finished} &> /dev/null
|
|
if [ "${result-1}" -ne "0" ]; then
|
|
: $(( ++ERROR_COUNT ))
|
|
fi
|
|
# Bit of a hack, but it works well enough.
|
|
debug "finished ${finished} with result ${result-1}"
|
|
unset JOBS_ARRAY[${finished}]
|
|
}
|
|
|
|
run_job() {
|
|
local debug_file=${1} text_file=${2} newpid
|
|
|
|
if [ ${#JOBS_ARRAY[@]} -ge ${NUM_JOBS} ]; then
|
|
# Reclaim a spot.
|
|
finish_job
|
|
fi
|
|
|
|
dump_file "${debug_file}" "${text_file}" &
|
|
newpid=$!
|
|
debug "Started ${debug_file} ${text_file} at ${newpid}"
|
|
JOBS_ARRAY[$newpid]=1
|
|
}
|
|
|
|
# Given path to a debug file, return its text file
|
|
get_text_for_debug() {
|
|
local debug_file=$1
|
|
local text_dir=$(dirname "${debug_file#$DEBUG_ROOT}")
|
|
local text_path=${SYSROOT}${text_dir}/$(basename "${debug_file}" .debug)
|
|
echo ${text_path}
|
|
}
|
|
|
|
# Given path to a text file, return its debug file
|
|
get_debug_for_text() {
|
|
local text_file=$1
|
|
local text_path=${text_file#${SYSROOT}}
|
|
local debug_path=${DEBUG_ROOT}${text_path}.debug
|
|
echo ${debug_path}
|
|
}
|
|
|
|
# Returns true if the file given is a 32-bit ELF file.
|
|
is_32b_elf() {
|
|
local elf="$1"
|
|
file "${elf}" | grep -q "ELF 32-bit"
|
|
}
|
|
|
|
# Dump given debug and text file. Returns 1 if any errors, even
|
|
# if they can be ignored, but only sets ERROR_COUNT if the error should not
|
|
# be ignored (and we should not proceed to upload).
|
|
dump_file() {
|
|
trap '_cleanup_worker; exit 1' INT TERM
|
|
trap _cleanup_worker EXIT
|
|
local debug_file="$1"
|
|
local text_file="$2"
|
|
local debug_directory="$(dirname "${debug_file}")"
|
|
local dump_syms_prog="${DUMP_SYMS}"
|
|
# 32-bit dump_syms must be used to dump a 32-bit ELF file
|
|
if is_32b_elf "${text_file}"; then
|
|
dump_syms_prog="${DUMP_SYMS32}"
|
|
debug "Using ${dump_syms_prog} for 32-bit file ${text_file}"
|
|
fi
|
|
SYM_FILE=$(mktemp -t "breakpad.sym.XXXXXX")
|
|
# Dump symbols as root in order to read all files.
|
|
if ! sudo "${dump_syms_prog}" "${text_file}" "${debug_directory}" \
|
|
> "${SYM_FILE}" 2> /dev/null; then
|
|
# Try dumping just the main file to get public-only symbols.
|
|
ERR_FILE=$(mktemp -t "breakpad.err.XXXXXX")
|
|
if ! sudo "${dump_syms_prog}" "${text_file}" > "${SYM_FILE}" \
|
|
2> "${ERR_FILE}"; then
|
|
# A lot of files (like kernel files) contain no debug information, do
|
|
# not consider such occurrences as errors.
|
|
if grep -q "file contains no debugging information" "${ERR_FILE}"; then
|
|
warn "No symbols found for ${text_file}"
|
|
_worker_finished 0
|
|
exit 0
|
|
fi
|
|
error "Unable to dump symbols for ${text_file}:"
|
|
error "$(<"${ERR_FILE}")"
|
|
exit 1
|
|
else
|
|
warn "File ${text_file} did not have debug info, using linkage symbols"
|
|
fi
|
|
fi
|
|
local file_id=$(head -1 ${SYM_FILE} | cut -d' ' -f4)
|
|
local module_name=$(head -1 ${SYM_FILE} | cut -d' ' -f5)
|
|
# Show file upload success and symbol info for easier lookup
|
|
debug "Dumped symbols from ${text_file} for ${module_name}|${file_id}."
|
|
# Sanity check: if we've created the same named file in the /usr/lib/debug
|
|
# directory during the src_compile stage of an ebuild, verify our sym file
|
|
# is the same.
|
|
local installed_sym="${DEBUG_ROOT}"/$(basename "${text_file}").sym
|
|
if [ -e "${installed_sym}" ]; then
|
|
if ! cmp --quiet "${installed_sym}" "${SYM_FILE}"; then
|
|
error "${installed_sym} differ from current sym file:"
|
|
error "$(diff "${installed_sym}" "${SYM_FILE}")"
|
|
: $(( ++ERROR_COUNT ))
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
local container_dir="${FLAGS_minidump_symbol_root}/${module_name}/${file_id}"
|
|
sudo mkdir -p "${container_dir}"
|
|
sudo mv "${SYM_FILE}" "${container_dir}/${module_name}.sym"
|
|
_worker_finished 0
|
|
exit 0
|
|
}
|
|
|
|
# Convert the given debug file. No return value.
|
|
process_file() {
|
|
local debug_file="$1"
|
|
local text_file="$(get_text_for_debug ${debug_file})"
|
|
if [ -h "${debug_file}" ]; then
|
|
# Don't follow symbolic links. In particular, we don't want to bother
|
|
# with the *.debug links in the "debug/.build-id/" directory.
|
|
debug "Skipping symbolic link: ${debug_file}"
|
|
return 0
|
|
fi
|
|
if [ "${text_file##*.}" == "ko" ]; then
|
|
# Skip kernel objects. We can't use their symbols and they sometimes
|
|
# have objects with empty text sections which trigger errors in dump_sym.
|
|
debug "Skipping kernel object: ${text_file}"
|
|
return 0
|
|
fi
|
|
if [ ! -f "${text_file}" ]; then
|
|
# Allow files to not exist, for instance if they are in the INSTALL_MASK.
|
|
warn "Binary does not exist: ${text_file}"
|
|
return 0
|
|
elif [ ! -r "${text_file}" ]; then
|
|
# Allow files to not be readable, for instance setuid programs like passwd
|
|
warn "Binary is not user readable: ${text_file}"
|
|
return 0
|
|
fi
|
|
|
|
run_job "${debug_file}" "${text_file}"
|
|
}
|
|
|
|
main() {
|
|
|
|
# Parse command line
|
|
FLAGS_HELP="usage: $0 [flags] [<files...>]"
|
|
FLAGS "$@" || exit 1
|
|
eval set -- "${FLAGS_ARGV}"
|
|
|
|
switch_to_strict_mode
|
|
|
|
[ -n "$FLAGS_board" ] || die_notrace "--board is required."
|
|
|
|
SYSROOT="/build/${FLAGS_board}"
|
|
|
|
if [[ -z "${FLAGS_minidump_symbol_root}" ]]; then
|
|
FLAGS_minidump_symbol_root="${SYSROOT}/usr/lib/debug/breakpad"
|
|
fi
|
|
|
|
info "Writing minidump symbols to ${FLAGS_minidump_symbol_root}"
|
|
|
|
DEBUG_ROOT="${SYSROOT}/usr/lib/debug"
|
|
sudo rm -rf "${FLAGS_minidump_symbol_root}"
|
|
|
|
# Open our control pipe.
|
|
trap '_cleanup_master; exit 1' INT TERM
|
|
trap _cleanup_master EXIT
|
|
CONTROL_PIPE=$(mktemp -t "breakpad.fifo.XXXXXX")
|
|
rm "${CONTROL_PIPE}"
|
|
mkfifo "${CONTROL_PIPE}"
|
|
exec {CONTROL_PIPE_FD}<>${CONTROL_PIPE}
|
|
|
|
# We require our stderr (which error/info/warn go through) to be a
|
|
# pipe for atomic write reasons; thus if it isn't, abuse cat to make it
|
|
# so.
|
|
if [ ! -p /dev/stderr ]; then
|
|
debug "Replacing stderr with a cat process for pipe requirements..."
|
|
exec 2> >(cat 1>&2)
|
|
fi
|
|
|
|
if [ -z "${FLAGS_ARGV}" ]; then
|
|
# Sort on size; we want to start the largest first since it's likely going
|
|
# to be the chrome binary (which can take 98% of the runtime when we're
|
|
# running with parallelization for 6 or higher).
|
|
for debug_file in $(find "${DEBUG_ROOT}" -name \*.debug \
|
|
-type f -exec stat -c '%s %n' {} + | sort -gr | cut -d' ' -f2-); do
|
|
process_file "${debug_file}"
|
|
done
|
|
else
|
|
for either_file in ${FLAGS_ARGV}; do
|
|
either_file=${either_file#\'}
|
|
either_file=${either_file%\'}
|
|
if [ ! -h "${either_file}" -a ! -f "${either_file}" ]; then
|
|
error "Specified file ${either_file} does not exist"
|
|
: $(( ++ERROR_COUNT ))
|
|
continue
|
|
fi
|
|
if [ "${either_file##*.}" == "debug" ]; then
|
|
debug_file="${either_file}"
|
|
else
|
|
debug_file="$(get_debug_for_text ${either_file})"
|
|
fi
|
|
process_file "${debug_file}"
|
|
done
|
|
fi
|
|
|
|
while [[ ${#JOBS_ARRAY[@]} != 0 ]]; do
|
|
finish_job
|
|
done
|
|
|
|
local size=$(sudo find "${FLAGS_minidump_symbol_root}" \
|
|
-type f -name '*.sym' -exec du -b {} + | \
|
|
awk '{t += $1} END {print t}')
|
|
info "Generated ${size:-0}B of unique debug information"
|
|
|
|
if [[ ${ERROR_COUNT} == 0 ]]; then
|
|
return 0
|
|
fi
|
|
die_notrace "Encountered ${ERROR_COUNT} problems"
|
|
}
|
|
|
|
main "$@"
|