aboutsummaryrefslogtreecommitdiffstats
path: root/java/debug.sh
diff options
context:
space:
mode:
Diffstat (limited to 'java/debug.sh')
-rwxr-xr-xjava/debug.sh368
1 files changed, 368 insertions, 0 deletions
diff --git a/java/debug.sh b/java/debug.sh
new file mode 100755
index 00000000000..d6e439bec90
--- /dev/null
+++ b/java/debug.sh
@@ -0,0 +1,368 @@
1#!/bin/bash
2### Run Emacs under GDB or JDB on Android.
3
4## Copyright (C) 2023 Free Software Foundation, Inc.
5
6## This file is part of GNU Emacs.
7
8## GNU Emacs is free software: you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation, either version 3 of the License, or
11## (at your option) any later version.
12
13## GNU Emacs is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16## GNU General Public License for more details.
17
18## You should have received a copy of the GNU General Public License
19## along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
20
21set -m
22oldpwd=`pwd`
23cd `dirname $0`
24
25devices=`adb devices | grep device | awk -- '/device\y/ { print $1 }' -`
26device=
27progname=$0
28package=org.gnu.emacs
29activity=org.gnu.emacs.EmacsActivity
30gdb_port=5039
31jdb_port=64013
32jdb=no
33attach_existing=no
34gdbserver=
35gdb=gdb
36
37while [ $# -gt 0 ]; do
38 case "$1" in
39 ## This option specifies the serial number of a device to use.
40 "--device" )
41 device="$2"
42 if [ -z device ]; then
43 echo "You must specify an argument to --device"
44 exit 1
45 fi
46 shift
47 ;;
48 "--help" )
49 echo "Usage: $progname [options] -- [gdb options]"
50 echo ""
51 echo " --device DEVICE run Emacs on the specified device"
52 echo " --port PORT run the GDB server on a specific port"
53 echo " --jdb-port PORT run the JDB server on a specific port"
54 echo " --jdb run JDB instead of GDB"
55 echo " --gdb use specified GDB binary"
56 echo " --attach-existing attach to an existing process"
57 echo " --gdbserver BINARY upload and use the specified gdbserver binary"
58 echo " --help print this message"
59 echo ""
60 echo "Available devices:"
61 for device in $devices; do
62 echo " " $device
63 done
64 echo ""
65 exit 0
66 ;;
67 "--jdb" )
68 jdb=yes
69 ;;
70 "--gdb" )
71 shift
72 gdb=$1
73 ;;
74 "--gdbserver" )
75 shift
76 gdbserver=$1
77 ;;
78 "--port" )
79 shift
80 gdb_port=$1
81 ;;
82 "--jdb-port" )
83 shift
84 jdb_port=$1
85 ;;
86 "--attach-existing" )
87 attach_existing=yes
88 ;;
89 "--" )
90 shift
91 gdbargs=$@
92 break;
93 ;;
94 * )
95 echo "$progname: Unrecognized argument $1"
96 exit 1
97 ;;
98 esac
99 shift
100done
101
102if [ -z "$devices" ]; then
103 echo "No devices are available."
104 exit 1
105fi
106
107if [ -z $device ]; then
108 device=$devices
109fi
110
111if [ `wc -w <<< "$devices"` -gt 1 ] && [ -z device ]; then
112 echo "Multiple devices are available. Please pick one using"
113 echo "--device and try again."
114fi
115
116echo "Looking for $package on device $device"
117
118# Find the application data directory
119app_data_dir=`adb -s $device shell run-as $package sh -c 'pwd 2> /dev/null'`
120
121if [ -z $app_data_dir ]; then
122 echo "The data directory for the package $package was not found."
123 echo "Is it installed?"
124fi
125
126echo "Found application data directory at" "$app_data_dir"
127
128# Generate an awk script to extract PIDs from Android ps output. It
129# is enough to run `ps' as the package user on newer versions of
130# Android, but that doesn't work on Android 2.3.
131cat << EOF > tmp.awk
132BEGIN {
133 pid = 0;
134 pid_column = 2;
135}
136
137{
138 # Remove any trailing carriage return from the input line.
139 gsub ("\r", "", \$NF)
140
141 # If this is line 1, figure out which column contains the PID.
142 if (NR == 1)
143 {
144 for (n = 1; n <= NF; ++n)
145 {
146 if (\$n == "PID")
147 pid_column=n;
148 }
149 }
150 else if (\$NF == "$package")
151 print \$pid_column
152}
153EOF
154
155# Make sure that file disappears once this script exits.
156trap "rm -f $(pwd)/tmp.awk" 0
157
158# First, run ps to fetch the list of process IDs.
159package_pids=`adb -s $device shell ps`
160
161# Next, extract the list of PIDs currently running.
162package_pids=`awk -f tmp.awk <<< $package_pids`
163
164if [ "$attach_existing" != "yes" ]; then
165 # Finally, kill each existing process.
166 for pid in $package_pids; do
167 echo "Killing existing process $pid..."
168 adb -s $device shell run-as $package kill -9 $pid &> /dev/null
169 done
170
171 # Now run the main activity. This must be done as the adb user and
172 # not as the package user.
173 echo "Starting activity $activity and attaching debugger"
174
175 # Exit if the activity could not be started.
176 adb -s $device shell am start -D -n "$package/$activity"
177 if [ ! $? ]; then
178 exit 1;
179 fi
180
181 # Sleep for a bit. Otherwise, the process may not have started
182 # yet.
183 sleep 1
184
185 # Now look for processes matching the package again.
186 package_pids=`adb -s $device shell ps`
187
188 # Next, remove lines matching "ps" itself.
189 package_pids=`awk -f tmp.awk <<< $package_pids`
190fi
191
192pid=$package_pids
193num_pids=`wc -w <<< "$package_pids"`
194
195if [ $num_pids -gt 1 ]; then
196 echo "More than one process was started:"
197 echo ""
198 adb -s $device shell run-as $package ps | awk -- "{
199 if (!match (\$0, /ps/) && match (\$0, /$package/))
200 print \$0
201 }"
202 echo ""
203 printf "Which one do you want to attach to? "
204 read pid
205elif [ -z $package_pids ]; then
206 echo "No processes were found to attach to."
207 exit 1
208fi
209
210# If either --jdb was specified or debug.sh is not connecting to an
211# existing process, then store a suitable JDB invocation in
212# jdb_command. GDB will then run JDB to unblock the application from
213# the wait dialog after startup.
214
215if [ "$jdb" = "yes" ] || [ "$attach_existing" != yes ]; then
216 adb -s $device forward --remove-all
217 adb -s $device forward "tcp:$jdb_port" "jdwp:$pid"
218
219 if [ ! $? ]; then
220 echo "Failed to forward jdwp:$pid to $jdb_port!"
221 echo "Perhaps you need to specify a different port with --port?"
222 exit 1;
223 fi
224
225 jdb_command="jdb -connect \
226 com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port"
227
228 if [ $jdb = "yes" ]; then
229 # Just start JDB and then exit
230 $jdb_command
231 exit 1
232 fi
233fi
234
235if [ -n "$jdb_command" ]; then
236 echo "Starting JDB to unblock application."
237
238 # Start JDB to unblock the application.
239 coproc JDB { $jdb_command; }
240
241 # Tell JDB to first suspend all threads.
242 echo "suspend" >&${JDB[1]}
243
244 # Tell JDB to print a magic string once the program is
245 # initialized.
246 echo "print \"__verify_jdb_has_started__\"" >&${JDB[1]}
247
248 # Now wait for JDB to give the string back.
249 line=
250 while :; do
251 read -u ${JDB[0]} line
252 if [ ! $? ]; then
253 echo "Failed to read JDB output"
254 exit 1
255 fi
256
257 case "$line" in
258 *__verify_jdb_has_started__*)
259 # Android only polls for a Java debugger every 200ms, so
260 # the debugger must be connected for at least that long.
261 echo "Pausing 1 second for the program to continue."
262 sleep 1
263 break
264 ;;
265 esac
266 done
267
268 # Note that JDB does not exit until GDB is fully attached!
269fi
270
271# See if gdbserver has to be uploaded
272gdbserver_cmd=
273is_root=
274if [ -z "$gdbserver" ]; then
275 gdbserver_bin=/system/bin/gdbserver64
276else
277 gdbserver_bin=/data/local/tmp/gdbserver
278 gdbserver_cat="cat $gdbserver_bin | run-as $package sh -c \
279 \"tee gdbserver > /dev/null\""
280
281 # Upload the specified gdbserver binary to the device.
282 adb -s $device push "$gdbserver" "$gdbserver_bin"
283
284 if (adb -s $device shell ls /system/bin | grep -G tee); then
285 # Copy it to the user directory.
286 adb -s $device shell "$gdbserver_cat"
287 adb -s $device shell "run-as $package chmod 777 gdbserver"
288 gdbserver_cmd="./gdbserver"
289 else
290 # Hopefully this is an old version of Android which allows
291 # execution from /data/local/tmp. Its `chmod' doesn't support
292 # `+x' either.
293 adb -s $device shell "chmod 777 $gdbserver_bin"
294 gdbserver_cmd="$gdbserver_bin"
295
296 # If the user is root, then there is no need to open any kind
297 # of TCP socket.
298 if (adb -s $device shell id | grep -G root); then
299 gdbserver=
300 is_root=yes
301 fi
302 fi
303fi
304
305# Now start gdbserver on the device asynchronously.
306
307echo "Attaching gdbserver to $pid on $device..."
308exec 5<> /tmp/file-descriptor-stamp
309rm -f /tmp/file-descriptor-stamp
310
311if [ -z "$gdbserver" ]; then
312 if [ "$is_root" = "yes" ]; then
313 adb -s $device shell $gdbserver_bin --multi \
314 "0.0.0.0:7564" --attach $pid >&5 &
315 gdb_socket="tcp:7564"
316 else
317 adb -s $device shell $gdbserver_bin --multi \
318 "0.0.0.0:7564" --attach $pid >&5 &
319 gdb_socket="tcp:7564"
320 fi
321else
322 # Normally the program cannot access $gdbserver_bin when it is
323 # placed in /data/local/tmp.
324 adb -s $device shell run-as $package $gdbserver_cmd --multi \
325 "+debug.$package.socket" --attach $pid >&5 &
326 gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket"
327fi
328
329# In order to allow adb to forward to the gdbserver socket, make the
330# app data directory a+x.
331adb -s $device shell run-as $package chmod a+x $app_data_dir
332
333# Wait until gdbserver successfully runs.
334line=
335while read -u 5 line; do
336 case "$line" in
337 *Attached* )
338 break;
339 ;;
340 *error* | *Error* | failed )
341 echo "GDB error:" $line
342 exit 1
343 ;;
344 * )
345 ;;
346 esac
347done
348
349# Now that GDB is attached, tell the Java debugger to resume execution
350# and then exit.
351
352if [ -n "$jdb_command" ]; then
353 echo "resume" >&${JDB[1]}
354 echo "exit" >&${JDB[1]}
355fi
356
357# Forward the gdb server port here.
358adb -s $device forward "tcp:$gdb_port" $gdb_socket
359if [ ! $? ]; then
360 echo "Failed to forward $app_data_dir/debug.$package.socket"
361 echo "to $gdb_port! Perhaps you need to specify a different port"
362 echo "with --port?"
363 exit 1;
364fi
365
366# Finally, start gdb with any extra arguments needed.
367cd "$oldpwd"
368$gdb --eval-command "target remote localhost:$gdb_port" $gdbargs