1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
|
#!/bin/bash
### Run Emacs under GDB or JDB on Android.
## Copyright (C) 2023 Free Software Foundation, Inc.
## This file is part of GNU Emacs.
## GNU Emacs is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
## GNU Emacs is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
## You should have received a copy of the GNU General Public License
## along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
set -m
oldpwd=`pwd`
cd `dirname $0`
devices=`adb devices | grep device | awk -- '/device\y/ { print $1 }' -`
device=
progname=$0
package=org.gnu.emacs
activity=org.gnu.emacs.EmacsActivity
gdb_port=5039
jdb_port=64013
jdb=no
attach_existing=no
gdbserver=
while [ $# -gt 0 ]; do
case "$1" in
## This option specifies the serial number of a device to use.
"--device" )
device="$2"
if [ -z device ]; then
echo "You must specify an argument to --device"
exit 1
fi
shift
;;
"--help" )
echo "Usage: $progname [options] -- [gdb options]"
echo ""
echo " --device DEVICE run Emacs on the specified device"
echo " --port PORT run the GDB server on a specific port"
echo " --jdb-port PORT run the JDB server on a specific port"
echo " --jdb run JDB instead of GDB"
echo " --attach-existing attach to an existing process"
echo " --gdbserver BINARY upload and use the specified gdbserver binary"
echo " --help print this message"
echo ""
echo "Available devices:"
for device in $devices; do
echo " " $device
done
echo ""
exit 0
;;
"--jdb" )
jdb=yes
;;
"--gdbserver" )
shift
gdbserver=$1
;;
"--port" )
shift
gdb_port=$1
;;
"--jdb-port" )
shift
jdb_port=$1
;;
"--attach-existing" )
attach_existing=yes
;;
"--" )
shift
gdbargs=$@
break;
;;
* )
echo "$progname: Unrecognized argument $1"
exit 1
;;
esac
shift
done
if [ -z $devices ]; then
echo "No devices are available."
exit 1
fi
if [ -z $device ]; then
device=$devices
fi
if [ `wc -w <<< "$devices"` -gt 1 ] && [ -z device ]; then
echo "Multiple devices are available. Please pick one using"
echo "--device and try again."
fi
echo "Looking for $package on device $device"
# Find the application data directory
app_data_dir=`adb -s $device shell run-as $package sh -c 'pwd 2> /dev/null'`
if [ -z $app_data_dir ]; then
echo "The data directory for the package $package was not found."
echo "Is it installed?"
fi
echo "Found application data directory at $app_data_dir..."
# Find which PIDs are associated with org.gnu.emacs
package_uid=`adb -s $device shell run-as $package id -u`
if [ -z $package_uid ]; then
echo "Failed to obtain UID of packages named $package"
exit 1
fi
# First, run ps -u $package_uid -o PID,CMD to fetch the list of
# process IDs.
package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD`
# Next, remove lines matching "ps" itself.
package_pids=`awk -- '{
if (!match ($0, /(PID|ps)/))
print $1
}' <<< $package_pids`
if [ "$attach_existing" != "yes" ]; then
# Finally, kill each existing process.
for pid in $package_pids; do
echo "Killing existing process $pid..."
adb -s $device shell run-as $package kill -9 $pid &> /dev/null
done
# Now run the main activity. This must be done as the adb user and
# not as the package user.
echo "Starting activity $activity and attaching debugger"
# Exit if the activity could not be started.
adb -s $device shell am start -D "$package/$activity"
if [ ! $? ]; then
exit 1;
fi
# Now look for processes matching the package again.
package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD`
# Next, remove lines matching "ps" itself.
package_pids=`awk -- '{
if (!match ($0, /(PID|ps)/))
print $1
}' <<< $package_pids`
fi
pid=$package_pids
num_pids=`wc -w <<< "$package_pids"`
if [ $num_pids -gt 1 ]; then
echo "More than one process was started:"
echo ""
adb -s $device shell run-as $package ps -u $package_uid | awk -- '{
if (!match ($0, /ps/))
print $0
}'
echo ""
printf "Which one do you want to attach to? "
read pid
elif [ -z $package_pids ]; then
echo "No processes were found to attach to."
exit 1
fi
# This isn't necessary when attaching gdb to an existing process.
if [ "$jdb" = "yes" ] || [ "$attach_existing" != yes ]; then
# Start JDB to make the wait dialog disappear.
echo "Attaching JDB to unblock the application."
adb -s $device forward --remove-all
adb -s $device forward "tcp:$jdb_port" "jdwp:$pid"
if [ ! $? ]; then
echo "Failed to forward jdwp:$pid to $jdb_port!"
echo "Perhaps you need to specify a different port with --port?"
exit 1;
fi
jdb_command="jdb -connect \
com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port"
if [ $jdb = "yes" ]; then
# Just start JDB and then exit
$jdb_command
exit 1
fi
exec 4<> /tmp/file-descriptor-stamp
# Now run JDB with IO redirected to file descriptor 4 in a subprocess.
$jdb_command <&4 >&4 &
character=
# Next, wait until the prompt is found.
while read -n1 -u 4 character; do
if [ "$character" = ">" ]; then
echo "JDB attached successfully"
break;
fi
done
fi
# See if gdbserver has to be uploaded
if [ -z "$gdbserver" ]; then
gdbserver_bin=/system/bin/gdbserver
else
gdbserver_bin=/data/local/tmp/gdbserver
# Upload the specified gdbserver binary to the device.
adb -s $device push "$gdbserver" "$gdbserver_bin"
adb -s $device shell chmod +x "$gdbserver_bin"
fi
# Now start gdbserver on the device asynchronously.
echo "Attaching gdbserver to $pid on $device..."
exec 5<> /tmp/file-descriptor-stamp
if [ -z "$gdbserver" ]; then
adb -s $device shell run-as $package $gdbserver_bin --once \
"+debug.$package_uid.socket" --attach $pid >&5 &
gdb_socket="localfilesystem:$app_data_dir/debug.$package_uid.socket"
else
# Normally the program cannot access $gdbserver_bin when it is
# placed in /data/local/tmp.
adb -s $device shell $gdbserver_bin --once \
"+/data/local/tmp/debug.$package_uid.socket" \
--attach $pid >&5 &
gdb_socket="localfilesystem:/data/local/tmp/debug.$package_uid.socket"
fi
# Wait until gdbserver successfully runs.
line=
while read -u 5 line; do
case "$line" in
*Attached* )
break;
;;
*error* | *Error* | failed )
echo $line
exit 1
;;
* )
;;
esac
done
if [ "$attach_existing" != "yes" ]; then
# Send EOF to JDB to make it go away. This will also cause
# Android to allow Emacs to continue executing.
echo "Making JDB go away..."
echo "exit" >&4
read -u 4 line
echo "JDB has gone away with $line"
fi
# Forward the gdb server port here.
adb -s $device forward "tcp:$gdb_port" $gdb_socket
if [ ! $? ]; then
echo "Failed to forward $app_data_dir/debug.$package_uid.socket"
echo "to $gdb_port! Perhaps you need to specify a different port"
echo "with --port?"
exit 1;
fi
# Finally, start gdb with any extra arguments needed.
cd "$oldpwd"
gdb --eval-command "" --eval-command "target remote localhost:$gdb_port" $gdbargs
|