#!/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 upload all debug symbols required for crash reporting
# purposes.  This script need only be used to upload release builds
# symbols or to debug crashes on non-release builds (in which case try
# to only upload the symbols for those executables involved).

# --- 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 breakpad_root "" "Root directory for breakpad symbols."
DEFINE_boolean official_build ${FLAGS_FALSE} "Point to official symbol server."
DEFINE_boolean regenerate ${FLAGS_FALSE} "Regenerate all symbols."
DEFINE_boolean strip_x86_cfi ${FLAGS_TRUE} "Strip CFI data for x86."
DEFINE_boolean verbose ${FLAGS_FALSE} "Be verbose."
DEFINE_boolean yes ${FLAGS_FALSE} "Answer yes to all prompts."

SYM_UPLOAD="sym_upload"

ANY_ERRORS=0

OUT_DIR=$(mktemp -d "/tmp/err.XXXX")

function cleanup() {
  rm -rf "${OUT_DIR}"
}

function really_upload() {
  if [ ${FLAGS_yes} -eq ${FLAGS_TRUE} ]; then
    return 0
  fi
  echo "Uploading symbols for an entire Chromium OS build is really only "
  echo "necessary for release builds and in a few cases for developers "
  echo "to debug problems.  It will take considerable time to run.  For "
  echo "developer debugging purposes, consider instead passing specific files "
  echo "to upload."
  read -p "Are you sure you want to upload all build symbols (y/N)? " SURE
  SURE="${SURE:0:1}" # Get just the first character
  if [ "${SURE}" != "y" ]; then
    echo "Ok, better safe than sorry."
    return 1
  fi
  return 0
}

# Upload the given symbol file to given URL.
function upload_file() {
  local symbol_file="$1"
  local upload_url="$2"
  local upload_file="${symbol_file}"
  if [ ${FLAGS_strip_x86_cfi} -eq ${FLAGS_TRUE} ]; then
    local arch=$(portageq-${FLAGS_board} envvar ARCH)
    if [ "${arch}" == "x86" ]; then
      # Call frame info is unnecessary for 32b x86 targets with frame
      # pointer used (as all of ours have) and it accounts for over
      # half the size of the symbols uploaded.  To buy us more time
      # for the server team to increase the current 160MB, remove this
      # data unless the user requests it.
      if [ ${FLAGS_verbose} -eq ${FLAGS_TRUE} ]; then
        info "Stripping CFI for ${symbol_file} due to board ${FLAGS_board}."
      fi
      upload_file="${OUT_DIR}/stripped.sym"
      sed '/^STACK CFI/d' < "${symbol_file}" > "${upload_file}"
    fi
  fi
  if [ ${FLAGS_verbose} -eq ${FLAGS_TRUE} ]; then
    info "Uploading ${symbol_file}"
  fi
  "${SYM_UPLOAD}" "${upload_file}" "${upload_url}" > "${OUT_DIR}/stdout" \
    2> "${OUT_DIR}/stderr"
  if ! grep -q "Successfully sent the symbol file." "${OUT_DIR}/stdout"; then
    error "Unable to upload symbols in ${symbol_file}:"
    cat "${OUT_DIR}/stderr"
    ANY_ERRORS=1
    return 1
  fi
  if [ ${FLAGS_verbose} -eq ${FLAGS_TRUE} ]; then
    size=$(wc -c "${upload_file}" | cut -d' ' -f1)
    info "Successfully uploaded ${size}B."
  fi
  return 0
}

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}"

  local upload_url=""
  if [ $FLAGS_official_build -eq $FLAGS_TRUE ]; then
    upload_url="http://clients2.google.com/cr/symbol"
  else
    upload_url="http://clients2.google.com/cr/staging_symbol"
    warn "This is an unofficial build, uploading to staging server."
  fi
  info "Uploading symbols to ${upload_url} from ${SYSROOT}."

  DEFAULT_BREAKPAD_ROOT="${SYSROOT}/usr/lib/debug/breakpad"
  if [ -z "${FLAGS_breakpad_root}" ]; then
    FLAGS_breakpad_root="${DEFAULT_BREAKPAD_ROOT}"
  else
    if [ ${FLAGS_regenerate} -eq ${FLAGS_TRUE} ]; then
      warn "Assuming --noregenerate when --breakpad_root is specified"
      FLAGS_regenerate=${FLAGS_FALSE}
    fi
  fi

  if [ -z "${FLAGS_ARGV}" ]; then
    if [ ${FLAGS_regenerate} -eq ${FLAGS_TRUE} ]; then
      really_upload || exit 1
      info "Clearing ${DEFAULT_BREAKPAD_ROOT}"
      sudo rm -rf "${DEFAULT_BREAKPAD_ROOT}"
      info "Generating all breakpad symbol files."
      local verbosity=""
      local generate_script="${SCRIPTS_DIR}/cros_generate_breakpad_symbols"
      [ ${FLAGS_verbose} -eq ${FLAGS_TRUE} ] && verbosity="--verbose"
      if ! "${generate_script}" --board=${FLAGS_board} ${verbosity}; then
        error "Some errors while generating symbols; uploading anyway"
        ANY_ERRORS=1
      fi
    fi

    info "Uploading all breakpad symbol files."
    for sym_file in $(find "${FLAGS_breakpad_root}" -name \*.sym); do
      ! upload_file "${sym_file}" "${upload_url}"
    done
  else
    error "Unexpected args ${FLAGS_ARGV}"
  fi

  [ ${ANY_ERRORS} -ne 0 ] && die "Encountered problems"
  return 0
}

main "$@"
