blob: 57bb3aa45168f2612f54d8831f182509b1d49eb2 [file] [log] [blame]
#!/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.
#
# --- BEGIN COMMON.SH BOILERPLATE ---
# Load common CrOS utilities. Inside the chroot this file is installed in
# /usr/lib/crosutils. Outside the chroot we find it relative to the script's
# location.
find_common_sh() {
local common_paths=(/usr/lib/crosutils $(dirname "$(readlink -f "$0")"))
local path
SCRIPT_ROOT=
for path in "${common_paths[@]}"; do
if [ -r "${path}/common.sh" ]; then
SCRIPT_ROOT=${path}
break
fi
done
}
find_common_sh
. "${SCRIPT_ROOT}/common.sh" || { echo "Unable to load common.sh"; exit 1; }
# --- END COMMON.SH BOILERPLATE ---
# 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."
CUMULATIVE_SIZE=0
ANY_ERRORS=0
SYM_FILE=$(mktemp "/tmp/sym.XXXX")
ERR_FILE=$(mktemp "/tmp/err.XXXX")
function cleanup() {
rm -f "${SYM_FILE}" "${ERR_FILE}"
}
# Given path to a debug file, return its text file
function 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
function 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 64-bit ELF file.
function is_64b_elf() {
local elf="$1"
file "${elf}" | grep -q "ELF 64-bit"
}
# Verify the file given is an ELF file that dump_syms can handle. A 32-bit
# dump_syms cannot dump a 64-bit ELF file, and vice versa.
function verify_not_mismatched_elf() {
local elf="$1"
if [ ${DUMP_SYMS_IS_64BIT} -ne 0 ]; then
if ! is_64b_elf "${elf}"; then
warn "Not dumping symbols for file ${elf} because it is not 64-bit"
return 1
fi
else
if is_64b_elf "${elf}"; then
warn "Not dumping symbols for file ${elf} because it is 64-bit"
return 1
fi
fi
return 0
}
# Dump given debug and text file. Returns 1 if any errors, even
# if they can be ignored, but only sets ANY_ERRORS if the error should not
# be ignored (and we should not proceed to upload).
function dump_file() {
local debug_file="$1"
local text_file="$2"
local debug_directory="$(dirname "${debug_file}")"
# 32-bit dump_syms cannot dump a 64-bit ELF file, and vice versa
verify_not_mismatched_elf "${text_file}" || return 1
# Dump symbols as root in order to read all files.
if ! sudo "${DUMP_SYMS}" "${text_file}" "${debug_directory}" > "${SYM_FILE}" \
2> "${ERR_FILE}"; then
# Try dumping just the main file to get public-only symbols.
if ! sudo "${DUMP_SYMS}" "${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}"
return 1
fi
error "Unable to dump symbols for ${text_file}:"
cat "${ERR_FILE}"
ANY_ERRORS=1
return 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)
if [ ${FLAGS_verbose} -eq ${FLAGS_TRUE} ]; then
# Show file upload success and symbol info for easier lookup
info "Dumped symbols from ${text_file} for ${module_name}|${file_id}."
fi
# 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 ! diff "${installed_sym}" "${SYM_FILE}"; then
error "${installed_sym} differ from current sym file:"
diff "${installed_sym}" "${SYM_FILE}"
ANY_ERRORS=1
return 1
fi
fi
size=$(wc -c "${SYM_FILE}" | cut -d' ' -f1)
CUMULATIVE_SIZE=$((CUMULATIVE_SIZE + $size))
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"
return 0
}
# Convert the given debug file. No return value.
function process_file() {
local debug_file="$1"
local text_file="$(get_text_for_debug ${debug_file})"
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.
if [ ${FLAGS_verbose} -eq ${FLAGS_TRUE} ]; then
info "Skipping kernel object: ${text_file}"
fi
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
fi
dump_file "${debug_file}" "${text_file}"
}
function main() {
trap cleanup EXIT
# Parse command line
FLAGS_HELP="usage: $0 [flags] [<files...>]"
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
set -e
[ -n "$FLAGS_board" ] || die "--board is required."
case "$(portageq-${FLAGS_board} envvar ARCH)" in
amd64)
echo "Detected amd64 board.."
DUMP_SYMS="/build/${FLAGS_board}/usr/bin/dump_syms"
DUMP_SYMS_IS_64BIT=1
;;
*)
DUMP_SYMS="dump_syms"
DUMP_SYMS_IS_64BIT=0
esac
echo "Using dump_sys: $DUMP_SYMS"
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"
CUMULATIVE_SIZE=0
sudo rm -rf "${FLAGS_minidump_symbol_root}"
if [ -z "${FLAGS_ARGV}" ]; then
for debug_file in $(find "${DEBUG_ROOT}" -name \*.debug); do
! process_file "${debug_file}"
done
else
for either_file in ${FLAGS_ARGV}; do
either_file=${either_file#\'}
either_file=${either_file%\'}
if [ ! -f "${either_file}" ]; then
error "Specified file ${either_file} does not exist"
ANY_ERRORS=1
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
info "Generated ${CUMULATIVE_SIZE}B of debug information"
[ ${ANY_ERRORS} -ne 0 ] && die "Encountered problems"
return 0
}
main "$@"