OLD | NEW |
---|---|
(Empty) | |
1 #!/bin/bash | |
2 # | |
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 # | |
7 | |
8 # A generic script used to attach to a running Chromium process and | |
9 # debug it. Most users should not use this directly, but one of the | |
10 # wrapper scripts like adb_gdb_content_shell, or adb_gdb_drt | |
11 # | |
12 # Use --help to print full usage instructions. | |
13 # | |
14 | |
15 PROGNAME=$(basename "$0") | |
16 PROGDIR=$(dirname "$0") | |
17 | |
18 # Location of Chromium-top-level sources. | |
19 CHROMIUM_SRC=$(cd "$PROGDIR"/../.. && pwd 2>/dev/null) | |
20 | |
21 TMPDIR= | |
22 GDBSERVER_PIDFILE= | |
23 TARGET_GDBSERVER= | |
24 | |
25 clean_exit () { | |
bulach
2012/10/17 14:20:04
these functions here (up to 54:log) have the "{" i
digit1
2012/10/18 12:33:26
Done.
| |
26 if [ "$TMPDIR" ]; then | |
27 GDBSERVER_PID=$(cat $GDBSERVER_PIDFILE 2>/dev/null) | |
28 if [ "$GDBSERVER_PID" ]; then | |
29 log "Killing background gdbserver process: $GDBSERVER_PID" | |
30 kill -9 $GDBSERVER_PID >/dev/null 2>&1 | |
31 fi | |
32 if [ "$TARGET_GDBSERVER" ]; then | |
33 log "Removing target gdbserver binary: $TARGET_GDBSERVER." | |
34 "$ADB" shell rm "$TARGET_GDBSERVER" >/dev/null 2>&1 | |
35 fi | |
36 log "Cleaning up: $TMPDIR" | |
37 rm -rf "$TMPDIR" | |
38 fi | |
39 exit $1 | |
40 } | |
41 | |
42 # Ensure clean exit on Ctrl-C. | |
43 trap "clean_exit 1" INT | |
44 | |
45 panic () { | |
46 echo "ERROR: $@" >&2 | |
47 clean_exit 1 | |
48 } | |
49 | |
50 fail_panic () { | |
51 if [ $? != 0 ]; then panic "$@"; fi | |
52 } | |
53 | |
54 log () { | |
55 if [ "$VERBOSE" -gt 0 ]; then | |
56 echo "$@" | |
57 fi | |
58 } | |
59 | |
60 DEFAULT_PULL_LIBS_DIR=/tmp/$USER-adb-gdb-libs | |
61 | |
62 # NOTE: Allow wrapper scripts to set various default through ADB_GDB_XXX | |
63 # environment variables. This is only for cosmetic reasons, i.e. to | |
64 # display proper | |
65 | |
66 # Allow wrapper scripts to set the default activity through | |
67 # the ADB_GDB_ACTIVITY variable. Users are still able to change the | |
68 # final activity name through --activity=<name> option. | |
69 # | |
70 # This is only for cosmetic reasons, i.e. to display the proper default | |
71 # in the --help output. | |
72 # | |
73 DEFAULT_ACTIVITY=${ADB_GDB_ACTIVITY:-".Main"} | |
74 | |
75 # Allow wrapper scripts to set the program name through ADB_GDB_PROGNAME | |
76 PROGNAME=${ADB_GDB_PROGNAME:-$(basename "$0")} | |
77 | |
78 ACTIVITY=$DEFAULT_ACTIVITY | |
79 ADB= | |
80 ANNOTATE= | |
81 # Note: Ignore BUILDTYPE variable, because the Ninja build doesn't use it. | |
82 BUILDTYPE= | |
83 FORCE= | |
84 GDBINIT= | |
85 GDBSERVER= | |
86 HELP= | |
87 NDK_DIR= | |
88 NO_PULL_LIBS= | |
89 PACKAGE_NAME= | |
90 PID= | |
91 PROGRAM_NAME="activity" | |
92 PULL_LIBS= | |
93 PULL_LIBS_DIR= | |
94 SANDBOXED= | |
95 SANDBOXED_INDEX= | |
96 START= | |
97 SYMBOL_DIR= | |
98 TARGET_ARCH= | |
99 TOOLCHAIN= | |
100 VERBOSE=0 | |
101 | |
102 for opt; do | |
103 optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') | |
104 case $opt in | |
105 --adb=*) | |
106 ADB=$optarg | |
107 ;; | |
108 --activity=*) | |
109 ACTIVITY=$optarg | |
110 ;; | |
111 --annotate=3) | |
112 ANNOTATE=$optarg | |
113 ;; | |
114 --force) | |
115 FORCE=true | |
116 ;; | |
117 --gdbserver=*) | |
118 GDBSERVER=$optarg | |
119 ;; | |
120 --help|-h|-?) | |
121 HELP=true | |
122 ;; | |
123 --ndk-dir=*) | |
124 NDK_DIR=$optarg | |
125 ;; | |
126 --no-pull-libs) | |
127 NO_PULL_LIBS=true | |
128 ;; | |
129 --package-name=*) | |
130 PACKAGE_NAME=$optarg | |
131 ;; | |
132 --pid=*) | |
133 PID=$optarg | |
134 ;; | |
135 --program-name=*) | |
136 PROGRAM_NAME=$optarg | |
137 ;; | |
138 --pull-libs) | |
139 PULL_LIBS=true | |
140 ;; | |
141 --pull-libs-dir=*) | |
142 PULL_LIBS_DIR=$optarg | |
143 ;; | |
144 --sandboxed) | |
145 SANDBOXED=true | |
146 ;; | |
147 --sandboxed=*) | |
148 SANDBOXED=true | |
149 SANDBOXED_INDEX=$optarg | |
150 ;; | |
151 --script=*) | |
152 GDBINIT=$optarg | |
153 ;; | |
154 --start) | |
155 START=true | |
156 ;; | |
157 --symbol-dir=*) | |
158 SYMBOL_DIR=$optarg | |
159 ;; | |
160 --target-arch=*) | |
161 TARGET_ARCH=$optarg | |
162 ;; | |
163 --toolchain=*) | |
164 TOOLCHAIN=$optarg | |
165 ;; | |
166 --verbose) | |
167 VERBOSE=$(( $VERBOSE + 1 )) | |
168 ;; | |
169 --debug) | |
170 BUILDTYPE=Debug | |
171 ;; | |
172 --release) | |
173 BUILDTYPE=Release | |
174 ;; | |
175 -*) | |
176 panic "Unknown option $OPT, see --help." >&2 | |
177 ;; | |
178 *) | |
179 if [ "$PACKAGE_NAME" ]; then | |
180 panic "You can only provide a single package name as argument!\ | |
181 See --help." | |
182 fi | |
183 PACKAGE_NAME=$opt | |
184 ;; | |
185 esac | |
186 done | |
187 | |
188 print_help_options () | |
189 { | |
190 cat <<EOF | |
191 EOF | |
192 } | |
193 | |
194 if [ "$HELP" ]; then | |
195 if [ "$ADB_GDB_PROGNAME" ]; then | |
196 # Assume wrapper scripts all provide a default package name. | |
197 cat <<EOF | |
198 Usage: $PROGNAME [options] | |
199 | |
200 Attach gdb to a running Android $PROGRAM_NAME process. | |
201 EOF | |
202 else | |
203 # Assume this is a direct call to adb_gdb | |
204 cat <<EOF | |
205 Usage: $PROGNAME [options] [<package-name>] | |
206 | |
207 Attach gdb to a running Android $PROGRAM_NAME process. | |
208 | |
209 If provided, <package-name> must be the name of the Android application's | |
210 package name to be debugged. You can also use --package-name=<name> to | |
211 specify it. | |
212 EOF | |
213 fi | |
214 | |
215 cat <<EOF | |
216 | |
217 This script is used to debug a running $PROGRAM_NAME process. | |
218 This can be a regular Android application process, or a sandboxed | |
219 service, if you use the --sandboxed or --sandboxed=<num> option. | |
220 | |
221 This script needs several things to work properly. It will try to pick | |
222 them up automatically for you though: | |
223 | |
224 - target gdbserver binary | |
225 - host gdb client (e.g. arm-linux-androideabi-gdb) | |
226 - directory with symbolic version of $PROGRAM_NAME's shared libraries. | |
227 | |
228 If you have sourced Chromium's build/android/envsetup.sh, this script will | |
229 find all of them automatically. This is the recommended way to use it. | |
230 | |
231 Otherwise, if you have ANDROID_NDK_ROOT defined in your environment, | |
232 the script will use it to find the gdb and gdbserver binaries. You can | |
233 also use --ndk-dir=<path> to specify an alternative NDK installation | |
234 directory. | |
235 | |
236 The script tries to find the most recent version of the debug version of | |
237 shared libraries under one of the following directories: | |
238 | |
239 \$CHROMIUM_SRC/out/Release/lib/ (used by Ninja builds) | |
240 \$CHROMIUM_SRC/out/Debug/lib/ (used by Ninja builds) | |
241 \$CHROMIUM_SRC/out/Release/lib.target/ (used by Make builds) | |
242 \$CHROMIUM_SRC/out/Debug/lib.target/ (used by Make builds) | |
243 | |
244 You can restrict this search by using --release or --debug to specify the | |
245 build type, or simply use --symbol-dir=<path> to specify the file manually. | |
246 | |
247 The script tries to extract the target architecture from your GYP_DEFINES, | |
248 but if this fails, will default to 'arm'. Use --target-arch=<name> to force | |
249 its value. | |
250 | |
251 Otherwise, the script will complain, but you can use the --gdbserver, | |
252 --gdb and --symbol-lib options to specify everything manually. | |
253 | |
254 An alternative to --gdb=<file> is to use --toollchain=<path> to specify | |
255 the path to the host target-specific cross-toolchain. | |
256 | |
257 You will also need the 'adb' tool in your path. Otherwise, use the --adb | |
258 option. The script will complain if there is more than one device connected | |
259 and ANDROID_SERIAL is not defined. | |
260 | |
261 The first time you use it on a device, the script will pull many system | |
262 libraries required by the process into a temporary directory. This | |
263 is done to strongly improve the debugging experience, like allowing | |
264 readable thread stacks and more. The libraries are copied to the following | |
265 directory by default: | |
266 | |
267 $DEFAULT_PULL_LIBS_DIR/ | |
268 | |
269 But you can use the --pull-libs-dir=<path> option to specify an | |
270 alternative. The script can detect when you change the connected device, | |
271 and will re-pull the libraries only in this case. You can however force it | |
272 with the --pull-libs option. | |
273 | |
274 Any local .gdbinit script will be ignored, but it is possible to pass a | |
275 gdb command script with the --script=<file> option. Note that its commands | |
276 will be passed to gdb after the remote connection and library symbol | |
277 loading have completed. | |
278 | |
279 Valid options: | |
280 --help|-h|-? Print this message. | |
281 --verbose Increase verbosity. | |
282 | |
283 --sandboxed Debug first sandboxed process we find. | |
284 --sandboxed=<num> Debug specific sandboxed process. | |
285 --symbol-dir=<path> Specify directory with symbol shared libraries. | |
286 --package-name=<name> Specify package name (alternative to 1st argument). | |
287 --program-name=<name> Specify program name (cosmetic only). | |
288 --pid=<pid> Specify application process pid. | |
289 --force Kill any previous debugging session, if any. | |
290 --start Start package's activity on device. | |
291 --activity=<name> Activity name for --start [$DEFAULT_ACTIVITY]. | |
292 --annotate=<num> Enable gdb annotation. | |
293 --script=<file> Specify extra GDB init script. | |
294 | |
295 --gdbserver=<file> Specify targer gdbserver binary. | |
296 --gdb=<program> Specify host gdb client binary. | |
297 --target-arch=<name> Specify NDK target arch. | |
298 --adb=<program> Specify host ADB binary. | |
299 | |
300 --pull-libs Force system libraries extraction. | |
301 --no-pull-libs Do not extract any system library. | |
302 --libs-dir=<path> Specify system libraries extraction directory. | |
303 | |
304 --debug Use libraries under out/Debug. | |
305 --release Use libraries under out/Release. | |
306 | |
307 EOF | |
308 exit 0 | |
309 fi | |
310 | |
311 if [ -z "$PACKAGE_NAME" ]; then | |
312 panic "Please specify a package name on the command line. See --help." | |
313 fi | |
314 | |
315 if [ -z "$NDK_DIR" ]; then | |
316 if [ -z "$ANDROID_NDK_ROOT" ]; then | |
317 panic "Can't find NDK directory, please source \ | |
318 build/android/envsetup.sh!" | |
319 fi | |
320 else | |
321 if [ ! -d "$NDK_DIR" ]; then | |
322 panic "Invalid directory: $NDK_DIR" | |
323 fi | |
324 if [ ! -f "$NDK_DIR/ndk-build" ]; then | |
325 panic "Not a valid NDK directory: $NDK_DIR" | |
326 fi | |
327 ANDROID_NDK_ROOT=$NDK_DIR | |
328 fi | |
329 | |
330 if [ "$GDBINIT" -a ! -f "$GDBINIT" ]; then | |
331 panic "Unknown --script file: $GDBINIT" | |
332 fi | |
333 | |
334 # Find the target architecture from our $GYP_DEFINES | |
335 # This returns an NDK-compatible architecture name. | |
336 # out: NDK Architecture name, or empty string. | |
337 get_gyp_target_arch () | |
338 { | |
339 local ARCH=$(echo $GYP_DEFINES | tr ' ' '\n' | grep '^target_arch=' |\ | |
340 cut -d= -f2) | |
341 case $ARCH in | |
342 ia32|i?86|x86) echo "x86";; | |
343 mips|arm) echo "$ARCH";; | |
344 *) echo ""; | |
345 esac | |
346 } | |
347 | |
348 if [ -z "$TARGET_ARCH" ]; then | |
349 TARGET_ARCH=$(get_gyp_target_arch) | |
350 if [ -z "$TARGET_ARCH" ]; then | |
351 TARGET_ARCH=arm | |
352 fi | |
353 else | |
354 # Nit: accept Chromium's 'ia32' as a valid target architecture. This | |
355 # script prefers the NDK 'x86' name instead because it uses it to find | |
356 # NDK-specific files (host gdb) with it. | |
357 if [ "$TARGET_ARCH" = "ia32" ]; then | |
358 TARGET_ARCH=x86 | |
359 log "Auto-config: --arch=$TARGET_ARCH (equivalent to ia32)" | |
360 fi | |
361 fi | |
362 | |
363 # Detect the NDK system tag, i.e. the name used to identify the host. | |
364 # out: NDK system tag (e.g. 'linux-x86'). | |
365 get_ndk_host_tag () | |
366 { | |
367 if [ -z "$NDK_HOST_TAG" ]; then | |
368 case $(uname -s) in | |
369 Linux) NDK_HOST_TAG=linux-x86;; | |
370 Darwin) NDK_HOST_TAG=darwin-x86;; | |
371 *) panic "You can't run this script on this system: $uname -a";; | |
372 esac | |
373 fi | |
374 echo "$NDK_HOST_TAG" | |
375 } | |
376 | |
377 # Convert an NDK architecture name into a GNU configure triplet. | |
378 # $1: NDK architecture name (e.g. 'arm') | |
379 # Out: Android GNU configure triplet (e.g. 'arm-linux-androideabi') | |
380 get_arch_gnu_config () | |
381 { | |
382 case $1 in | |
383 arm) | |
384 echo "arm-linux-androideabi" | |
385 ;; | |
386 x86) | |
387 echo "i686-linux-android" | |
388 ;; | |
389 mips) | |
390 echo "mipsel-linux-android" | |
391 ;; | |
392 *) | |
393 echo "$ARCH-linux-android" | |
394 ;; | |
395 esac | |
396 } | |
397 | |
398 # Convert an NDK architecture name into a toolchain name prefix | |
399 # $1: NDK architecture name (e.g. 'arm') | |
400 # Out: NDK toolchain name prefix (e.g. 'arm-linux-androideabi') | |
401 get_arch_toolchain_prefix () | |
402 { | |
403 # Return the configure triplet, except for x86! | |
404 if [ "$1" = "x86" ]; then | |
405 echo "$1" | |
406 else | |
407 get_arch_gnu_config $1 | |
408 fi | |
409 } | |
410 | |
411 # Find a NDK toolchain prebuilt file or sub-directory. | |
412 # This will probe the various arch-specific toolchain directories | |
413 # in the NDK for the needed file. | |
414 # $1: NDK install path | |
415 # $2: NDK architecture name | |
416 # $3: prebuilt sub-path to look for. | |
417 # Out: file path, or empty if none is found. | |
418 get_ndk_toolchain_prebuilt () | |
419 { | |
420 local NDK_DIR="${1%/}" | |
421 local ARCH="$2" | |
422 local SUBPATH="$3" | |
423 local NAME="$(get_arch_toolchain_prefix $ARCH)" | |
424 local FILE TARGET | |
425 FILE=$NDK_DIR/toolchains/$NAME-4.6/prebuilt/$SUBPATH | |
426 if [ ! -f "$FILE" ]; then | |
427 FILE=$NDK_DIR/toolchains/$NAME-4.4.3/prebuilt/$SUBPATH | |
428 if [ ! -f "$FILE" ]; then | |
429 FILE= | |
430 fi | |
431 fi | |
432 echo "$FILE" | |
433 } | |
434 | |
435 # Find the path to an NDK's toolchain full prefix for a given architecture | |
436 # $1: NDK install path | |
437 # $2: NDK architecture name | |
438 # Out: install path + binary prefix (e.g. | |
439 # ".../path/to/bin/arm-linux-androideabi-") | |
440 get_ndk_toolchain_fullprefix () | |
441 { | |
442 local NDK_DIR="$1" | |
443 local ARCH="$2" | |
444 local TARGET NAME HOST GCC CONFIG | |
445 | |
446 # NOTE: This will need to be updated if the NDK changes the names or moves | |
447 # the location of its prebuilt toolchains. | |
448 # | |
449 GCC= | |
450 HOST=$(get_ndk_host_tag) | |
451 CONFIG=$(get_arch_gnu_config $ARCH) | |
452 GCC=$(get_ndk_toolchain_prebuilt \ | |
453 "$NDK_DIR" "$ARCH" "$HOST/bin/$CONFIG-gcc") | |
454 if [ ! -f "$GCC" -a "$ARCH" = "x86" ]; then | |
455 # Special case, the x86 toolchain used to be incorrectly | |
456 # named i686-android-linux-gcc! | |
457 GCC=$(get_ndk_toolchain_prebuilt \ | |
458 "$NDK_DIR" "$ARCH" "$HOST/bin/i686-android-linux-gcc") | |
459 fi | |
460 if [ -z "$GCC" ]; then | |
461 panic "Cannot find Android NDK toolchain for '$ARCH' architecture. \ | |
462 Please verify your NDK installation!" | |
463 fi | |
464 echo "${GCC%%gcc}" | |
465 } | |
466 | |
467 # $1: NDK install path | |
468 # $2: target architecture. | |
469 get_ndk_gdbserver () | |
470 { | |
471 local NDK_DIR="$1" | |
472 local ARCH=$2 | |
473 local HOST=$(get_ndk_host_tag) | |
474 local BINARY | |
475 | |
476 # The location has moved after NDK r8 | |
477 BINARY=$NDK_DIR/prebuilt/android-$ARCH/gdbserver/gdbserver | |
478 if [ ! -f "$BINARY" ]; then | |
479 BINARY=$(get_ndk_toolchain_prebuilt "$NDK_DIR" "$ARCH" gdbserver) | |
480 fi | |
481 echo "$BINARY" | |
482 } | |
483 | |
484 # Check/probe the path to the Android toolchain installation. Always | |
485 # use the NDK versions of gdb and gdbserver. They must match to avoid | |
486 # issues when both binaries do not speak the same wire protocol. | |
487 # | |
488 if [ -z "$TOOLCHAIN" ]; then | |
489 ANDROID_TOOLCHAIN=$(get_ndk_toolchain_fullprefix \ | |
490 "$ANDROID_NDK_ROOT" "$TARGET_ARCH") | |
491 ANDROID_TOOLCHAIN=$(dirname "$ANDROID_TOOLCHAIN") | |
492 log "Auto-config: --toolchain=$ANDROID_TOOLCHAIN" | |
493 else | |
494 # Be flexible, allow one to specify either the install path or the bin | |
495 # sub-directory in --toolchain: | |
496 # | |
497 if [ -d "$TOOLCHAIN/bin" ]; then | |
498 TOOLCHAIN=$TOOLCHAIN/bin | |
499 fi | |
500 ANDROID_TOOLCHAIN=$TOOLCHAIN | |
501 fi | |
502 | |
503 # Cosmetic: Remove trailing directory separator. | |
504 ANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN%/} | |
505 | |
506 # Find host GDB client binary | |
507 GDB=$(which $ANDROID_TOOLCHAIN/*-gdb 2>/dev/null | head -1) | |
508 if [ -z "$GDB" ]; then | |
509 panic "Can't find Android gdb client in your path, check your \ | |
510 --toolchain path." | |
511 fi | |
512 log "Host gdb client: $GDB" | |
513 | |
514 # Find gdbserver binary, we will later push it to /data/local/tmp | |
515 # This ensures that both gdbserver and $GDB talk the same binary protocol, | |
516 # otherwise weird problems will appear. | |
517 # | |
518 if [ -z "$GDBSERVER" ]; then | |
519 GDBSERVER=$(get_ndk_gdbserver "$ANDROID_NDK_ROOT" "$TARGET_ARCH") | |
520 if [ -z "$GDBSERVER" ]; then | |
521 panic "Can't find NDK gdbserver binary. use --gdbserver to specify \ | |
522 valid one!" | |
523 fi | |
524 log "Auto-config: --gdbserver=$GDBSERVER" | |
525 fi | |
526 | |
527 | |
528 | |
529 # Check that ADB is in our path | |
530 if [ -z "$ADB" ]; then | |
531 ADB=$(which adb 2>/dev/null) | |
532 if [ -z "$ADB" ]; then | |
533 panic "Can't find 'adb' tool in your path. Install it or use \ | |
534 --adb=<file>" | |
535 fi | |
536 log "Auto-config: --adb=$ADB" | |
537 fi | |
538 | |
539 # Check that it works minimally | |
540 ADB_VERSION=$($ADB version 2>/dev/null) | |
541 echo "$ADB_VERSION" | fgrep -q -e "Android Debug Bridge" | |
542 if [ $? != 0 ]; then | |
543 panic "Your 'adb' tool seems invalid, use --adb=<file> to specify a \ | |
544 different one: $ADB" | |
545 fi | |
546 | |
547 # If there are more than one device connected, and ANDROID_SERIAL is not | |
548 # defined, print an error message. | |
549 NUM_DEVICES_PLUS2=$($ADB devices 2>/dev/null | wc -l) | |
550 if [ "$NUM_DEVICES_PLUS2" -lt 3 -a -z "$ANDROID_SERIAL" ]; then | |
551 echo "ERROR: There is more than one Android device connected to ADB." | |
552 echo "Please define ANDROID_SERIAL to specify which one to use." | |
553 exit 1 | |
554 fi | |
555 | |
556 # A unique ID for this script's session. This needs to be the same in all | |
557 # sub-shell commands we're going to launch, so take the PID of the launcher | |
558 # process. | |
559 TMP_ID=$$ | |
560 | |
561 # Temporary directory, will get cleaned up on exit. | |
562 TMPDIR=/tmp/$USER-adb-gdb-tmp-$TMP_ID | |
563 mkdir -p "$TMPDIR" && rm -rf "$TMPDIR"/* | |
564 | |
565 GDBSERVER_PIDFILE="$TMPDIR"/gdbserver-$TMP_ID.pid | |
566 | |
567 # Run a command through adb shell, strip the extra \r from the output | |
568 # and return the correct status code to detect failures. This assumes | |
569 # that the adb shell command prints a final \n to stdout. | |
570 # $1+: command to run | |
571 # Out: command's stdout | |
572 # Return: command's status | |
573 # Note: the command's stderr is lost | |
574 adb_shell () | |
575 { | |
576 local TMPOUT="$(mktemp)" | |
577 local LASTLINE RET | |
578 local ADB=${ADB:-adb} | |
579 | |
580 # The weird sed rule is to strip the final \r on each output line | |
581 # Since 'adb shell' never returns the command's proper exit/status code, | |
582 # we force it to print it as '%%<status>' in the temporary output file, | |
583 # which we will later strip from it. | |
584 $ADB shell $@ ";" echo "%%\$?" 2>/dev/null | \ | |
585 sed -e 's![[:cntrl:]]!!g' > $TMPOUT | |
586 # Get last line in log, which contains the exit code from the command | |
587 LASTLINE=$(sed -e '$!d' $TMPOUT) | |
588 # Extract the status code from the end of the line, which must | |
589 # be '%%<code>'. | |
590 RET=$(echo "$LASTLINE" | \ | |
591 awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,RSTART+2); } }') | |
592 # Remove the status code from the last line. Note that this may result | |
593 # in an empty line. | |
594 LASTLINE=$(echo "$LASTLINE" | \ | |
595 awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,1,RSTART-1); } }') | |
596 # The output itself: all lines except the status code. | |
597 sed -e '$d' $TMPOUT && printf "%s" "$LASTLINE" | |
598 # Remove temp file. | |
599 rm -f $TMPOUT | |
600 # Exit with the appropriate status. | |
601 return $RET | |
602 } | |
603 | |
604 # If --force is specified, try to kill any gdbserver process started by the | |
605 # same user on the device. Normally, these are killed automatically by the | |
606 # script on exit, but there are a few corner cases where this would still | |
607 # be needed. | |
608 if [ "$FORCE" ]; then | |
609 GDBSERVER_PIDS=$(adb_shell ps | awk '$9 ~ /gdbserver/ { print $2; }') | |
610 for GDB_PID in $GDBSERVER_PIDS; do | |
611 log "Killing previous gdbserver (PID=$GDB_PID)" | |
612 adb_shell kill -9 $GDB_PID | |
613 done | |
614 fi | |
615 | |
616 if [ "$START" ]; then | |
617 log "Starting $PROGRAM_NAME on device." | |
618 adb_shell am start -n $PACKAGE_NAME/$ACTIVITY 2>/dev/null | |
619 adb_shell ps | grep -q $PACKAGE_NAME | |
620 fail_panic "Could not start $PROGRAM_NAME on device. Are you sure the \ | |
621 package is installed?" | |
622 fi | |
623 | |
624 # Return the timestamp of a given time, as number of seconds since epoch. | |
625 # $1: file path | |
626 # Out: file timestamp | |
627 get_file_timestamp () | |
628 { | |
629 stat -c %Y "$1" 2>/dev/null | |
630 } | |
631 | |
632 # Detect the build type and symbol directory. This is done by finding | |
633 # the most recent sub-directory containing debug shared libraries under | |
634 # $CHROMIUM_SRC/out/ | |
635 # | |
636 # $1: $BUILDTYPE value, can be empty | |
637 # Out: nothing, but this sets SYMBOL_DIR | |
638 # | |
639 detect_symbol_dir () { | |
640 local SUBDIRS SUBDIR LIST DIR DIR_LIBS TSTAMP | |
641 # Note: Ninja places debug libraries under out/$BUILDTYPE/lib/, while | |
642 # Make places then under out/$BUILDTYPE/lib.target. | |
643 if [ "$1" ]; then | |
644 SUBDIRS="$1/lib $1/lib.target" | |
645 else | |
646 SUBDIRS="Release/lib Debug/lib Release/lib.target Debug/lib.target" | |
647 fi | |
648 LIST=$TMPDIR/scan-subdirs-$$.txt | |
649 printf "" > "$LIST" | |
650 for SUBDIR in $SUBDIRS; do | |
651 DIR=$CHROMIUM_SRC/out/$SUBDIR | |
652 if [ -d "$DIR" ]; then | |
653 # Ignore build directories that don't contain symbol versions | |
654 # of the shared libraries. | |
655 DIR_LIBS=$(ls "$DIR"/lib*.so 2>/dev/null) | |
656 if [ -z "$DIR_LIBS" ]; then | |
657 echo "No shared libs: $DIR" | |
658 continue | |
659 fi | |
660 TSTAMP=$(get_file_timestamp "$DIR") | |
661 printf "%s %s\n" "$TSTAMP" "$SUBDIR" >> "$LIST" | |
662 fi | |
663 done | |
664 SUBDIR=$(cat $LIST | sort -r | head -1 | cut -d" " -f2) | |
665 rm -f "$LIST" | |
666 | |
667 if [ -z "$SUBDIR" ]; then | |
668 if [ -z "$1" ]; then | |
669 panic "Could not find any build directory under \ | |
670 $CHROMIUM_SRC/out. Please build the program first!" | |
671 else | |
672 panic "Could not find any $1 directory under \ | |
673 $CHROMIUM_SRC/out. Check your build type!" | |
674 fi | |
675 fi | |
676 | |
677 SYMBOL_DIR=$CHROMIUM_SRC/out/$SUBDIR | |
678 log "Auto-config: --symbol-dir=$SYMBOL_DIR" | |
679 } | |
680 | |
681 if [ -z "$SYMBOL_DIR" ]; then | |
682 detect_symbol_dir "$BUILDTYPE" | |
683 fi | |
684 | |
685 # Allow several concurrent debugging sessions | |
686 TARGET_GDBSERVER=/data/local/tmp/gdbserver-adb-gdb-$TMP_ID | |
687 | |
688 # Return the build fingerprint contained in a build.prop file. | |
689 # $1: path to build.prop file | |
690 get_build_fingerprint_from () | |
691 { | |
692 cat "$1" | grep -e '^ro.build.fingerprint=' | cut -d= -f2 | |
693 } | |
694 | |
695 | |
696 ORG_PULL_LIBS_DIR=$PULL_LIBS_DIR | |
697 PULL_LIBS_DIR=${PULL_LIBS_DIR:-$DEFAULT_PULL_LIBS_DIR} | |
698 | |
699 HOST_FINGERPRINT= | |
700 DEVICE_FINGERPRINT=$(adb_shell getprop ro.build.fingerprint) | |
701 log "Device build fingerprint: $DEVICE_FINGERPRINT" | |
702 | |
703 # If --pull-libs-dir is not specified, and this is a platform build, look | |
704 # if we can use the symbolic libraries under $ANDROID_PRODUCT_OUT/symbols/ | |
705 # directly, if the build fingerprint matches the device. | |
706 if [ -z "$ORG_PULL_LIBS_DIR" -a \ | |
707 "$ANDROID_PRODUCT_OUT" -a \ | |
708 -f "$ANDROID_PRODUCT_OUT/system/build.prop" ]; then | |
709 ANDROID_FINGERPRINT=$(get_build_fingerprint_from \ | |
710 "$ANDROID_PRODUCT_OUT"/system/build.prop) | |
711 log "Android build fingerprint: $ANDROID_FINGERPRINT" | |
712 if [ "$ANDROID_FINGERPRINT" = "$DEVICE_FINGERPRINT" ]; then | |
713 log "Perfect match!" | |
714 PULL_LIBS_DIR=$ANDROID_PRODUCT_OUT/symbols | |
715 HOST_FINGERPRINT=$ANDROID_FINGERPRINT | |
716 if [ "$PULL_LIBS" ]; then | |
717 log "Ignoring --pull-libs since the device and platform build \ | |
718 fingerprints match." | |
719 NO_PULL_LIBS=true | |
720 fi | |
721 fi | |
722 fi | |
723 | |
724 # If neither --pull-libs an --no-pull-libs were specified, check the build | |
725 # fingerprints of the device, and the cached system libraries on the host. | |
726 # | |
727 if [ -z "$NO_PULL_LIBS" -a -z "$PULL_LIBS" ]; then | |
728 if [ ! -f "$PULL_LIBS_DIR/build.prop" ]; then | |
729 log "Auto-config: --pull-libs (no cached libraries)" | |
730 PULL_LIBS=true | |
731 else | |
732 HOST_FINGERPRINT=$(get_build_fingerprint_from "$PULL_LIBS_DIR/build.prop") | |
733 log "Host build fingerprint: $HOST_FINGERPRINT" | |
734 if [ "$HOST_FINGERPRINT" == "$DEVICE_FINGERPRINT" ]; then | |
735 log "Auto-config: --no-pull-libs (fingerprint match)" | |
736 NO_PULL_LIBS=true | |
737 else | |
738 log "Auto-config: --pull-libs (fingerprint mismatch)" | |
739 PULL_LIBS=true | |
740 fi | |
741 fi | |
742 fi | |
743 | |
744 # Extract the system libraries from the device if necessary. | |
745 if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then | |
746 echo "Extracting system libraries into: $PULL_LIBS_DIR" | |
747 fi | |
748 | |
749 mkdir -p "$PULL_LIBS_DIR" | |
750 fail_panic "Can't create --libs-dir directory: $PULL_LIBS_DIR" | |
751 | |
752 # If requested, work for M-x gdb. The gdb indirections make it | |
753 # difficult to pass --annotate=3 to the gdb binary itself. | |
754 GDB_ARGS= | |
755 if [ "$ANNOTATE" ]; then | |
756 GDB_ARGS=$GDB_ARGS" --annotate=$ANNOTATE" | |
757 fi | |
758 | |
759 # Get the PID from the first argument or else find the PID of the | |
760 # browser process. | |
761 if [ -z "$PID" ]; then | |
762 PROCESSNAME=$PACKAGE_NAME | |
763 if [ "$SANDBOXED_INDEX" ]; then | |
764 PROCESSNAME=$PROCESSNAME:sandboxed_process$SANDBOXED_INDEX | |
765 elif [ "$SANDBOXED" ]; then | |
766 PROCESSNAME=$PROCESSNAME:sandboxed_process | |
767 PID=$(adb_shell ps | \ | |
768 awk '$9 ~ /^'$PROCESSNAME'/ { print $2; }' | head -1) | |
769 fi | |
770 if [ -z "$PID" ]; then | |
771 PID=$(adb_shell ps | \ | |
772 awk '$9 == "'$PROCESSNAME'" { print $2; }' | head -1) | |
773 fi | |
774 if [ -z "$PID" ]; then | |
775 if [ "$START" ]; then | |
776 panic "Can't find application process PID, did it crash?" | |
777 else | |
778 panic "Can't find application process PID, are you sure it is \ | |
779 running? Try using --start." | |
780 fi | |
781 fi | |
782 log "Found process PID: $PID" | |
783 elif [ "$SANDBOXED" ]; then | |
784 echo "WARNING: --sandboxed option ignored due to use of --pid." | |
785 fi | |
786 | |
787 # Determine if 'adb shell' runs as root or not. | |
788 # If so, we can launch gdbserver directly, otherwise, we have to | |
789 # use run-as $PACKAGE_NAME ..., which requires the package to be debuggable. | |
790 # | |
791 SHELL_UID=$(adb shell cat /proc/self/status | \ | |
792 awk '$1 == "Uid:" { print $2; }') | |
793 log "Shell UID: $SHELL_UID" | |
794 COMMAND_PREFIX= | |
795 if [ "$SHELL_UID" != 0 -o -n "$NO_ROOT" ]; then | |
796 log "Using run-as $PACKAGE_NAME to run without root." | |
797 COMMAND_PREFIX="run-as $PACKAGE_NAME" | |
798 fi | |
799 | |
800 # Pull device's system libraries that are mapped by our process. | |
801 # Pulling all system libraries is too long, so determine which ones | |
802 # we need by looking at /proc/$PID/maps instead | |
803 if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then | |
804 echo "Extracting system libraries into: $PULL_LIBS_DIR" | |
805 SYSTEM_LIBS=$(adb_shell $COMMAND_PREFIX cat /proc/$PID/maps | \ | |
806 awk '$6 ~ /\/system\/.*\.so$/ { print $6; }' | sort -u) | |
807 for SYSLIB in /system/bin/linker $SYSTEM_LIBS; do | |
808 echo "Pulling from device: $SYSLIB" | |
809 DST_FILE=$PULL_LIBS_DIR$SYSLIB | |
810 DST_DIR=$(dirname "$DST_FILE") | |
811 mkdir -p "$DST_DIR" && adb pull $SYSLIB "$DST_FILE" 2>/dev/null | |
812 fail_panic "Could not pull $SYSLIB from device !?" | |
813 done | |
814 echo "Pulling device build.prop" | |
815 adb pull /system/build.prop $PULL_LIBS_DIR/build.prop | |
816 fail_panic "Could not pull device build.prop !?" | |
817 fi | |
818 | |
819 # Find all the sub-directories of $PULL_LIBS_DIR, up to depth 4 | |
820 # so we can add them to solib-search-path later. | |
821 SOLIB_DIRS=$(find $PULL_LIBS_DIR -mindepth 1 -maxdepth 4 -type d | \ | |
822 grep -v "^$" | tr '\n' ':') | |
823 | |
824 # This is a re-implementation of gdbclient, where we use compatible | |
825 # versions of gdbserver and $GDBNAME to ensure that everything works | |
826 # properly. | |
827 # | |
828 | |
829 # Push gdbserver to the device | |
830 log "Pushing gdbserver to $TARGET_GDBSERVER" | |
831 adb push $GDBSERVER $TARGET_GDBSERVER &>/dev/null | |
832 fail_panic "Could not copy gdbserver to the device!" | |
833 | |
834 PORT=5039 | |
835 HOST_PORT=$PORT | |
836 TARGET_PORT=$PORT | |
837 | |
838 # Pull the app_process binary from the device | |
839 GDBEXEC=app_process | |
840 log "Pulling $GDBEXEC from device" | |
841 adb pull /system/bin/$GDBEXEC "$TMPDIR"/$GDBEXEC &>/dev/null | |
842 fail_panic "Could not retrieve $GDBEXEC from the device!" | |
843 | |
844 # Setup network redirection | |
845 log "Setting network redirection (host:$HOST_PORT -> device:$TARGET_PORT)" | |
846 adb forward tcp:$HOST_PORT tcp:$TARGET_PORT | |
847 fail_panic "Could not setup network redirection from \ | |
848 host:localhost:$HOST_PORT to device:localhost:$TARGET_PORT!" | |
849 | |
850 # Start gdbserver in the background | |
851 # Note that using run-as requires the package to be debuggable. | |
852 # | |
853 # If not, this will fail horribly. The alternative is to run the | |
854 # program as root, which requires of course root privileges. | |
855 # Maybe we should add a --root option to enable this? | |
856 # | |
857 log "Starting gdbserver in the background:" | |
858 GDBSERVER_LOG=$TMPDIR/gdbserver-$TMP_ID.log | |
859 log "adb shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \ | |
860 --attach $PID" | |
861 ("$ADB" shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \ | |
862 --attach $PID > $GDBSERVER_LOG 2>&1) & | |
863 GDBSERVER_PID=$! | |
864 echo "$GDBSERVER_PID" > $GDBSERVER_PIDFILE | |
865 log "background job pid: $GDBSERVER_PID" | |
866 | |
867 # Check that it is still running after a few seconds. If not, this means we | |
868 # could not properly attach to it | |
869 sleep 2 | |
870 log "Job control: $(jobs -l)" | |
871 STATE=$(jobs -l | awk '$2 == "'$GDBSERVER_PID'" { print $3; }') | |
872 if [ "$STATE" != "Running" ]; then | |
873 echo "ERROR: GDBServer could not attach to PID $PID!" | |
874 echo "Failure log (use --verbose for more information):" | |
875 cat $GDBSERVER_LOG | |
876 exit 1 | |
877 fi | |
878 | |
879 # Generate a file containing useful GDB initialization commands | |
880 readonly COMMANDS=$TMPDIR/gdb.init | |
881 log "Generating GDB initialization commands file: $COMMANDS" | |
882 echo -n "" > $COMMANDS | |
883 echo "file $TMPDIR/$GDBEXEC" >> $COMMANDS | |
884 echo "directory $CHROMIUM_SRC" >> $COMMANDS | |
885 echo "set solib-absolute-prefix $PULL_LIBS_DIR" >> $COMMANDS | |
886 echo "set solib-search-path $SOLIB_DIRS:$PULL_LIBS_DIR:$SYMBOL_DIR" \ | |
887 >> $COMMANDS | |
888 echo "echo Attaching and reading symbols, this may take a while.." \ | |
889 >> $COMMANDS | |
890 echo "target remote :$HOST_PORT" >> $COMMANDS | |
891 | |
892 if [ "$GDBINIT" ]; then | |
893 cat "$GDBINIT" >> $COMMANDS | |
894 fi | |
895 | |
896 if [ "$VERBOSE" -gt 0 ]; then | |
897 echo "### START $COMMANDS" | |
898 cat $COMMANDS | |
899 echo "### END $COMMANDS" | |
900 fi | |
901 | |
902 log "Launching gdb client: $GDB $GDBARGS -x $COMMANDS" | |
903 $GDB $GDBARGS -x $COMMANDS && | |
904 rm -f "$GDBSERVER_PIDFILE" | |
905 | |
906 clean_exit $? | |
OLD | NEW |