xref: /aosp_15_r20/art/tools/dt_fds_forward.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*795d594fSAndroid Build Coastguard Worker#
3*795d594fSAndroid Build Coastguard Worker# Copyright 2017, The Android Open Source Project
4*795d594fSAndroid Build Coastguard Worker#
5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*795d594fSAndroid Build Coastguard Worker#
9*795d594fSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*795d594fSAndroid Build Coastguard Worker#
11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*795d594fSAndroid Build Coastguard Worker# limitations under the License.
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker"""
18*795d594fSAndroid Build Coastguard WorkerA python program that simulates the plugin side of the dt_fd_forward transport for testing.
19*795d594fSAndroid Build Coastguard Worker
20*795d594fSAndroid Build Coastguard WorkerThis program will invoke a given java language runtime program and send down debugging arguments
21*795d594fSAndroid Build Coastguard Workerthat cause it to use the dt_fd_forward transport. This will then create a normal server-port that
22*795d594fSAndroid Build Coastguard Workerdebuggers can attach to.
23*795d594fSAndroid Build Coastguard Worker"""
24*795d594fSAndroid Build Coastguard Worker
25*795d594fSAndroid Build Coastguard Workerimport argparse
26*795d594fSAndroid Build Coastguard Workerimport array
27*795d594fSAndroid Build Coastguard Workerfrom multiprocessing import Process
28*795d594fSAndroid Build Coastguard Workerimport contextlib
29*795d594fSAndroid Build Coastguard Workerimport ctypes
30*795d594fSAndroid Build Coastguard Workerimport os
31*795d594fSAndroid Build Coastguard Workerimport select
32*795d594fSAndroid Build Coastguard Workerimport socket
33*795d594fSAndroid Build Coastguard Workerimport subprocess
34*795d594fSAndroid Build Coastguard Workerimport sys
35*795d594fSAndroid Build Coastguard Workerimport time
36*795d594fSAndroid Build Coastguard Worker
37*795d594fSAndroid Build Coastguard WorkerNEED_HANDSHAKE_MESSAGE = b"HANDSHAKE:REQD\x00"
38*795d594fSAndroid Build Coastguard WorkerLISTEN_START_MESSAGE   = b"dt_fd_forward:START-LISTEN\x00"
39*795d594fSAndroid Build Coastguard WorkerLISTEN_END_MESSAGE     = b"dt_fd_forward:END-LISTEN\x00"
40*795d594fSAndroid Build Coastguard WorkerACCEPTED_MESSAGE       = b"dt_fd_forward:ACCEPTED\x00"
41*795d594fSAndroid Build Coastguard WorkerHANDSHAKEN_MESSAGE     = b"dt_fd_forward:HANDSHAKE-COMPLETE\x00"
42*795d594fSAndroid Build Coastguard WorkerCLOSE_MESSAGE          = b"dt_fd_forward:CLOSING\x00"
43*795d594fSAndroid Build Coastguard Worker
44*795d594fSAndroid Build Coastguard Workerlibc = ctypes.cdll.LoadLibrary("libc.so.6")
45*795d594fSAndroid Build Coastguard Workerdef eventfd(init_val, flags):
46*795d594fSAndroid Build Coastguard Worker  """
47*795d594fSAndroid Build Coastguard Worker  Creates an eventfd. See 'man 2 eventfd' for more information.
48*795d594fSAndroid Build Coastguard Worker  """
49*795d594fSAndroid Build Coastguard Worker  return libc.eventfd(init_val, flags)
50*795d594fSAndroid Build Coastguard Worker
51*795d594fSAndroid Build Coastguard Worker@contextlib.contextmanager
52*795d594fSAndroid Build Coastguard Workerdef make_eventfd(init):
53*795d594fSAndroid Build Coastguard Worker  """
54*795d594fSAndroid Build Coastguard Worker  Creates an eventfd with given initial value that is closed after the manager finishes.
55*795d594fSAndroid Build Coastguard Worker  """
56*795d594fSAndroid Build Coastguard Worker  fd = eventfd(init, 0)
57*795d594fSAndroid Build Coastguard Worker  yield fd
58*795d594fSAndroid Build Coastguard Worker  os.close(fd)
59*795d594fSAndroid Build Coastguard Worker
60*795d594fSAndroid Build Coastguard Worker@contextlib.contextmanager
61*795d594fSAndroid Build Coastguard Workerdef make_sockets():
62*795d594fSAndroid Build Coastguard Worker  """
63*795d594fSAndroid Build Coastguard Worker  Make a (remote,local) socket pair. The remote socket is inheritable by forked processes. They are
64*795d594fSAndroid Build Coastguard Worker  both linked together.
65*795d594fSAndroid Build Coastguard Worker  """
66*795d594fSAndroid Build Coastguard Worker  (rfd, lfd) = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET)
67*795d594fSAndroid Build Coastguard Worker  yield (rfd, lfd)
68*795d594fSAndroid Build Coastguard Worker  rfd.close()
69*795d594fSAndroid Build Coastguard Worker  lfd.close()
70*795d594fSAndroid Build Coastguard Worker
71*795d594fSAndroid Build Coastguard Workerdef send_fds(sock, remote_read, remote_write, remote_event):
72*795d594fSAndroid Build Coastguard Worker  """
73*795d594fSAndroid Build Coastguard Worker  Send the three fds over the given socket.
74*795d594fSAndroid Build Coastguard Worker  """
75*795d594fSAndroid Build Coastguard Worker  sock.sendmsg([NEED_HANDSHAKE_MESSAGE],  # We want the transport to handle the handshake.
76*795d594fSAndroid Build Coastguard Worker               [(socket.SOL_SOCKET,  # Send over socket.
77*795d594fSAndroid Build Coastguard Worker                 socket.SCM_RIGHTS,  # Payload is file-descriptor array
78*795d594fSAndroid Build Coastguard Worker                 array.array('i', [remote_read, remote_write, remote_event]))])
79*795d594fSAndroid Build Coastguard Worker
80*795d594fSAndroid Build Coastguard Worker
81*795d594fSAndroid Build Coastguard Workerdef HandleSockets(host, port, local_sock, finish_event):
82*795d594fSAndroid Build Coastguard Worker  """
83*795d594fSAndroid Build Coastguard Worker  Handle the IO between the network and the runtime.
84*795d594fSAndroid Build Coastguard Worker
85*795d594fSAndroid Build Coastguard Worker  This is similar to what we will do with the plugin that controls the jdwp connection.
86*795d594fSAndroid Build Coastguard Worker
87*795d594fSAndroid Build Coastguard Worker  The main difference is it will keep around the connection and event-fd in order to let it send
88*795d594fSAndroid Build Coastguard Worker  ddms packets directly.
89*795d594fSAndroid Build Coastguard Worker  """
90*795d594fSAndroid Build Coastguard Worker  listening = False
91*795d594fSAndroid Build Coastguard Worker  with socket.socket() as sock:
92*795d594fSAndroid Build Coastguard Worker    sock.bind((host, port))
93*795d594fSAndroid Build Coastguard Worker    sock.listen()
94*795d594fSAndroid Build Coastguard Worker    while True:
95*795d594fSAndroid Build Coastguard Worker      sources = [local_sock, finish_event, sock]
96*795d594fSAndroid Build Coastguard Worker      print("Starting select on " + str(sources))
97*795d594fSAndroid Build Coastguard Worker      (rf, _, _) = select.select(sources, [], [])
98*795d594fSAndroid Build Coastguard Worker      if local_sock in rf:
99*795d594fSAndroid Build Coastguard Worker        buf = local_sock.recv(1024)
100*795d594fSAndroid Build Coastguard Worker        print("Local_sock has data: " + str(buf))
101*795d594fSAndroid Build Coastguard Worker        if buf == LISTEN_START_MESSAGE:
102*795d594fSAndroid Build Coastguard Worker          print("listening on " + str(sock))
103*795d594fSAndroid Build Coastguard Worker          listening = True
104*795d594fSAndroid Build Coastguard Worker        elif buf == LISTEN_END_MESSAGE:
105*795d594fSAndroid Build Coastguard Worker          print("End listening")
106*795d594fSAndroid Build Coastguard Worker          listening = False
107*795d594fSAndroid Build Coastguard Worker        elif buf == ACCEPTED_MESSAGE:
108*795d594fSAndroid Build Coastguard Worker          print("Fds were accepted.")
109*795d594fSAndroid Build Coastguard Worker        elif buf == HANDSHAKEN_MESSAGE:
110*795d594fSAndroid Build Coastguard Worker          print("Handshake completed.")
111*795d594fSAndroid Build Coastguard Worker        elif buf == CLOSE_MESSAGE:
112*795d594fSAndroid Build Coastguard Worker          # TODO Dup the fds and send a fake DDMS message like the actual plugin would.
113*795d594fSAndroid Build Coastguard Worker          print("Fds were closed")
114*795d594fSAndroid Build Coastguard Worker        else:
115*795d594fSAndroid Build Coastguard Worker          print("Unknown data received from socket " + str(buf))
116*795d594fSAndroid Build Coastguard Worker          return
117*795d594fSAndroid Build Coastguard Worker      elif sock in rf:
118*795d594fSAndroid Build Coastguard Worker        (conn, addr) = sock.accept()
119*795d594fSAndroid Build Coastguard Worker        with conn:
120*795d594fSAndroid Build Coastguard Worker          print("connection accepted from " + str(addr))
121*795d594fSAndroid Build Coastguard Worker          if listening:
122*795d594fSAndroid Build Coastguard Worker            with make_eventfd(1) as efd:
123*795d594fSAndroid Build Coastguard Worker              print("sending fds ({}, {}, {}) to target.".format(conn.fileno(), conn.fileno(), efd))
124*795d594fSAndroid Build Coastguard Worker              send_fds(local_sock, conn.fileno(), conn.fileno(), efd)
125*795d594fSAndroid Build Coastguard Worker          else:
126*795d594fSAndroid Build Coastguard Worker            print("Closing fds since we cannot accept them.")
127*795d594fSAndroid Build Coastguard Worker      if finish_event in rf:
128*795d594fSAndroid Build Coastguard Worker        print("woke up from finish_event")
129*795d594fSAndroid Build Coastguard Worker        return
130*795d594fSAndroid Build Coastguard Worker
131*795d594fSAndroid Build Coastguard Workerdef StartChildProcess(cmd_pre, cmd_post, jdwp_lib, jdwp_ops, remote_sock, can_be_runtest):
132*795d594fSAndroid Build Coastguard Worker  """
133*795d594fSAndroid Build Coastguard Worker  Open the child java-language runtime process.
134*795d594fSAndroid Build Coastguard Worker  """
135*795d594fSAndroid Build Coastguard Worker  full_cmd = list(cmd_pre)
136*795d594fSAndroid Build Coastguard Worker  os.set_inheritable(remote_sock.fileno(), True)
137*795d594fSAndroid Build Coastguard Worker  jdwp_arg = jdwp_lib + "=" + \
138*795d594fSAndroid Build Coastguard Worker             jdwp_ops + "transport=dt_fd_forward,address=" + str(remote_sock.fileno())
139*795d594fSAndroid Build Coastguard Worker  if can_be_runtest and cmd_pre[0].endswith("run-test"):
140*795d594fSAndroid Build Coastguard Worker    print("Assuming run-test. Pass --no-run-test if this isn't true")
141*795d594fSAndroid Build Coastguard Worker    full_cmd += ["--with-agent", jdwp_arg]
142*795d594fSAndroid Build Coastguard Worker  else:
143*795d594fSAndroid Build Coastguard Worker    full_cmd.append("-agentpath:" + jdwp_arg)
144*795d594fSAndroid Build Coastguard Worker  full_cmd += cmd_post
145*795d594fSAndroid Build Coastguard Worker  print("Running " + str(full_cmd))
146*795d594fSAndroid Build Coastguard Worker  # Start the actual process with the fd being passed down.
147*795d594fSAndroid Build Coastguard Worker  proc = subprocess.Popen(full_cmd, close_fds=False)
148*795d594fSAndroid Build Coastguard Worker  # Get rid of the extra socket.
149*795d594fSAndroid Build Coastguard Worker  remote_sock.close()
150*795d594fSAndroid Build Coastguard Worker  proc.wait()
151*795d594fSAndroid Build Coastguard Worker
152*795d594fSAndroid Build Coastguard Workerdef main():
153*795d594fSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(description="""
154*795d594fSAndroid Build Coastguard Worker                                   Runs a socket that forwards to dt_fds.
155*795d594fSAndroid Build Coastguard Worker
156*795d594fSAndroid Build Coastguard Worker                                   Pass '--' to start passing in the program we will pass the debug
157*795d594fSAndroid Build Coastguard Worker                                   options down to.
158*795d594fSAndroid Build Coastguard Worker                                   """)
159*795d594fSAndroid Build Coastguard Worker  parser.add_argument("--host", type=str, default="localhost",
160*795d594fSAndroid Build Coastguard Worker                      help="Host we will listen for traffic on. Defaults to 'localhost'.")
161*795d594fSAndroid Build Coastguard Worker  parser.add_argument("--debug-lib", type=str, default="libjdwp.so",
162*795d594fSAndroid Build Coastguard Worker                      help="jdwp library we pass to -agentpath:. Default is 'libjdwp.so'")
163*795d594fSAndroid Build Coastguard Worker  parser.add_argument("--debug-options", type=str, default="server=y,suspend=y,",
164*795d594fSAndroid Build Coastguard Worker                      help="non-address options we pass to jdwp agent, default is " +
165*795d594fSAndroid Build Coastguard Worker                           "'server=y,suspend=y,'")
166*795d594fSAndroid Build Coastguard Worker  parser.add_argument("--port", type=int, default=12345,
167*795d594fSAndroid Build Coastguard Worker                      help="port we will expose the traffic on. Defaults to 12345.")
168*795d594fSAndroid Build Coastguard Worker  parser.add_argument("--no-run-test", default=False, action="store_true",
169*795d594fSAndroid Build Coastguard Worker                      help="don't pass in arguments for run-test even if it looks like that is " +
170*795d594fSAndroid Build Coastguard Worker                           "the program")
171*795d594fSAndroid Build Coastguard Worker  parser.add_argument("--pre-end", type=int, default=1,
172*795d594fSAndroid Build Coastguard Worker                      help="number of 'rest' arguments to put before passing in the debug options")
173*795d594fSAndroid Build Coastguard Worker  end_idx = 0 if '--' not in sys.argv else sys.argv.index('--')
174*795d594fSAndroid Build Coastguard Worker  if end_idx == 0 and ('--help' in sys.argv or '-h' in sys.argv):
175*795d594fSAndroid Build Coastguard Worker    parser.print_help()
176*795d594fSAndroid Build Coastguard Worker    return
177*795d594fSAndroid Build Coastguard Worker  args = parser.parse_args(sys.argv[:end_idx][1:])
178*795d594fSAndroid Build Coastguard Worker  rest = sys.argv[1 + end_idx:]
179*795d594fSAndroid Build Coastguard Worker
180*795d594fSAndroid Build Coastguard Worker  with make_eventfd(0) as wakeup_event:
181*795d594fSAndroid Build Coastguard Worker    with make_sockets() as (remote_sock, local_sock):
182*795d594fSAndroid Build Coastguard Worker      invoker = Process(target=StartChildProcess,
183*795d594fSAndroid Build Coastguard Worker                        args=(rest[:args.pre_end],
184*795d594fSAndroid Build Coastguard Worker                              rest[args.pre_end:],
185*795d594fSAndroid Build Coastguard Worker                              args.debug_lib,
186*795d594fSAndroid Build Coastguard Worker                              args.debug_options,
187*795d594fSAndroid Build Coastguard Worker                              remote_sock,
188*795d594fSAndroid Build Coastguard Worker                              not args.no_run_test))
189*795d594fSAndroid Build Coastguard Worker      socket_handler = Process(target=HandleSockets,
190*795d594fSAndroid Build Coastguard Worker                               args=(args.host, args.port, local_sock, wakeup_event))
191*795d594fSAndroid Build Coastguard Worker      socket_handler.start()
192*795d594fSAndroid Build Coastguard Worker      invoker.start()
193*795d594fSAndroid Build Coastguard Worker    invoker.join()
194*795d594fSAndroid Build Coastguard Worker    # Write any 64 bit value to the wakeup_event to make sure that the socket handler will wake
195*795d594fSAndroid Build Coastguard Worker    # up and exit.
196*795d594fSAndroid Build Coastguard Worker    os.write(wakeup_event, b'\x00\x00\x00\x00\x00\x00\x01\x00')
197*795d594fSAndroid Build Coastguard Worker    socket_handler.join()
198*795d594fSAndroid Build Coastguard Worker
199*795d594fSAndroid Build Coastguard Workerif __name__ == '__main__':
200*795d594fSAndroid Build Coastguard Worker  main()
201