Skip to content

Commit

Permalink
Allow debugging the navigation of a renderer by URL
Browse files Browse the repository at this point in the history
This adds the --wait-for-debugger-on-navigation flag which pauses
renderers when the target URL is known displaying a message for
each. The debug_renderer script is also updated to automatically
debug the site passed and unblock all other renderer processes.

Documentation in linux/debugging.md is updated to explain how
to use the updated script and flag.

Bug: 730065
Change-Id: I758bf6d10ba9b6a846d216987c20053d4c5ac240
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4028699
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Commit-Queue: Robert Flack <flackr@chromium.org>
Reviewed-by: Stephen McGruer <smcgruer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1073426}
  • Loading branch information
Robert Flack authored and Chromium LUCI CQ committed Nov 18, 2022
1 parent c91ce36 commit cdbf8c4
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 25 deletions.
1 change: 1 addition & 0 deletions content/browser/renderer_host/render_process_host_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3364,6 +3364,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
switches::kVideoThreads,
switches::kVideoUnderflowThresholdMs,
switches::kVModule,
switches::kWaitForDebuggerOnNavigation,
switches::kWebAuthRemoteDesktopSupport,
switches::kWebViewDrawFunctorUsesVulkan,
switches::kWebglAntialiasingMode,
Expand Down
5 changes: 5 additions & 0 deletions content/public/common/content_switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,11 @@ const char kValidateInputEventStream[] = "validate-input-event-stream";
// kWaitForDebugger flag passed on or not.
const char kWaitForDebuggerChildren[] = "wait-for-debugger-children";

// On every navigation a message with the renderer's URL will be logged and the
// renderer will wait for a debugger to be attached or SIGUSR1 to be sent to
// continue execution.
const char kWaitForDebuggerOnNavigation[] = "wait-for-debugger-on-navigation";

// Flag used by WebUI test runners to wait for debugger to be attached.
const char kWaitForDebuggerWebUI[] = "wait-for-debugger-webui";

Expand Down
1 change: 1 addition & 0 deletions content/public/common/content_switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ CONTENT_EXPORT extern const char kUtilitySubType[];
CONTENT_EXPORT extern const char kV8CacheOptions[];
CONTENT_EXPORT extern const char kValidateInputEventStream[];
CONTENT_EXPORT extern const char kWaitForDebuggerChildren[];
CONTENT_EXPORT extern const char kWaitForDebuggerOnNavigation[];
CONTENT_EXPORT extern const char kWaitForDebuggerWebUI[];
CONTENT_EXPORT extern const char kWebAuthRemoteDesktopSupport[];
CONTENT_EXPORT extern const char kWebglAntialiasingMode[];
Expand Down
8 changes: 8 additions & 0 deletions content/renderer/render_frame_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "cc/trees/ukm_manager.h"
#include "content/common/associated_interfaces.mojom.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/content_switches_internal.h"
#include "content/common/debug_utils.h"
#include "content/common/features.h"
#include "content/common/frame.mojom.h"
Expand Down Expand Up @@ -3704,6 +3705,13 @@ void RenderFrameImpl::DidCommitNavigation(
TRACE_EVENT2("navigation,rail", "RenderFrameImpl::didCommitProvisionalLoad",
"id", routing_id_, "url",
GetLoadingUrl().possibly_invalid_spec());
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWaitForDebuggerOnNavigation)) {
std::string renderer =
base::StrCat({"Renderer url=\"",
TrimURL(GetLoadingUrl().possibly_invalid_spec()), "\""});
content::WaitForDebugger(renderer);
}

