Number of fixes:
- allow to pass command line arguments from (lldb) prompt; - make lldb exit as opposite to relying on kill (which is problematics because it messes up tty settings); - handle event timeout in autoexit_command to avoid endless loop; - use dynamic TCP port and listen on localhost (no need to expose the port to Internet); - close accept socket once connection is established; (fixes #112)
This commit is contained in:
		 Andy Polyakov
					Andy Polyakov
				
			
				
					committed by
					
						 Shazron Abdullah
						Shazron Abdullah
					
				
			
			
				
	
			
			
			 Shazron Abdullah
						Shazron Abdullah
					
				
			
						parent
						
							e882932053
						
					
				
				
					commit
					54818aae5c
				
			
							
								
								
									
										98
									
								
								ios-deploy.c
									
									
									
									
									
								
							
							
						
						
									
										98
									
								
								ios-deploy.c
									
									
									
									
									
								
							| @@ -61,6 +61,7 @@ const char* lldb_prep_noninteractive_cmds = "\ | |||||||
|  */ |  */ | ||||||
| #define LLDB_FRUITSTRAP_MODULE CFSTR("\ | #define LLDB_FRUITSTRAP_MODULE CFSTR("\ | ||||||
| import lldb\n\ | import lldb\n\ | ||||||
|  | import os\n\ | ||||||
| import sys\n\ | import sys\n\ | ||||||
| import shlex\n\ | import shlex\n\ | ||||||
| \n\ | \n\ | ||||||
| @@ -90,13 +91,14 @@ def connect_command(debugger, command, result, internal_dict):\n\ | |||||||
| \n\ | \n\ | ||||||
| def run_command(debugger, command, result, internal_dict):\n\ | def run_command(debugger, command, result, internal_dict):\n\ | ||||||
|     device_app = internal_dict['fruitstrap_device_app']\n\ |     device_app = internal_dict['fruitstrap_device_app']\n\ | ||||||
|  |     args = command.split('--',1)\n\ | ||||||
|     error = lldb.SBError()\n\ |     error = lldb.SBError()\n\ | ||||||
|     lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))\n\ |     lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))\n\ | ||||||
|     lldb.target.Launch(lldb.SBLaunchInfo(shlex.split('{args}')), error)\n\ |     lldb.target.Launch(lldb.SBLaunchInfo(shlex.split(args[1] and args[1] or '{args}')), error)\n\ | ||||||
|     lockedstr = ': Locked'\n\ |     lockedstr = ': Locked'\n\ | ||||||
|     if lockedstr in str(error):\n\ |     if lockedstr in str(error):\n\ | ||||||
|        print('\\nDevice Locked\\n')\n\ |        print('\\nDevice Locked\\n')\n\ | ||||||
|        sys.exit(254)\n\ |        os._exit(254)\n\ | ||||||
|     else:\n\ |     else:\n\ | ||||||
|        print(str(error))\n\ |        print(str(error))\n\ | ||||||
| \n\ | \n\ | ||||||
| @@ -106,12 +108,16 @@ def safequit_command(debugger, command, result, internal_dict):\n\ | |||||||
|     listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)\n\ |     listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)\n\ | ||||||
|     event = lldb.SBEvent()\n\ |     event = lldb.SBEvent()\n\ | ||||||
|     while True:\n\ |     while True:\n\ | ||||||
|         if listener.WaitForEvent(1, event):\n\ |         if listener.WaitForEvent(1, event) and lldb.SBProcess.EventIsProcessEvent(event):\n\ | ||||||
|             state = process.GetStateFromEvent(event)\n\ |             state = lldb.SBProcess.GetStateFromEvent(event)\n\ | ||||||
|         else:\n\ |         else:\n\ | ||||||
|             state = lldb.eStateInvalid\n\ |             state = process.GetState()\n\ | ||||||
|  | \n\ | ||||||
|  |         if state == lldb.eStateRunning:\n\ | ||||||
|             process.Detach()\n\ |             process.Detach()\n\ | ||||||
|         sys.exit(0)\n\ |             os._exit(0)\n\ | ||||||
|  |         elif state > lldb.eStateRunning:\n\ | ||||||
|  |             os._exit(state)\n\ | ||||||
| \n\ | \n\ | ||||||
| def autoexit_command(debugger, command, result, internal_dict):\n\ | def autoexit_command(debugger, command, result, internal_dict):\n\ | ||||||
|     process = lldb.target.process\n\ |     process = lldb.target.process\n\ | ||||||
| @@ -119,10 +125,16 @@ def autoexit_command(debugger, command, result, internal_dict):\n\ | |||||||
|     listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)\n\ |     listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)\n\ | ||||||
|     event = lldb.SBEvent()\n\ |     event = lldb.SBEvent()\n\ | ||||||
|     while True:\n\ |     while True:\n\ | ||||||
|         if listener.WaitForEvent(1, event):\n\ |         if listener.WaitForEvent(1, event) and lldb.SBProcess.EventIsProcessEvent(event):\n\ | ||||||
|             state = process.GetStateFromEvent(event)\n\ |             state = lldb.SBProcess.GetStateFromEvent(event)\n\ | ||||||
|         else:\n\ |         else:\n\ | ||||||
|             state = lldb.eStateInvalid\n\ |             state = process.GetState()\n\ | ||||||
|  | \n\ | ||||||
|  |         if state == lldb.eStateExited:\n\ | ||||||
|  |             os._exit(process.GetExitStatus())\n\ | ||||||
|  |         elif state == lldb.eStateStopped:\n\ | ||||||
|  |             debugger.HandleCommand('bt')\n\ | ||||||
|  |             os._exit({exitcode_app_crash})\n\ | ||||||
| \n\ | \n\ | ||||||
|         stdout = process.GetSTDOUT(1024)\n\ |         stdout = process.GetSTDOUT(1024)\n\ | ||||||
|         while stdout:\n\ |         while stdout:\n\ | ||||||
| @@ -133,14 +145,6 @@ def autoexit_command(debugger, command, result, internal_dict):\n\ | |||||||
|         while stderr:\n\ |         while stderr:\n\ | ||||||
|             sys.stdout.write(stderr)\n\ |             sys.stdout.write(stderr)\n\ | ||||||
|             stderr = process.GetSTDERR(1024)\n\ |             stderr = process.GetSTDERR(1024)\n\ | ||||||
| \n\ |  | ||||||
|         if lldb.SBProcess.EventIsProcessEvent(event):\n\ |  | ||||||
|             if state == lldb.eStateExited:\n\ |  | ||||||
|                 sys.exit(process.GetExitStatus())\n\ |  | ||||||
|             if state == lldb.eStateStopped:\n\ |  | ||||||
|                 debugger.HandleCommand('frame select')\n\ |  | ||||||
|                 debugger.HandleCommand('bt')\n\ |  | ||||||
|                 sys.exit({exitcode_app_crash})\n\ |  | ||||||
| ") | ") | ||||||
|  |  | ||||||
| typedef struct am_device * AMDeviceRef; | typedef struct am_device * AMDeviceRef; | ||||||
| @@ -164,7 +168,7 @@ char *device_id = NULL; | |||||||
| char *args = NULL; | char *args = NULL; | ||||||
| char *list_root = NULL; | char *list_root = NULL; | ||||||
| int timeout = 0; | int timeout = 0; | ||||||
| int port = 12345; | int port = 0;	// 0 means "dynamically assigned" | ||||||
| CFStringRef last_path = NULL; | CFStringRef last_path = NULL; | ||||||
| service_conn_t gdbfd; | service_conn_t gdbfd; | ||||||
| pid_t parent = 0; | pid_t parent = 0; | ||||||
| @@ -822,22 +826,9 @@ server_callback (CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef add | |||||||
|     int res; |     int res; | ||||||
|  |  | ||||||
|     if (CFDataGetLength (data) == 0) { |     if (CFDataGetLength (data) == 0) { | ||||||
|         // FIXME: Close the socket |         // close the socket on which we've got end-of-file, the server_socket. | ||||||
|         //shutdown (CFSocketGetNative (lldb_socket), SHUT_RDWR); |         CFSocketInvalidate(s); | ||||||
|         //close (CFSocketGetNative (lldb_socket)); |         CFRelease(s); | ||||||
|         CFSocketInvalidate(lldb_socket); |  | ||||||
|         CFSocketInvalidate(server_socket); |  | ||||||
|         int mypid = getpid(); |  | ||||||
|         assert((child != 0) && (child != mypid)); //child should not be here |  | ||||||
|         if ((parent != 0) && (parent == mypid) && (child != 0)) |  | ||||||
|         { |  | ||||||
|             if (verbose) |  | ||||||
|             { |  | ||||||
|                 printf("Got an empty packet hence killing child (%d) tree\n", child); |  | ||||||
|             } |  | ||||||
|             kill_ptree(child, SIGHUP); |  | ||||||
|         } |  | ||||||
|         exit(exitcode_error); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     res = write (CFSocketGetNative (lldb_socket), CFDataGetBytePtr (data), CFDataGetLength (data)); |     res = write (CFSocketGetNative (lldb_socket), CFDataGetBytePtr (data), CFDataGetLength (data)); | ||||||
| @@ -847,8 +838,12 @@ void lldb_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef a | |||||||
| { | { | ||||||
|     //printf ("lldb: %s\n", CFDataGetBytePtr (data)); |     //printf ("lldb: %s\n", CFDataGetBytePtr (data)); | ||||||
|  |  | ||||||
|     if (CFDataGetLength (data) == 0) |     if (CFDataGetLength (data) == 0) { | ||||||
|  |         // close the socket on which we've got end-of-file, the lldb_socket. | ||||||
|  |         CFSocketInvalidate(s); | ||||||
|  |         CFRelease(s); | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|     write (gdbfd, CFDataGetBytePtr (data), CFDataGetLength (data)); |     write (gdbfd, CFDataGetBytePtr (data), CFDataGetLength (data)); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -859,21 +854,20 @@ void fdvendor_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataR | |||||||
|     //PRINT ("callback!\n"); |     //PRINT ("callback!\n"); | ||||||
|  |  | ||||||
|     lldb_socket  = CFSocketCreateWithNative(NULL, socket, kCFSocketDataCallBack, &lldb_callback, NULL); |     lldb_socket  = CFSocketCreateWithNative(NULL, socket, kCFSocketDataCallBack, &lldb_callback, NULL); | ||||||
|  |     int flag = 1; | ||||||
|  |     int res = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag)); | ||||||
|  |     assert(res == 0); | ||||||
|     CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, lldb_socket, 0), kCFRunLoopCommonModes); |     CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, lldb_socket, 0), kCFRunLoopCommonModes); | ||||||
|  |  | ||||||
|  |     CFSocketInvalidate(s); | ||||||
|  |     CFRelease(s); | ||||||
| } | } | ||||||
|  |  | ||||||
| void start_remote_debug_server(AMDeviceRef device) { | void start_remote_debug_server(AMDeviceRef device) { | ||||||
|     char buf [256]; |  | ||||||
|     int res, err, i; |  | ||||||
|     char msg [256]; |  | ||||||
|     int chsum, len; |  | ||||||
|     struct stat s; |  | ||||||
|     socklen_t buflen; |  | ||||||
|     struct sockaddr name; |  | ||||||
|     int namelen; |  | ||||||
|  |  | ||||||
|     assert(AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL) == 0); |     int res = AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL); | ||||||
|     assert (gdbfd); |     assert(res == 0); | ||||||
|  |     assert(gdbfd > 0); | ||||||
|  |  | ||||||
|     /* |     /* | ||||||
|      * The debugserver connection is through a fd handle, while lldb requires a host/port to connect, so create an intermediate |      * The debugserver connection is through a fd handle, while lldb requires a host/port to connect, so create an intermediate | ||||||
| @@ -887,20 +881,24 @@ void start_remote_debug_server(AMDeviceRef device) { | |||||||
|     addr4.sin_len = sizeof(addr4); |     addr4.sin_len = sizeof(addr4); | ||||||
|     addr4.sin_family = AF_INET; |     addr4.sin_family = AF_INET; | ||||||
|     addr4.sin_port = htons(port); |     addr4.sin_port = htons(port); | ||||||
|     addr4.sin_addr.s_addr = htonl(INADDR_ANY); |     addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | ||||||
|  |  | ||||||
|     CFSocketRef fdvendor = CFSocketCreate(NULL, PF_INET, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, NULL); |     CFSocketRef fdvendor = CFSocketCreate(NULL, PF_INET, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, NULL); | ||||||
|  |  | ||||||
|  |     if (port) { | ||||||
|         int yes = 1; |         int yes = 1; | ||||||
|         setsockopt(CFSocketGetNative(fdvendor), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); |         setsockopt(CFSocketGetNative(fdvendor), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); | ||||||
|     int flag = 1; |     } | ||||||
|     res = setsockopt(CFSocketGetNative(fdvendor), IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); |  | ||||||
|     assert (res == 0); |  | ||||||
|  |  | ||||||
|     CFDataRef address_data = CFDataCreate(NULL, (const UInt8 *)&addr4, sizeof(addr4)); |     CFDataRef address_data = CFDataCreate(NULL, (const UInt8 *)&addr4, sizeof(addr4)); | ||||||
|  |  | ||||||
|     CFSocketSetAddress(fdvendor, address_data); |     CFSocketSetAddress(fdvendor, address_data); | ||||||
|     CFRelease(address_data); |     CFRelease(address_data); | ||||||
|  |     socklen_t addrlen = sizeof(addr4); | ||||||
|  |     res = getsockname(CFSocketGetNative(fdvendor),(struct sockaddr *)&addr4,&addrlen); | ||||||
|  |     assert(res == 0); | ||||||
|  |     port = ntohs(addr4.sin_port); | ||||||
|  |  | ||||||
|     CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, fdvendor, 0), kCFRunLoopCommonModes); |     CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, fdvendor, 0), kCFRunLoopCommonModes); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1665,7 +1663,7 @@ void usage(const char* app) { | |||||||
|         "  -L, --justlaunch             just launch the app and exit lldb\n" |         "  -L, --justlaunch             just launch the app and exit lldb\n" | ||||||
|         "  -v, --verbose                enable verbose output\n" |         "  -v, --verbose                enable verbose output\n" | ||||||
|         "  -m, --noinstall              directly start debugging without app install (-d not required)\n" |         "  -m, --noinstall              directly start debugging without app install (-d not required)\n" | ||||||
|         "  -p, --port <number>          port used for device, default: 12345 \n" |         "  -p, --port <number>          port used for device, default: dynamic\n" | ||||||
|         "  -r, --uninstall              uninstall the app before install (do not use with -m; app cache and data are cleared) \n" |         "  -r, --uninstall              uninstall the app before install (do not use with -m; app cache and data are cleared) \n" | ||||||
|         "  -1, --bundle_id <bundle id>  specify bundle id for list and upload\n" |         "  -1, --bundle_id <bundle id>  specify bundle id for list and upload\n" | ||||||
|         "  -l, --list                   list files\n" |         "  -l, --list                   list files\n" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user