chromium/third_party/blink/tools/debug_renderer

#!/bin/bash
# Copyright 2015 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Runs Chrome / content_shell and attaches to the renderer process for the
# target URL (or the first navigation if none is specified).

usage() {
  echo "usage: $(basename $0) [-d <debug-url>] <path-to-content-shell> <url> [more-args...]"
  exit 1
}

DEFAULT_TARGET_FLAGS=(--no-sandbox --disable-hang-monitor --wait-for-debugger-on-navigation)

while getopts "h?d:" OPTION
do
  case $OPTION in
    h|\?)
      usage
      ;;
    d)
      DEBUG_URL=$OPTARG
      ;;
  esac
done

shift $[$OPTIND-1]

TARGET=$1
shift

if [ -z "$TARGET" ]; then
  usage
fi

if [[ "$TARGET" =~ run_web_tests.py$ ]]; then
  # Adjust flags to be passed to driver
  WEB_TESTS_FLAGS=(--jobs=1 --timeout-ms=100000000 --driver-logging)
  for DEFAULT_FLAG in "${DEFAULT_TARGET_FLAGS[@]}"; do
    WEB_TESTS_FLAGS=(${WEB_TESTS_FLAGS[@]} --additional-driver-flag="$DEFAULT_FLAG")
  done
  TARGET_FLAGS=(${WEB_TESTS_FLAGS[@]} "$@")
elif [[ "$TARGET" =~ run_wpt_tests.py$ ]]; then
  WPT_TESTS_FLAGS=(--jobs=1 --timeout-multiplier=10000 --verbose)
  for DEFAULT_FLAG in "${DEFAULT_TARGET_FLAGS[@]}"; do
    WPT_TESTS_FLAGS=(${WPT_TESTS_FLAGS[@]} --additional-driver-flag="$DEFAULT_FLAG")
  done
  TARGET_FLAGS=(${WPT_TESTS_FLAGS[@]} "$@")
else
  TARGET_FLAGS=(${DEFAULT_TARGET_FLAGS[@]} "$@")
fi

if [ -z "$DEBUG_URL" ]; then
  echo "Debugging first renderer"
fi

# TODO: If you pass a URL containing characters that require URL encoding,
# the URL will be encoded by chrome and won't be equivalent to the $DEBUG_URL.
# We should url encode DEBUG_URL so that it matches the url chrome navigates to.
RENDERER_PID_RE='Renderer url="([^"]+)" \(([0-9]+)\) paused waiting for debugger'

if [ -z "$DEBUGGER" ]; then
  if which lldb > /dev/null; then
    DEBUGGER="lldb"
    CONTINUE="continue"
  elif which gdb > /dev/null; then
    DEBUGGER="gdb -q"
    CONTINUE="signal SIGUSR1"
  else
    echo "no debugger found"
    exit 1
  fi
fi

OUTPUT=$(mktemp "${TMPDIR:-/tmp}"/"$(basename $0)".XXXXX)

UNBUFFER=""
# Use unbuffer to ensure we get the output while the subprocess is paused
# waiting for a renderer.
if [ -z `which unbuffer` ]; then
  echo "Warning: Couldn't find unbuffer. Buffered output can result in" \
        "failure to read the PID of the paused renderer from the pipe. If" \
        "you encounter a hang install unbuffer tool from your package manager."
else
  UNBUFFER="unbuffer "
fi

maybe_kill() {
  [ -n "$1" ] && ps -p $1 > /dev/null && kill $1
}

cleanup() {
  rm $OUTPUT
  maybe_kill "$BROWSER_PID"
  maybe_kill "$SIGNAL_PID"
}

trap cleanup EXIT
echo "Running ${UNBUFFER}${TARGET} ${TARGET_FLAGS[@]}" >&2
${UNBUFFER}"${TARGET}" "${TARGET_FLAGS[@]}" > >(tee $OUTPUT) 2>&1 &
BROWSER_PID=$!
echo "Process $BROWSER_PID logging to $OUTPUT"

wait_renderer_pid() {
  NEXT_LINE=1
  tail +1f $OUTPUT | while read LINE; do
    NEXT_LINE=$[$NEXT_LINE + 1]
    if [[ "$LINE" =~ $RENDERER_PID_RE ]]; then
      RENDERER_URL=${BASH_REMATCH[1]}
      RENDERER_PID=${BASH_REMATCH[2]}
      if [ -z "$DEBUG_URL" ] || [[ "$RENDERER_URL" == "$DEBUG_URL" ]]; then
        echo "$NEXT_LINE $RENDERER_PID"
        return
      fi
      # Unblock unrelated renderers.
      kill -s SIGUSR1 "$RENDERER_PID"
    fi
  done
}

signal_renderers() {
  STARTING_OUTPUT_LINE=$1
  tail +${STARTING_OUTPUT_LINE}f $OUTPUT | while read LINE; do
    if [[ "$LINE" =~ $RENDERER_PID_RE ]]; then
      kill -s SIGUSR1 ${BASH_REMATCH[2]}
    fi
  done
}

RESULT=$(wait_renderer_pid)
NEXT_LINE=$(echo $RESULT|cut -d' ' -f 1)
RENDERER_PID=$(echo $RESULT|cut -d' ' -f 2)
if [ -n "$RENDERER_PID" ]; then
  echo "Target renderer found. Sending SIGUSR1 to unblock any subsequent renderers."
  signal_renderers ${NEXT_LINE} &
  SIGNAL_PID=$!
  # print yellow message
  echo -e "\n\033[1;33mDebugging renderer, use '$CONTINUE' to run.\033[0m\n"
  $DEBUGGER -p $RENDERER_PID
fi