// Generate a new embedding token on each document change.
GetWebFrame()->SetEmbeddingToken(base::UnguessableToken::Create());
Expand Down
42 changes: 41 additions & 1 deletion docs/linux/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,46 @@ else
fi
```

#### Choosing renderer to debug by URL

In most cases you'll want to debug the renderer which is loading a particular
site. If you want a script which will automatically debug the renderer which has
visited a given target URL and continue all other renderers, you can use the
following:

```sh
./third_party/blink/tools/debug_renderer out/Default/content_shell https://example.domain/path
```

The script also supports specifying a different URL than the navigation URL.
This is useful when the renderer you want to debug is not the top frame but one
of the subframes on the page. For example, you could debug a particular subframe
on a page with:

```sh
./third_party/blink/tools/debug_renderer -d https://subframe.url/path out/Default/content_shell https://example.domain/path
```

However, if you need more fine-grained control over which renderers to debug
you can run chrome or content_shell directly with the
`--wait-for-debugger-on-navigation` flag which will pause each renderer at the
point of navigation (when the URL is known).

This will result in a series of lines such as the following in the output:
```
...:content_switches_internal.cc(119)] Renderer url="https://example.domain/path" (PID) paused waiting for debugger to attach. Send SIGUSR1 to unpause.
```

You can signal the renderers you aren't interested in to continue running with:
```sh
kill -s SIGUSR1 <pid>
```

And debug the renderer you are interested in debugging with:
```sh
gdb -p <pid>
```

#### Selective breakpoints

When debugging both the browser and renderer process, you might want to have
Expand Down Expand Up @@ -601,4 +641,4 @@ https://developer.mozilla.org/en/Debugging_Mozilla_on_Linux_FAQ
Symbols for Google Chrome's official builds are available from
`https://edgedl.me.gvt1.com/chrome/linux/symbols/google-chrome-debug-info-linux64-${VERSION}.zip`
where ${VERSION} is any version of Google Chrome that has recently been served
to Stable, Beta, or Unstable (Dev) channels on Linux.
to Stable, Beta, or Unstable (Dev) channels on Linux.
104 changes: 80 additions & 24 deletions third_party/blink/tools/debug_renderer
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,58 @@
# 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 first renderer process in gdb.
# 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]

RENDERER_PID_RE='Renderer \(([0-9]+)\) paused waiting for debugger'
DEFAULT_TARGET_FLAGS=(--no-sandbox --renderer-startup-dialog)
TARGET=$1
shift

if [ -z "$TARGET" ]; then
echo "usage: $(basename $0) <path-to-content-shell> [more-args...]"
exit 1
usage
fi

SAW_DISABLE_FEATURES=false
for TARGET_FLAG in "$@"; do
if [[ "$TARGET_FLAG" == "--disable-features="* ]]; then
TARGET_FLAG="$TARGET_FLAG,SpareRendererForSitePerProcess"
SAW_DISABLE_FEATURES=true
if ! [[ "$TARGET_FLAG" == "--"* ]]; then
URL=$TARGET_FLAG
break
fi
TARGET_FLAGS+=("$TARGET_FLAG")
done
if [ $SAW_DISABLE_FEATURES != true ]; then
DEFAULT_TARGET_FLAGS+=(--disable-features=SpareRendererForSitePerProcess)

if [ -z "$DEBUG_URL"]; then
if [ -z "$URL"]; then
echo "Debugging first renderer"
else
echo "Debugging renderer for navigation URL: $URL"
DEBUG_URL=$URL
fi
fi
TARGET_FLAGS=(${DEFAULT_TARGET_FLAGS[@]} ${TARGET_FLAGS[@]})

# 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'
TARGET_FLAGS=(${DEFAULT_TARGET_FLAGS[@]} "$@")

if [ -z "$DEBUGGER" ]; then
if which lldb > /dev/null; then
Expand All @@ -42,28 +70,56 @@ if [ -z "$DEBUGGER" ]; then
fi

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

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 "$TARGET" ${TARGET_FLAGS[@]} >&2
"$TARGET" ${TARGET_FLAGS[@]} > >(tee $OUTPUT) 2>&1 &
BROWSER_PID=$!

wait_renderer_pid() {
for i in {1..100}; do
browser_running || return
RENDERER_PID=$(renderer_pid)
[ -n "$RENDERER_PID" ] && return
sleep 0.2
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
}

browser_running() { ps -p $BROWSER_PID > /dev/null; }
renderer_pid() { [[ "$(cat $OUTPUT)" =~ $RENDERER_PID_RE ]] && echo ${BASH_REMATCH[1]}; }
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
}

wait_renderer_pid
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

wait

0 comments on commit cdbf8c4

Please sign in to comment.