| #!/bin/bash |
| # Copyright (c) 2010 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." |
| |
| DUMP_SYMS="dump_syms" |
| |
| 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} |
| } |
| |
| # Verify the file given is not a 64-bit ELF file. For now all targets |
| # are 32-bit, we'll need to determine the correct bit automatically |
| # once we release 64-bit versions. Allow files in /usr/lib64 to exist |
| # on the image and only give warnings. |
| function verify_not_64b_elf() { |
| local elf="$1" |
| if file "${elf}" | grep -q "ELF 64-bit"; then |
| # Allow with a warning if in /usr/lib64 |
| if echo "${elf}" | grep -q /usr/lib64; then |
| warn "64-bit usr/lib64 file ${elf} ignored." |
| else |
| error "File ${elf} is a 64b executable" |
| ANY_ERRORS=1 |
| fi |
| return 1 |
| 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}")" |
| # 64b ELF files may be installed on the target in PERL directories |
| verify_not_64b_elf "${debug_file}" || return 1 |
| verify_not_64b_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 |
| # 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 |
| 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 [ "${text_file#${AUTOTEST_ROOT}}" != "${text_file}" ]; then |
| # Skip autotest files, they are not part of the image to debug |
| # and some cause trouble to dump_syms because they are built |
| # externally (with different build options). |
| if [ ${FLAGS_verbose} -eq ${FLAGS_TRUE} ]; then |
| info "Skipping autotest file: ${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." |
| |
| 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" |
| AUTOTEST_ROOT="${SYSROOT}/usr/local/autotest" |
| CUMULATIVE_SIZE=0 |
| |
| 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 "$@" |