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