From 103592683f590061584646373f3b3bb46df3fa50 Mon Sep 17 00:00:00 2001 From: Alexander Peskov Date: Thu, 20 May 2021 18:24:24 +0300 Subject: [PATCH 01/23] [IOS-RPC] Missprint in flag value Signed-off-by: Alexander Peskov --- apps/ios_rpc/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ios_rpc/CMakeLists.txt b/apps/ios_rpc/CMakeLists.txt index 75f34b4dce05..608762fb48e7 100644 --- a/apps/ios_rpc/CMakeLists.txt +++ b/apps/ios_rpc/CMakeLists.txt @@ -73,5 +73,5 @@ ExternalProject_Add(ios_rpc IPHONEOS_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} DEVELOPMENT_TEAM=${CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM} TVM_BUILD_DIR=${CMAKE_BINARY_DIR} - USE_CUSTOM_DSO_LOADER=YES + USE_CUSTOM_DSO_LOADER=1 ) From a4794c56cafcf4b8a55fcab8f4199047f05bf8bd Mon Sep 17 00:00:00 2001 From: Alexander Peskov Date: Mon, 31 May 2021 17:30:19 +0300 Subject: [PATCH 02/23] [IOS-RPC] custom_dyld up commit id. Fix mem leak Signed-off-by: Alexander Peskov --- apps/ios_rpc/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ios_rpc/CMakeLists.txt b/apps/ios_rpc/CMakeLists.txt index 608762fb48e7..357f1f35fdb9 100644 --- a/apps/ios_rpc/CMakeLists.txt +++ b/apps/ios_rpc/CMakeLists.txt @@ -39,7 +39,7 @@ endif() # It is required to load unsigned shared modules on real iOS devices ExternalProject_Add(custom_dso_loader GIT_REPOSITORY https://github.com/octoml/macho-dyld.git - GIT_TAG 48d1e8b5c40c7f5b744cb089634af17dd86125b2 + GIT_TAG 0742b8129de7df1130be355b74faa8c036265bfc PREFIX custom_dso_loader LOG_DOWNLOAD TRUE LOG_CONFIGURE TRUE From 55be91eba4931733c73a5ec463dd7b9bcd5b72a7 Mon Sep 17 00:00:00 2001 From: Alexander Peskov Date: Sun, 30 May 2021 15:46:09 +0300 Subject: [PATCH 03/23] [IOS-RPC] build without scheme Signed-off-by: Alexander Peskov --- apps/ios_rpc/CMakeLists.txt | 4 ++-- apps/ios_rpc/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/ios_rpc/CMakeLists.txt b/apps/ios_rpc/CMakeLists.txt index 357f1f35fdb9..93024d3d227a 100644 --- a/apps/ios_rpc/CMakeLists.txt +++ b/apps/ios_rpc/CMakeLists.txt @@ -62,14 +62,14 @@ ExternalProject_Add(ios_rpc CONFIGURE_COMMAND "" INSTALL_COMMAND "" BUILD_COMMAND xcodebuild - -scheme tvmrpc + -target tvmrpc -configuration ${CMAKE_BUILD_TYPE} -project /tvmrpc.xcodeproj - -derivedDataPath -sdk ${CMAKE_OSX_SYSROOT} -arch ${CMAKE_OSX_ARCHITECTURES} -hideShellScriptEnvironment build + SYMROOT= IPHONEOS_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} DEVELOPMENT_TEAM=${CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM} TVM_BUILD_DIR=${CMAKE_BINARY_DIR} diff --git a/apps/ios_rpc/README.md b/apps/ios_rpc/README.md index b2d9e199b334..4255ed92e264 100644 --- a/apps/ios_rpc/README.md +++ b/apps/ios_rpc/README.md @@ -118,7 +118,7 @@ cmake .. -DUSE_IOS_RPC=ON # to enable build iOS RPC application from TVM project tree cmake --build . --target custom_dso_loader ios_rpc # Will use custom DSO loader by default # Resulting iOS RPC app bundle will be placed in: -# apps/ios_rpc/ios_rpc/src/ios_rpc-build/Build/Products/[CONFIG]-iphoneos/tvmrpc.app +# apps/ios_rpc/ios_rpc/src/ios_rpc-build/[CONFIG]-iphoneos/tvmrpc.app ``` To enable using of Custom DSO Plugin during xcode build outsde of Cmake you should specify two additional variables. From bcdff79bc20b9a6fbd43defbd3a43bdc129ef4f8 Mon Sep 17 00:00:00 2001 From: Alexander Peskov Date: Fri, 16 Apr 2021 12:29:16 +0300 Subject: [PATCH 04/23] [IOS-RPC] Add tracker support into ios-rpc app Also containes: * Links with tvm_runtime.dylib * Minor improvements from UX perspective * Add cli args support * Add caching for url/port/key attributes Signed-off-by: Alexander Peskov --- apps/ios_rpc/CMakeLists.txt | 2 +- apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj | 40 +- .../ios_rpc/tvmrpc/Base.lproj/Main.storyboard | 51 +- apps/ios_rpc/tvmrpc/RPCServer.h | 93 +++ apps/ios_rpc/tvmrpc/RPCServer.mm | 709 ++++++++++++++++++ apps/ios_rpc/tvmrpc/TVMRuntime.h | 65 -- apps/ios_rpc/tvmrpc/TVMRuntime.mm | 128 +--- apps/ios_rpc/tvmrpc/ViewController.h | 26 +- apps/ios_rpc/tvmrpc/ViewController.mm | 237 +++--- apps/ios_rpc/tvmrpc/main.m | 2 + apps/ios_rpc/tvmrpc/rpc_args.h | 64 ++ apps/ios_rpc/tvmrpc/rpc_args.mm | 221 ++++++ apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm | 21 +- 13 files changed, 1288 insertions(+), 371 deletions(-) create mode 100644 apps/ios_rpc/tvmrpc/RPCServer.h create mode 100644 apps/ios_rpc/tvmrpc/RPCServer.mm delete mode 100644 apps/ios_rpc/tvmrpc/TVMRuntime.h create mode 100644 apps/ios_rpc/tvmrpc/rpc_args.h create mode 100644 apps/ios_rpc/tvmrpc/rpc_args.mm diff --git a/apps/ios_rpc/CMakeLists.txt b/apps/ios_rpc/CMakeLists.txt index 93024d3d227a..3c01e41de1b9 100644 --- a/apps/ios_rpc/CMakeLists.txt +++ b/apps/ios_rpc/CMakeLists.txt @@ -57,7 +57,7 @@ ExternalProject_Add(custom_dso_loader # iOS RPC Xcode project wrapper to integrate into Cmake ExternalProject_Add(ios_rpc PREFIX ios_rpc - DEPENDS custom_dso_loader + DEPENDS custom_dso_loader tvm_runtime SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_COMMAND "" INSTALL_COMMAND "" diff --git a/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj b/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj index a5b69c829e4f..bd8a33c4d867 100644 --- a/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj +++ b/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj @@ -26,6 +26,10 @@ objects = { /* Begin PBXBuildFile section */ + 016B19C22657B390002E1719 /* RPCServer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 016B19C12657B390002E1719 /* RPCServer.mm */; }; + 01A1DB432652CBA700655BBC /* rpc_args.mm in Sources */ = {isa = PBXBuildFile; fileRef = 01A1DB412652CBA700655BBC /* rpc_args.mm */; }; + 01A9B7B3265BD1FD000D092F /* libtvm_runtime.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */; }; + 01A9B7B8265BD307000D092F /* libtvm_runtime.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; C02637501F1C25E8007247A9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C026374F1F1C25E8007247A9 /* main.m */; }; C02637531F1C25E8007247A9 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C02637521F1C25E8007247A9 /* AppDelegate.m */; }; C02637591F1C25E8007247A9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C02637571F1C25E8007247A9 /* Main.storyboard */; }; @@ -47,7 +51,26 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 01A9B7B9265BD307000D092F /* Embed Libraries */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 01A9B7B8265BD307000D092F /* libtvm_runtime.dylib in Embed Libraries */, + ); + name = "Embed Libraries"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + 016B19C02657B390002E1719 /* RPCServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RPCServer.h; sourceTree = ""; }; + 016B19C12657B390002E1719 /* RPCServer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RPCServer.mm; sourceTree = ""; }; + 01A1DB402652CBA700655BBC /* rpc_args.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rpc_args.h; sourceTree = ""; }; + 01A1DB412652CBA700655BBC /* rpc_args.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = rpc_args.mm; sourceTree = ""; }; + 01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libtvm_runtime.dylib; path = "${TVM_BUILD_DIR}/libtvm_runtime.dylib"; sourceTree = ""; }; C026374B1F1C25E8007247A9 /* tvmrpc.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvmrpc.app; sourceTree = BUILT_PRODUCTS_DIR; }; C026374F1F1C25E8007247A9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; C02637511F1C25E8007247A9 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -58,7 +81,6 @@ C026375D1F1C25E8007247A9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; C026375F1F1C25E8007247A9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C02637651F1C2690007247A9 /* TVMRuntime.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TVMRuntime.mm; sourceTree = ""; }; - C02637671F1C269B007247A9 /* TVMRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TVMRuntime.h; sourceTree = ""; }; C02637681F1C26AF007247A9 /* ViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = ""; }; C05A2C861F1DCE0900D4798B /* tvmrpcLauncher.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = tvmrpcLauncher.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C05A2C881F1DCE0900D4798B /* tvmrpcLauncher.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = tvmrpcLauncher.mm; sourceTree = ""; }; @@ -71,6 +93,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 01A9B7B3265BD1FD000D092F /* libtvm_runtime.dylib in Frameworks */, D7685AD324390EAE00D1469C /* CoreML.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -109,12 +132,15 @@ C026374D1F1C25E8007247A9 /* tvmrpc */ = { isa = PBXGroup; children = ( - C02637681F1C26AF007247A9 /* ViewController.mm */, - C02637671F1C269B007247A9 /* TVMRuntime.h */, + 016B19C02657B390002E1719 /* RPCServer.h */, + 016B19C12657B390002E1719 /* RPCServer.mm */, + 01A1DB402652CBA700655BBC /* rpc_args.h */, + 01A1DB412652CBA700655BBC /* rpc_args.mm */, C02637651F1C2690007247A9 /* TVMRuntime.mm */, C02637511F1C25E8007247A9 /* AppDelegate.h */, C02637521F1C25E8007247A9 /* AppDelegate.m */, C02637541F1C25E8007247A9 /* ViewController.h */, + C02637681F1C26AF007247A9 /* ViewController.mm */, C02637571F1C25E8007247A9 /* Main.storyboard */, C026375A1F1C25E8007247A9 /* Assets.xcassets */, C026375C1F1C25E8007247A9 /* LaunchScreen.storyboard */, @@ -144,6 +170,7 @@ D7685AD124390EAD00D1469C /* Frameworks */ = { isa = PBXGroup; children = ( + 01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */, D7685AD224390EAD00D1469C /* CoreML.framework */, ); name = Frameworks; @@ -160,6 +187,7 @@ C02637481F1C25E8007247A9 /* Frameworks */, C02637491F1C25E8007247A9 /* Resources */, C05A2C901F1E683A00D4798B /* ShellScript */, + 01A9B7B9265BD307000D092F /* Embed Libraries */, ); buildRules = ( ); @@ -199,7 +227,7 @@ TargetAttributes = { C026374A1F1C25E8007247A9 = { CreatedOnToolsVersion = 8.3.3; - DevelopmentTeam = 3FR42MXLK9; + DevelopmentTeam = B2Q598A43U; ProvisioningStyle = Automatic; }; C05A2C851F1DCE0900D4798B = { @@ -271,6 +299,8 @@ buildActionMask = 2147483647; files = ( C02637691F1C26AF007247A9 /* ViewController.mm in Sources */, + 01A1DB432652CBA700655BBC /* rpc_args.mm in Sources */, + 016B19C22657B390002E1719 /* RPCServer.mm in Sources */, C02637531F1C25E8007247A9 /* AppDelegate.m in Sources */, C02637661F1C2690007247A9 /* TVMRuntime.mm in Sources */, C02637501F1C25E8007247A9 /* main.m in Sources */, @@ -351,6 +381,7 @@ "$(inherited)", "DMLC_USE_LOGGING_LIBRARY=", "TVM_USE_LIBBACKTRACE=0", + "TVM_LOG_CUSTOMIZE=1", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -398,6 +429,7 @@ GCC_PREPROCESSOR_DEFINITIONS = ( "DMLC_USE_LOGGING_LIBRARY=", "TVM_USE_LIBBACKTRACE=0", + "TVM_LOG_CUSTOMIZE=1", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; diff --git a/apps/ios_rpc/tvmrpc/Base.lproj/Main.storyboard b/apps/ios_rpc/tvmrpc/Base.lproj/Main.storyboard index 2356abf6ca4f..c2dfc6269c51 100644 --- a/apps/ios_rpc/tvmrpc/Base.lproj/Main.storyboard +++ b/apps/ios_rpc/tvmrpc/Base.lproj/Main.storyboard @@ -16,13 +16,11 @@ - - - - + + - + @@ -41,21 +39,19 @@ - - - + + + + + + + + + + + + + diff --git a/apps/ios_rpc/tvmrpc/RPCServer.h b/apps/ios_rpc/tvmrpc/RPCServer.h new file mode 100644 index 000000000000..1ad9b1f71c36 --- /dev/null +++ b/apps/ios_rpc/tvmrpc/RPCServer.h @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/*! + * \file Provide interfaces to launch and control RPC Service routine + */ + +#import + +/*! + * \brief Enum with possible status of RPC server + * Used to report state to listener + */ +typedef enum { + RPCServerStatus_Launched, // Worker thread is launched + RPCServerStatus_Stopped, // Worker thread stopped + RPCServerStatus_Connected, // Connected to Proxy/Tracker + RPCServerStatus_Disconnected, // Disconnected from Proxy/Tracker + RPCServerStatus_RPCSessionStarted, // RPC session is started + RPCServerStatus_RPCSessionFinished // RPC session is finished +} RPCServerStatus; + +/*! + * \brief Enum with modes of servicing supported by RPCServer + */ +typedef enum { + /// Tracker mode. Same as Pure Server plus register it into Tracker. + RPCServerMode_Tracker, + /// Proxy mode. Connect to proxy server and wait response. + RPCServerMode_Proxy, + /// Pure RPC server mode. Open port with RPC server and wait incoming connection. + RPCServerMode_PureServer +} RPCServerMode; + +/*! + * \brief Listener for events happened with RPCServer + */ +@protocol RPCServerEventListener +/// Callback to notifying about new status +- (void)onError:(NSString*) msg; +/// Callback to notifying about error +- (void)onStatusChanged:(RPCServerStatus) status; +@end + + +/*! + * \brief RPC Server instance + * Contains internal worker thread plus + */ +@interface RPCServer : NSObject + +/*! + * \brief Create server with specified sevicing mode + * \param mode Mode of server + */ ++ (instancetype)serverWithMode:(RPCServerMode) mode; + +/*! + * \brief Set listener delegate + * \param delegate Event listener delegate to set + */ +- (void)setDelegate:(id) delegate; + +/*! + * \brief Start RPC server with options. Non blocking method + * \param host Host address of Tracker/Proxy to connect. Skipped for PureServer mode + * \param port Port of Tracker/Proxy to connect. Skipped for PureServer mode + * \param key device key to use in RPC server + */ +- (void)startWithHost:(NSString*) host port:(int) port key:(NSString*) key; + +/*! + * \brief Stop RPC server. Non blocking method + */ +- (void)stop; + +@end diff --git a/apps/ios_rpc/tvmrpc/RPCServer.mm b/apps/ios_rpc/tvmrpc/RPCServer.mm new file mode 100644 index 000000000000..0e95b47e6470 --- /dev/null +++ b/apps/ios_rpc/tvmrpc/RPCServer.mm @@ -0,0 +1,709 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/*! + * \file ViewController.mm + */ + +#import "RPCServer.h" + +#include +#include + +#include +#include + +// To get device WiFi IP +#include +#include +#include +#include + +// TVM internal header to access Magic keys like kRPCMagic and others +#include "../../../src/runtime/rpc/rpc_endpoint.h" + +namespace tvm { +namespace runtime { + +/*! + * \brief Message handling function for event driven server. + * + * \param in_bytes The incoming bytes. + * \param event_flag 1: read_available, 2: write_avaiable. + * \return State flag. + * 1: continue running, no need to write, + * 2: need to write + * 0: shutdown + */ +using FEventHandler = PackedFunc; + +/*! + * \brief Create a server event handler. + * + * \param outputStream The output stream used to send outputs. + * \param name The name of the server. + * \param remote_key The remote key + * \return The event handler. + */ +FEventHandler CreateServerEventHandler(NSOutputStream* outputStream, std::string name, + std::string remote_key) { + const PackedFunc* event_handler_factor = Registry::Get("rpc.CreateEventDrivenServer"); + ICHECK(event_handler_factor != nullptr) + << "You are using tvm_runtime module built without RPC support. " + << "Please rebuild it with USE_RPC flag."; + + PackedFunc writer_func([outputStream](TVMArgs args, TVMRetValue* rv) { + TVMByteArray *data = args[0].ptr(); + int64_t nbytes = [outputStream write:reinterpret_cast(data->data) maxLength:data->size]; + if (nbytes < 0) { + NSLog(@"%@", [outputStream streamError].localizedDescription); + throw tvm::Error("Stream error"); + } + *rv = nbytes; + }); + + return (*event_handler_factor)(writer_func, name, remote_key); +} + +/*! + * \brief Helper function to query real IP of device in WiFi network + * \return string with IPv4 in format "192.168.0.1" or "unknown" if cannot detect + */ +static std::string getWiFiAddress() { + std::string address = "unknown"; + ifaddrs *interfaces = nullptr; + + int success = getifaddrs(&interfaces); + if (success == 0) { + // Loop through linked list of interfaces + ifaddrs *temp_addr = interfaces; + while(temp_addr != NULL) { + if(temp_addr->ifa_addr->sa_family == AF_INET) { + // Check if interface is en0 which is the wifi connection on the iPhone + if(std::string(temp_addr->ifa_name) == "en0") { + address = std::string(inet_ntoa(((sockaddr_in *)temp_addr->ifa_addr)->sin_addr)); + } + } + temp_addr = temp_addr->ifa_next; + } + } + + freeifaddrs(interfaces); + return address; +} + +} // namespace runtime +} // namespace tvm + + +// Forward declaration +@interface RPCTrackerClien : RPCServer +@end + +// Forward declaration +@interface RPCServerPure : RPCServer +@end + +/*! + * \brief Base implementation of server to work with RPCProxy + * Will automatically connect/reconnect to RPCProxy server + */ +@implementation RPCServer { +@protected + // The key of the server. + NSString* key_; + // The url of host. + NSString* url_; + // The port of host. + NSInteger port_; + // Event listener + id delegate_; + // Worker thread + NSThread* worker_thread_; + // Triger to continue processing + BOOL shouldKeepRunning; + // Ip of rpc server (actually ip of ios device) + std::string server_ip_; + // Port of rpc server + int server_port_; + // Input socket stream + NSInputStream* inputStream_; + // Output socket stream + NSOutputStream* outputStream_; + // Temporal receive buffer. + std::string recvBuffer_; + // Whether connection is initialized. + bool initialized_; + // Initial bytes to be send to remote + std::string initBytes_; + // Send pointer of initial bytes. + size_t initSendPtr_; + // Event handler. + tvm::runtime::FEventHandler handler_; +} + ++ (instancetype)serverWithMode:(RPCServerMode) mode { + if (mode == RPCServerMode_PureServer) + return [[RPCServerPure alloc] init]; + if (mode == RPCServerMode_Proxy) + return [[RPCServer alloc] init]; + if (mode == RPCServerMode_Tracker) + return [[RPCTrackerClien alloc] init]; + return nil; +} + +- (instancetype)init { + [super init]; + server_ip_ = tvm::runtime::getWiFiAddress(); + return self; +} + +/*! + * Internal setters methods + */ +- (void)setDelegate:(id) delegate { delegate_ = delegate; } +- (void)setKey:(NSString*) key { key_ = key; } +- (void)setUrl:(NSString*) url { url_ = url; } +- (void)setPort:(NSInteger) port { port_ = port; } + +/*! + * \brief Main event listener method. All stream event handling starts here + */ +- (void)stream:(NSStream*)strm handleEvent:(NSStreamEvent)event { + std::string buffer; + switch (event) { + case NSStreamEventOpenCompleted: { + break; + } + case NSStreamEventHasBytesAvailable: + if (strm == inputStream_) { + [self onReadAvailable]; + } + break; + case NSStreamEventHasSpaceAvailable: { + if (strm == outputStream_) { + [self onWriteAvailable]; + } + break; + } + case NSStreamEventErrorOccurred: { + NSLog(@"%@", [strm streamError].localizedDescription); + break; + } + case NSStreamEventEndEncountered: { + [self onEndEvent]; + break; + } + default: { + NSLog(@"Unknown event"); + } + } +} + +-(void)notifyError:(NSString*) msg { + NSLog(@"[IOS-RPC] ERROR: %@", msg); + if (delegate_ != nil) + [delegate_ onError:msg]; +} + +-(void)notifyState:(RPCServerStatus) state { + if (state == RPCServerStatus_Launched) { + // Notify host runner script with actual address + NSLog(@"[IOS-RPC] IP: %s", server_ip_.c_str()); + NSLog(@"[IOS-RPC] PORT: %d", server_port_); + } + // Notify host runner script with current status + NSLog(@"[IOS-RPC] STATE: %d", state); + // Notify listener + if (delegate_ != nil) + [delegate_ onStatusChanged:state]; +} + +- (void)onReadAvailable { + if (!initialized_) { + using tvm::runtime::kRPCMagic; + int code; + size_t nbytes = [inputStream_ read:reinterpret_cast(&code) maxLength:sizeof(code)]; + if (nbytes != sizeof(code)) { + [self notifyError:@"Fail to receive remote confirmation code."]; + [self close]; + } else if (code == kRPCMagic + 2) { + [self notifyError:@"Proxy server cannot find client that matches the key"]; + [self close]; + } else if (code == kRPCMagic + 1) { + [self notifyError:@"Proxy server already have another server with same key"]; + [self close]; + } else if (code != kRPCMagic) { + [self notifyError:@"Given address is not a TVM RPC Proxy"]; + [self close]; + } else { + initialized_ = true; + ICHECK(handler_ != nullptr); + } + } + const int kBufferSize = 4 << 10; + if (initialized_) { + while ([inputStream_ hasBytesAvailable]) { + recvBuffer_.resize(kBufferSize); + uint8_t* bptr = reinterpret_cast(&recvBuffer_[0]); + size_t nbytes = [inputStream_ read:bptr maxLength:kBufferSize]; + recvBuffer_.resize(nbytes); + int flag = 1; + if ([outputStream_ hasSpaceAvailable]) { + flag |= 2; + } + // always try to write + try { + TVMByteArray arr {recvBuffer_.data(), recvBuffer_.size()}; + flag = handler_(arr, flag); + if (flag == 2) { + [self onShutdownReceived]; + } + } catch (const tvm::Error& e) { + [self close]; + } + } + } +} + +- (void)onWriteAvailable { + if (initSendPtr_ < initBytes_.length()) { + initSendPtr_ += [outputStream_ write:reinterpret_cast(&initBytes_[initSendPtr_]) + maxLength:(initBytes_.length() - initSendPtr_)]; + } + [self notifyState:RPCServerStatus_Connected]; + if (initialized_) { + try { + TVMByteArray dummy {nullptr, 0}; + int flag = handler_(dummy, 2); + if (flag == 2) { + [self onShutdownReceived]; + } + } catch (const tvm::Error& e) { + [self close]; + } + } +} + +- (void)onShutdownReceived { + [self close]; +} + +- (void)onEndEvent { + [self close]; + [self notifyState:RPCServerStatus_RPCSessionFinished]; + // Basic behaviour is to reconnect + [self open]; +} + +- (void)open { + // Initialize the data states. + std::string full_key = std::string("server:") + [key_ UTF8String]; + std::ostringstream os; + int rpc_magic = tvm::runtime::kRPCMagic; + os.write(reinterpret_cast(&rpc_magic), sizeof(rpc_magic)); + int keylen = static_cast(full_key.length()); + os.write(reinterpret_cast(&keylen), sizeof(keylen)); + os.write(full_key.c_str(), full_key.length()); + initialized_ = false; + initBytes_ = os.str(); + initSendPtr_ = 0; + + // Initialize the network. + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)url_, port_, + &readStream, &writeStream); + inputStream_ = (NSInputStream*)readStream; + outputStream_ = (NSOutputStream*)writeStream; + [inputStream_ setDelegate:self]; + [outputStream_ setDelegate:self]; + [inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ open]; + [inputStream_ open]; + + handler_ = tvm::runtime::CreateServerEventHandler(outputStream_, full_key, "%toinit"); + ICHECK(handler_ != nullptr); +} + +- (void)close { + [inputStream_ close]; + [outputStream_ close]; + [inputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [inputStream_ setDelegate:nil]; + [outputStream_ setDelegate:nil]; + inputStream_ = nil; + outputStream_ = nil; + handler_ = nullptr; + [self notifyState:RPCServerStatus_Disconnected]; +} + +- (void)startWithHost:(NSString*) host port: (int) port key:(NSString*) key { + key_ = [key copy]; + port_ = port; + url_ = [host copy]; + + // process in separate thead with runloop + worker_thread_ = [[NSThread alloc] initWithBlock:^{ + @autoreleasepool { + [self open]; + [self notifyState:RPCServerStatus_Launched]; + shouldKeepRunning = YES; + while (shouldKeepRunning && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + [self notifyState:RPCServerStatus_Stopped]; + } + }]; + [worker_thread_ start]; +} + +- (void)stop { + if (worker_thread_ == nil) + return; + + [self performSelector:@selector(stop_) onThread:worker_thread_ withObject:nil waitUntilDone:NO]; + worker_thread_ = nil; +} + +- (void)stop_ { + [self close]; + shouldKeepRunning = NO; +} + +@end + +@implementation RPCServerPure { + // Socket with incoming conenction (only for Pure RPC server) + CFSocketNativeHandle socket_; +} + +- (void)handleConnect:(const CFSocketNativeHandle) hdl { + socket_ = hdl; + + // Initialize the data states. + std::string full_key = std::string("server:") + [key_ UTF8String]; + std::ostringstream os; + int rpc_magic = tvm::runtime::kRPCMagic; + os.write(reinterpret_cast(&rpc_magic), sizeof(rpc_magic)); + int keylen = static_cast(full_key.length()); + os.write(reinterpret_cast(&keylen), sizeof(keylen)); + os.write(full_key.c_str(), full_key.length()); + initialized_ = false; + initBytes_ = os.str(); + initSendPtr_ = 0; + + // Initialize the network. + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + CFStreamCreatePairWithSocket(NULL, socket_, &readStream, &writeStream); + + inputStream_ = (NSInputStream*)readStream; + outputStream_ = (NSOutputStream*)writeStream; + [inputStream_ setDelegate:self]; + [outputStream_ setDelegate:self]; + [inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ open]; + [inputStream_ open]; + + handler_ = tvm::runtime::CreateServerEventHandler(outputStream_, full_key, "%toinit"); + ICHECK(handler_ != nullptr); +} + +static void handleConnect(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { + RPCServerPure* it = static_cast(info); + [it handleConnect:*static_cast(data)]; +} + +- (void)open { + CFSocketContext ctx {}; + ctx.info = self; + + CFSocketRef myipv4cfsock = CFSocketCreate( + kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, + kCFSocketAcceptCallBack, handleConnect, &ctx); + + struct sockaddr_in sin; + int rpc_port = 9090; // TODO: hardcoded. Should try bind in range of ports + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(rpc_port); + sin.sin_addr.s_addr= INADDR_ANY; + + CFDataRef sincfd = CFDataCreate( + kCFAllocatorDefault, + (UInt8 *)&sin, + sizeof(sin)); + + if (CFSocketSetAddress(myipv4cfsock, sincfd) != 0) + @throw [NSException exceptionWithName:@"SocketError" + reason:[NSString stringWithFormat:@"Can not bind to port %d", rpc_port] + userInfo:nil]; + CFRelease(sincfd); + server_port_ = rpc_port; + + CFRunLoopSourceRef socketsource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, myipv4cfsock, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), socketsource, kCFRunLoopDefaultMode); +} + +- (void)onEndEvent { + [self close]; + [self notifyState:RPCServerStatus_RPCSessionFinished]; +} + +@end + +typedef enum { + HandshakeToSend, + HandshakeToRecv, + ServerInfoToSend, + ServerInfoToRecv, + ReportResToSend, + ReportResToRecv, + UpdateKeyToSend, + UpdateKeyToRecv, + WaitConnection, +} TrackerClientState; + +@implementation RPCTrackerClien { + // RPC Server to register in tracker + RPCServerPure* rpc_server_; + // Size of data required accumulate in readBuffer before processing + int required_data_size; + // State of tracker client + TrackerClientState state_; +} + +- (void)toSend:(NSData*) data { + // try to send + NSInteger sent_size = [outputStream_ write:(uint8_t*)data.bytes maxLength:data.length]; + // assume that all data is sent + if (sent_size != data.length) + @throw [NSException exceptionWithName:@"SocketError" + reason:[NSString stringWithFormat:@"Unable to send data"] + userInfo:nil]; +} + +- (void)toSendPacked:(NSData*) data { + // try to send + int packet_size = data.length; + NSInteger sent_size = [outputStream_ write:(uint8_t*)&packet_size maxLength:sizeof(packet_size)]; + if (sent_size != sizeof(packet_size)) + @throw [NSException exceptionWithName:@"SocketError" + reason:[NSString stringWithFormat:@"Unable to send data"] + userInfo:nil]; + + NSInteger sent_data = [outputStream_ write:(uint8_t*)data.bytes maxLength:data.length]; + // assume that all data is sent + if (sent_data != data.length) + @throw [NSException exceptionWithName:@"SocketError" + reason:[NSString stringWithFormat:@"Unable to send data"] + userInfo:nil]; +} + +- (void)onReadAvailable { + const int kBufferSize = 4 << 10; + const int prev_size = recvBuffer_.size(); + recvBuffer_.resize(kBufferSize); + size_t nbytes = [inputStream_ read:(uint8_t*)recvBuffer_.data() + prev_size + maxLength:recvBuffer_.size() - prev_size]; + recvBuffer_.resize(nbytes + prev_size); + + if (recvBuffer_.size() < required_data_size) + return; + + switch (state_) { + case HandshakeToRecv: { + int code = tvm::runtime::kRPCTrackerMagic; + if (recvBuffer_.size() < sizeof(code)) { + required_data_size = sizeof(code); + break; + } + + if (recvBuffer_.size() != sizeof(code) || *(int*)recvBuffer_.data() != code) { + [self notifyError:@"Wrong responce, server is not tracker."]; + [self close]; + break; + } + + recvBuffer_.erase(recvBuffer_.begin(), recvBuffer_.begin() + sizeof(code)); + required_data_size = 0; + [self notifyState:RPCServerStatus_Connected]; + state_ = ServerInfoToSend; + break; + } + case ServerInfoToRecv: { + std::string expected = std::to_string(static_cast(tvm::runtime::TrackerCode::kSuccess)); + int packet_size; + if (recvBuffer_.size() < sizeof(packet_size)) { + required_data_size = sizeof(packet_size); + break; + } + + packet_size = *(int*)recvBuffer_.data(); + if (recvBuffer_.size() < sizeof(packet_size) + packet_size) { + required_data_size = sizeof(packet_size) + packet_size; + break; + } + + if (std::string((char*)recvBuffer_.data() + sizeof(packet_size), packet_size) != expected) { + [self notifyError:@"Was not able to update info in tracker. Responce is not OK."]; + [self close]; + break; + } + + recvBuffer_.erase(recvBuffer_.begin(), recvBuffer_.begin() + sizeof(packet_size) + packet_size); + required_data_size = 0; + state_ = ReportResToSend; + break; + } + case ReportResToRecv: { + std::string expected = std::to_string(static_cast(tvm::runtime::TrackerCode::kSuccess)); + int packet_size; + if (recvBuffer_.size() < sizeof(packet_size)) { + required_data_size = sizeof(packet_size); + break; + } + + packet_size = *(int*)recvBuffer_.data(); + if (recvBuffer_.size() < sizeof(packet_size) + packet_size) { + required_data_size = sizeof(packet_size) + packet_size; + break; + } + + if (std::string((char*)recvBuffer_.data() + sizeof(packet_size), packet_size) != expected) { + [self notifyError:@"Was not able to put resource into tracker. Responce is not OK."]; + [self close]; + break; + } + + recvBuffer_.erase(recvBuffer_.begin(), recvBuffer_.begin() + sizeof(packet_size) + packet_size); + required_data_size = 0; + state_ = WaitConnection; + break; + } + default: + // Nothing + break; + } + + if (outputStream_.hasSpaceAvailable) + [self onWriteAvailable]; +} + +- (void)onWriteAvailable { + switch (state_) { + case HandshakeToSend: { + int code = tvm::runtime::kRPCTrackerMagic; + NSData* data = [NSData dataWithBytes:&code length:sizeof(code)]; + [self toSend:data]; + state_ = HandshakeToRecv; + break; + } + case ServerInfoToSend: { + std::ostringstream ss; + ss << "[" << static_cast(tvm::runtime::TrackerCode::kUpdateInfo) << ", {\"key\": \"server:" << key_.UTF8String + << "\"}]"; + std::string data_s = ss.str(); + NSData* data = [NSData dataWithBytes:data_s.data() length:data_s.length()]; + [self toSendPacked:data]; + state_ = ServerInfoToRecv; + break; + } + case ReportResToSend: { + std::mt19937 gen(std::random_device{}()); + std::uniform_real_distribution dis(0.0, 1.0); + + // TODO: All values are hardcoded + int port = 9090; + std::string custom_addr = "null"; + std::string matchkey = std::string(key_.UTF8String) + ":" + std::to_string(dis(gen)); + + std::ostringstream ss; + ss << "[" << static_cast(tvm::runtime::TrackerCode::kPut) << ", \"" << key_.UTF8String << "\", [" << port + << ", \"" << matchkey << "\"], " << custom_addr << "]"; + + std::string data_s = ss.str(); + NSData* data = [NSData dataWithBytes:data_s.data() length:data_s.length()]; + [self toSendPacked:data]; + state_ = ReportResToRecv; + break; + } + default: + // Nothing + break; + } + + if (inputStream_.hasBytesAvailable) + [self onReadAvailable]; +} + +-(void)open { + // create RPC pure server + // * set self as delegate (to register back when servicing is finished) + // * mute printing status + // * stop processing by sending stop + rpc_server_ = [[RPCServerPure alloc] init]; + rpc_server_.key = key_; + rpc_server_.delegate = self; + [rpc_server_ open]; + + // Initialize the network. + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + + CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)url_, port_, + &readStream, &writeStream); + + inputStream_ = (NSInputStream*)readStream; + outputStream_ = (NSOutputStream*)writeStream; + [inputStream_ setDelegate:self]; + [outputStream_ setDelegate:self]; + [inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ open]; + [inputStream_ open]; +} + +- (void)close { + [rpc_server_ close]; + [super close]; + [self notifyState:RPCServerStatus_Disconnected]; +} + +- (void)onEndEvent { + [self close]; +} + +- (void)onError:(NSString*) msg { + // transfer error form rpc_server_ to real delegate + [self notifyError:msg]; +} + +- (void)onStatusChanged:(RPCServerStatus) status { + if (status == RPCServerStatus_RPCSessionFinished) { + [self notifyState:status]; + state_ = ReportResToSend; + [self onWriteAvailable]; + } +} + +@end diff --git a/apps/ios_rpc/tvmrpc/TVMRuntime.h b/apps/ios_rpc/tvmrpc/TVMRuntime.h deleted file mode 100644 index 0d172fc3eaa1..000000000000 --- a/apps/ios_rpc/tvmrpc/TVMRuntime.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/*! - * \file TVMRuntime.h - */ -#import -// Customize logging mechanism, redirect to NSLOG -#define TVM_LOG_CUSTOMIZE 1 -#define TVM_METAL_RUNTIME 1 - -#include -#include -#include - -namespace tvm { -namespace runtime { - -/*! - * \brief Message handling function for event driven server. - * - * \param in_bytes The incoming bytes. - * \param event_flag 1: read_available, 2: write_avaiable. - * \return State flag. - * 1: continue running, no need to write, - * 2: need to write - * 0: shutdown - */ -using FEventHandler = std::function; - -/*! - * \brief Create a server event handler. - * - * \param outputStream The output stream used to send outputs. - * \param name The name of the server. - * \param remote_key The remote key - * \return The event handler. - */ -FEventHandler CreateServerEventHandler(NSOutputStream* outputStream, std::string name, - std::string remote_key); - -} // namespace runtime -} // namespace tvm - -@interface TVMRuntime : NSObject - -+ (void)launchSyncServer; - -@end diff --git a/apps/ios_rpc/tvmrpc/TVMRuntime.mm b/apps/ios_rpc/tvmrpc/TVMRuntime.mm index 8950eb4eab1d..458808e4cc19 100644 --- a/apps/ios_rpc/tvmrpc/TVMRuntime.mm +++ b/apps/ios_rpc/tvmrpc/TVMRuntime.mm @@ -20,50 +20,26 @@ /*! * \file TVMRuntime.mm */ -#include "TVMRuntime.h" -// Runtime API -#include "../../../src/runtime/c_runtime_api.cc" -#include "../../../src/runtime/contrib/random/random.cc" -#include "../../../src/runtime/cpu_device_api.cc" -#include "../../../src/runtime/dso_library.cc" -#include "../../../src/runtime/file_utils.cc" -#include "../../../src/runtime/library_module.cc" -#include "../../../src/runtime/logging.cc" -#include "../../../src/runtime/metadata_module.cc" -#include "../../../src/runtime/module.cc" -#include "../../../src/runtime/ndarray.cc" -#include "../../../src/runtime/object.cc" -#include "../../../src/runtime/profiling.cc" -#include "../../../src/runtime/registry.cc" -#include "../../../src/runtime/source_utils.cc" -#include "../../../src/runtime/system_library.cc" -#include "../../../src/runtime/thread_pool.cc" -#include "../../../src/runtime/threading_backend.cc" -#include "../../../src/runtime/workspace_pool.cc" - -// RPC server -#include "../../../src/runtime/rpc/rpc_channel.cc" -#include "../../../src/runtime/rpc/rpc_endpoint.cc" -#include "../../../src/runtime/rpc/rpc_local_session.cc" -#include "../../../src/runtime/rpc/rpc_module.cc" -#include "../../../src/runtime/rpc/rpc_server_env.cc" -#include "../../../src/runtime/rpc/rpc_session.cc" -#include "../../../src/runtime/rpc/rpc_socket_impl.cc" -// Graph executor -#include "../../../src/runtime/graph_executor/graph_executor.cc" -// Metal -#include "../../../src/runtime/metal/metal_device_api.mm" -#include "../../../src/runtime/metal/metal_module.mm" -// CoreML -#include "../../../src/runtime/contrib/coreml/coreml_runtime.mm" + +#import + +#include + +#include "rpc_args.h" + +// internal TVM header +#include <../../../src/runtime/file_utils.h> #if defined(USE_CUSTOM_DSO_LOADER) && USE_CUSTOM_DSO_LOADER == 1 +// internal TVM header to achive Library class +#include <../../../src/runtime/library_module.h> #include #endif namespace tvm { namespace runtime { namespace detail { + // Override logging mechanism void LogFatalImpl(const std::string& file, int lineno, const std::string& message) { throw tvm::runtime::InternalError(file, lineno, message); @@ -72,77 +48,13 @@ void LogFatalImpl(const std::string& file, int lineno, const std::string& messag void LogMessageImpl(const std::string& file, int lineno, const std::string& message) { NSLog(@"%s:%d: %s", file.c_str(), lineno, message.c_str()); } -} -} -} // namespace dmlc - -namespace tvm { -namespace runtime { - -class NSStreamChannel final : public RPCChannel { - public: - explicit NSStreamChannel(NSOutputStream* stream) : stream_(stream) {} - - size_t Send(const void* data, size_t size) final { - ssize_t nbytes = [stream_ write:reinterpret_cast(data) maxLength:size]; - if (nbytes < 0) { - NSLog(@"%@", [stream_ streamError].localizedDescription); - throw tvm::Error("Stream error"); - } - return nbytes; - } - - size_t Recv(void* data, size_t size) final { - LOG(FATAL) << "Do not allow explicit receive for"; - return 0; - } - - private: - NSOutputStream* stream_; -}; - -FEventHandler CreateServerEventHandler(NSOutputStream* outputStream, std::string name, - std::string remote_key) { - std::unique_ptr ch(new NSStreamChannel(outputStream)); - std::shared_ptr sess = RPCEndpoint::Create(std::move(ch), name, remote_key); - return [sess](const std::string& in_bytes, int flag) { - return sess->ServerAsyncIOEventHandler(in_bytes, flag); - }; -} -// Runtime environment -struct RPCEnv { - public: - RPCEnv() { - NSString* path = NSTemporaryDirectory(); - base_ = [path UTF8String]; - if (base_[base_.length() - 1] != '/') { - base_ = base_ + '/'; - } - } - // Get Path. - std::string GetPath(const std::string& file_name) { return base_ + file_name; } - - private: - std::string base_; -}; - -void LaunchSyncServer() { - // only load dylib from frameworks. - NSBundle* bundle = [NSBundle mainBundle]; - NSString* base = [bundle privateFrameworksPath]; - NSString* path = [base stringByAppendingPathComponent:@"tvm/rpc_config.txt"]; - std::string name = [path UTF8String]; - std::ifstream fs(name, std::ios::in); - std::string url, key; - int port; - ICHECK(fs >> url >> port >> key) << "Invalid RPC config file " << name; - RPCConnect(url, port, "server:" + key, TVMArgs(nullptr, nullptr, 0))->ServerLoop(); -} +} // namespace detail TVM_REGISTER_GLOBAL("tvm.rpc.server.workpath").set_body([](TVMArgs args, TVMRetValue* rv) { - static RPCEnv env; - *rv = env.GetPath(args[0]); + static const std::string base_ = NSTemporaryDirectory().UTF8String; + const std::string path = args[0]; + *rv = base_ + "/" + path; }); TVM_REGISTER_GLOBAL("tvm.rpc.server.load_module").set_body([](TVMArgs args, TVMRetValue* rv) { @@ -206,11 +118,3 @@ void Init(const std::string& name) { } // namespace runtime } // namespace tvm - -@implementation TVMRuntime - -+ (void)launchSyncServer { - tvm::runtime::LaunchSyncServer(); -} - -@end diff --git a/apps/ios_rpc/tvmrpc/ViewController.h b/apps/ios_rpc/tvmrpc/ViewController.h index b188a87b20d3..7f664074857e 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.h +++ b/apps/ios_rpc/tvmrpc/ViewController.h @@ -22,27 +22,10 @@ */ #import -#include "TVMRuntime.h" +#import "RPCServer.h" -@interface ViewController : UIViewController { - // input socket stream - NSInputStream* inputStream_; - // output socket stream - NSOutputStream* outputStream_; - // temporal receive buffer. - std::string recvBuffer_; - // Whether connection is initialized. - bool initialized_; - // Whether auto reconnect when a session is done. - bool auto_reconnect_; - // The key of the server. - std::string key_; - // Initial bytes to be send to remote - std::string initBytes_; - // Send pointer of initial bytes. - size_t initSendPtr_; - // Event handler. - tvm::runtime::FEventHandler handler_; +@interface ViewController : UIViewController { + RPCServer* server_; } @property(weak, nonatomic) IBOutlet UITextField* proxyURL; @@ -53,5 +36,8 @@ - (IBAction)connect:(id)sender; - (IBAction)disconnect:(id)sender; +@property (retain, nonatomic) IBOutlet UIButton *DisconnectButton; +@property (retain, nonatomic) IBOutlet UIButton *ConnectButton; +@property (retain, nonatomic) IBOutlet UISegmentedControl *ModeSelector; @end diff --git a/apps/ios_rpc/tvmrpc/ViewController.mm b/apps/ios_rpc/tvmrpc/ViewController.mm index 4218a6a19d90..e136fb08ee00 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.mm +++ b/apps/ios_rpc/tvmrpc/ViewController.mm @@ -22,168 +22,129 @@ */ #import "ViewController.h" -#include +#import "rpc_args.h" @implementation ViewController -- (void)stream:(NSStream*)strm handleEvent:(NSStreamEvent)event { - std::string buffer; - switch (event) { - case NSStreamEventOpenCompleted: { - self.statusLabel.text = @"Connected"; - break; - } - case NSStreamEventHasBytesAvailable: - if (strm == inputStream_) { - [self onReadAvailable]; - } - break; - case NSStreamEventHasSpaceAvailable: { - if (strm == outputStream_) { - [self onWriteAvailable]; - } - break; - } - case NSStreamEventErrorOccurred: { - NSLog(@"%@", [strm streamError].localizedDescription); - break; - } - case NSStreamEventEndEncountered: { - [self close]; - // auto reconnect when normal end. - [self open]; - break; - } - default: { - NSLog(@"Unknown event"); - } - } -} +- (void)viewDidLoad { + // To handle end editing events + self.proxyURL.delegate = self; + self.proxyPort.delegate = self; + self.proxyKey.delegate = self; -- (void)onReadAvailable { - constexpr int kRPCMagic = 0xff271; - if (!initialized_) { - int code; - size_t nbytes = [inputStream_ read:reinterpret_cast(&code) maxLength:sizeof(code)]; - if (nbytes != sizeof(code)) { - self.infoText.text = @"Fail to receive remote confirmation code."; - [self close]; - } else if (code == kRPCMagic + 2) { - self.infoText.text = @"Proxy server cannot find client that matches the key"; - [self close]; - } else if (code == kRPCMagic + 1) { - self.infoText.text = @"Proxy server already have another server with same key"; - [self close]; - } else if (code != kRPCMagic) { - self.infoText.text = @"Given address is not a TVM RPC Proxy"; - [self close]; - } else { - initialized_ = true; - self.statusLabel.text = @"Proxy connected."; - ICHECK(handler_ != nullptr); - } - } - const int kBufferSize = 4 << 10; - if (initialized_) { - while ([inputStream_ hasBytesAvailable]) { - recvBuffer_.resize(kBufferSize); - uint8_t* bptr = reinterpret_cast(&recvBuffer_[0]); - size_t nbytes = [inputStream_ read:bptr maxLength:kBufferSize]; - recvBuffer_.resize(nbytes); - int flag = 1; - if ([outputStream_ hasSpaceAvailable]) { - flag |= 2; - } - // always try to write - try { - flag = handler_(recvBuffer_, flag); - if (flag == 2) { - [self onShutdownReceived]; - } - } catch (const tvm::Error& e) { - [self close]; - } - } + RPCArgs args = get_current_rpc_args(); + self.proxyURL.text = @(args.host_url); + self.proxyPort.text = @(args.host_port).stringValue; + self.proxyKey.text = @(args.key); + + self.ModeSelector.selectedSegmentIndex = args.server_mode; + + // Connect to tracker immediately + if (args.immediate_connect) { + [self disableUIInteraction]; + [self open]; } } -- (void)onShutdownReceived { - [self close]; -} +/*! + * \brief Disable all UI elements + */ +- (void)disableUIInteraction { + void (^disable)(UITextField* field) = ^(UITextField* field) { + field.enabled = NO; + field.backgroundColor = [UIColor lightGrayColor]; + }; -- (void)onWriteAvailable { - if (initSendPtr_ < initBytes_.length()) { - initSendPtr_ += [outputStream_ write:reinterpret_cast(&initBytes_[initSendPtr_]) - maxLength:(initBytes_.length() - initSendPtr_)]; - } - if (initialized_) { - try { - std::string dummy; - int flag = handler_(dummy, 2); - if (flag == 2) { - [self onShutdownReceived]; - } - } catch (const tvm::Error& e) { - [self close]; - } - } + disable(self.proxyURL); + disable(self.proxyPort); + disable(self.proxyKey); + self.ConnectButton.enabled = NO; + self.DisconnectButton.enabled = NO; + self.ModeSelector.enabled = NO; } +/*! + * \brief Start RPC server + */ - (void)open { - constexpr int kRPCMagic = 0xff271; - NSLog(@"Connecting to the proxy server.."); - // Initialize the data states. - key_ = [self.proxyKey.text UTF8String]; - key_ = "server:" + key_; - std::ostringstream os; - int rpc_magic = kRPCMagic; - os.write(reinterpret_cast(&rpc_magic), sizeof(rpc_magic)); - int keylen = static_cast(key_.length()); - os.write(reinterpret_cast(&keylen), sizeof(keylen)); - os.write(key_.c_str(), key_.length()); - initialized_ = false; - initBytes_ = os.str(); - initSendPtr_ = 0; - // Initialize the network. - CFReadStreamRef readStream; - CFWriteStreamRef writeStream; - CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.proxyURL.text, - [self.proxyPort.text intValue], &readStream, &writeStream); - inputStream_ = (NSInputStream*)readStream; - outputStream_ = (NSOutputStream*)writeStream; - [inputStream_ setDelegate:self]; - [outputStream_ setDelegate:self]; - [inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ open]; - [inputStream_ open]; - handler_ = tvm::runtime::CreateServerEventHandler(outputStream_, key_, "%toinit"); - ICHECK(handler_ != nullptr); + RPCServerMode server_mode = static_cast(self.ModeSelector.selectedSegmentIndex); + + server_ = [RPCServer serverWithMode:server_mode]; + [server_ setDelegate:self]; + [server_ startWithHost:self.proxyURL.text + port:self.proxyPort.text.intValue + key:self.proxyKey.text]; + + NSLog(@"Connecting to the proxy server..."); self.infoText.text = @""; self.statusLabel.text = @"Connecting..."; } +/*! + * \brief Stop RPC server + */ - (void)close { - NSLog(@"Closing the streams."); - [inputStream_ close]; - [outputStream_ close]; - [inputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [inputStream_ setDelegate:nil]; - [outputStream_ setDelegate:nil]; - inputStream_ = nil; - outputStream_ = nil; - handler_ = nullptr; - self.statusLabel.text = @"Disconnected"; + [server_ stop]; + NSLog(@"Closing the streams..."); + self.statusLabel.text = @"Disconnecting..."; } +#pragma mark - Button responders + - (IBAction)connect:(id)sender { - [self open]; [[self view] endEditing:YES]; + [self open]; } - (IBAction)disconnect:(id)sender { + [[self view] endEditing:YES]; [self close]; } + +#pragma mark - UITextFieldDelegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [[self view] endEditing:YES]; // to hide keyboard on ret key + return FALSE; +} + +- (void)textFieldDidEndEditing:(UITextField *)textField { + // Update values in app arg cache + RPCArgs args = get_current_rpc_args(); + args.host_url = [self.proxyURL.text UTF8String]; + args.host_port = [self.proxyPort.text intValue]; + args.key = [self.proxyKey.text UTF8String]; + set_current_rpc_args(args); +} + + +#pragma mark - RPCServerEvenlListener + +- (void)onError:(NSString*) msg { + dispatch_sync(dispatch_get_main_queue(), ^{ + self.infoText.text = [NSString stringWithFormat:@"Error: %@", msg]; + }); +} + +- (void)onStatusChanged:(RPCServerStatus) status { + dispatch_sync(dispatch_get_main_queue(), ^{ + switch (status) { + case RPCServerStatus_Connected: + self.statusLabel.text = @"Connected"; + break; + case RPCServerStatus_Disconnected: + self.statusLabel.text = @"Disconnected"; + break; + default: + // Nothing + break; + } + }); +} + +- (void)dealloc { + [_ModeSelector release]; + [super dealloc]; +} @end diff --git a/apps/ios_rpc/tvmrpc/main.m b/apps/ios_rpc/tvmrpc/main.m index d971ce37998b..d329dd163571 100644 --- a/apps/ios_rpc/tvmrpc/main.m +++ b/apps/ios_rpc/tvmrpc/main.m @@ -23,8 +23,10 @@ #import #import "AppDelegate.h" +#import "rpc_args.h" int main(int argc, char * argv[]) { + update_rpc_args(argc, argv); @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/apps/ios_rpc/tvmrpc/rpc_args.h b/apps/ios_rpc/tvmrpc/rpc_args.h new file mode 100644 index 000000000000..9bbf147cdadd --- /dev/null +++ b/apps/ios_rpc/tvmrpc/rpc_args.h @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef TVM_APPS_IOS_RPC_ARGS_H_ +#define TVM_APPS_IOS_RPC_ARGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \brief Struct representing arguments of iOS RPC app + */ +typedef struct RPCArgs_t { + const char* host_url; /// Tracker or Proxy address (actually ip) + int host_port; /// Tracker or Proxy port + + const char* key; /// device key to report + const char* custom_addr; /// custom adress to report into Tracker. Ignored for other server modes. + int port; /// begin of port range to try to bind RPC port + int port_end; /// end of port range to try to bind RPC port + + char immediate_connect; /// 0 - UI interaction, 1 - automatically connect on launch + char server_mode; /// 0 - connect to Tracker, 1 - connect to Proxy, 2 - pure RPC server +} RPCArgs; + + +/*! + * \brief Get current global RPC args + */ +RPCArgs get_current_rpc_args(void); + +/*! + * \brief Set current global RPC args and update values in app cache + */ +void set_current_rpc_args(RPCArgs args); + +/*! + * \brief Pars command line args and update current global RPC args + * Also update values in app cache + */ +void update_rpc_args(int argc, char * argv[]); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TVM_APPS_IOS_RPC_ARGS_H_ diff --git a/apps/ios_rpc/tvmrpc/rpc_args.mm b/apps/ios_rpc/tvmrpc/rpc_args.mm new file mode 100644 index 000000000000..5194ab8f020f --- /dev/null +++ b/apps/ios_rpc/tvmrpc/rpc_args.mm @@ -0,0 +1,221 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import "rpc_args.h" + +#import + +#import "../../../src/support/utils.h" +#import "../../../src/support/socket.h" + +#import + +using std::string; + +const char* kUsage = "\n" +"iOS tvmrpc application supported flags:\n" +"--host_url - The tracker/proxy address, Default=0.0.0.0\n" +"--host_port - The tracker/proxy port, Default=9190\n" +"--port - The port of the RPC, Default=9090\n" +"--port_end - The end search port of the RPC, Default=9099\n" +"--key - The key used to identify the device type in tracker. Default=\"\"\n" +"--custom_addr - Custom IP Address to Report to RPC Tracker. Default=\"\"\n" +"--immediate_connect - No UI interconnection, connect to tracker immediately. Default=False\n" +"--server_mode - Server mode. Can be \"pure_server\", \"proxy\" or \"tracker\". Default=pure_server \n" +"\n"; + +struct RPCArgs_cpp { + string host_url = "0.0.0.0"; + int host_port = 9190; + + string key; + string custom_addr = ""; + int port = 9090; + int port_end = 9099; + + bool immediate_connect = false; + char server_mode = 0; + + operator RPCArgs() const { + return RPCArgs { + .host_url = host_url.c_str(), + .host_port = host_port, + .key = key.c_str(), + .custom_addr = custom_addr.c_str(), + .port = port, + .port_end = port_end, + .immediate_connect = immediate_connect, + .server_mode = server_mode + }; + }; + + RPCArgs_cpp& operator=(const RPCArgs& args) { + host_url = args.host_url; + host_port = args.host_port; + key = args.key; + custom_addr = args.custom_addr; + port = args.port; + port_end = args.port_end; + immediate_connect = args.immediate_connect; + server_mode = args.server_mode; + return *this; + } +}; + +struct RPCArgs_cpp g_rpc_args; + +static void restore_from_cache() { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + auto get_string_from_cache = [defaults] (const char* key) { + NSString* ns_key = [NSString stringWithUTF8String:key]; + NSString* ns_val = [defaults stringForKey:ns_key]; + return std::string(ns_val != nil ? [ns_val UTF8String] : ""); + }; + + auto get_int_from_cache = [defaults] (const char* key) { + NSString* ns_key = [NSString stringWithUTF8String:key]; + return static_cast([defaults integerForKey:ns_key]); + }; + + g_rpc_args.host_url = get_string_from_cache("tmvrpc_url"); + g_rpc_args.host_port = get_int_from_cache("tmvrpc_port"); + g_rpc_args.key = get_string_from_cache("tmvrpc_key"); +} + +static void update_in_cache() { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + [defaults setObject:[NSString stringWithUTF8String:g_rpc_args.host_url.c_str()] forKey:@"tmvrpc_url"]; + [defaults setInteger:g_rpc_args.host_port forKey:@"tmvrpc_port"]; + [defaults setObject:[NSString stringWithUTF8String:g_rpc_args.key.c_str()] forKey:@"tmvrpc_key"]; +} + +string GetCmdOption(int argc, char* argv[], string option, bool key = false) { + string cmd; + for (int i = 1; i < argc; ++i) { + string arg = argv[i]; + if (arg.find(option) == 0) { + if (key) { + cmd = argv[i]; + return cmd; + } + // We assume "=" is the end of option. + ICHECK_EQ(*option.rbegin(), '='); + cmd = arg.substr(arg.find('=') + 1); + return cmd; + } + } + return cmd; +} + +void update_rpc_args(int argc, char* argv[]) { + restore_from_cache(); + RPCArgs_cpp &args = g_rpc_args; + + using tvm::support::IsNumber; + using tvm::support::ValidateIP; + constexpr int MAX_PORT_NUM = 65535; + + const string immediate_connect = GetCmdOption(argc, argv, "--immediate_connect", true); + if (!immediate_connect.empty()) { + args.immediate_connect = true; + } + + const string server_mode = GetCmdOption(argc, argv, "--server_mode=", false); + if (!server_mode.empty()) { + if (server_mode == "tracker") { + args.server_mode = 0; + } else if (server_mode == "proxy") { + args.server_mode = 1; + } else if (server_mode == "pure_server") { + args.server_mode = 2; + } else { + LOG(WARNING) << "Wrong server_mode value."; + LOG(INFO) << kUsage; + exit(1); + } + } + + const string host_url = GetCmdOption(argc, argv, "--host_url="); + if (!host_url.empty()) { + if (!ValidateIP(host_url)) { + LOG(WARNING) << "Wrong tracker address format."; + LOG(INFO) << kUsage; + exit(1); + } + args.host_url = host_url; + } + + const string host_port = GetCmdOption(argc, argv, "--host_port="); + if (!host_port.empty()) { + if (!IsNumber(host_port) || stoi(host_port) > MAX_PORT_NUM) { + LOG(WARNING) << "Wrong trackerport number."; + LOG(INFO) << kUsage; + exit(1); + } + args.host_port = stoi(host_port); + } + + const string port = GetCmdOption(argc, argv, "--port="); + if (!port.empty()) { + if (!IsNumber(port) || stoi(port) > MAX_PORT_NUM) { + LOG(WARNING) << "Wrong port number."; + LOG(INFO) << kUsage; + exit(1); + } + args.port = stoi(port); + } + + const string port_end = GetCmdOption(argc, argv, "--port_end="); + if (!port_end.empty()) { + if (!IsNumber(port_end) || stoi(port_end) > MAX_PORT_NUM) { + LOG(WARNING) << "Wrong port_end number."; + LOG(INFO) << kUsage; + exit(1); + } + args.port_end = stoi(port_end); + } + + const string key = GetCmdOption(argc, argv, "--key="); + if (!key.empty()) { + args.key = key; + } + + const string custom_addr = GetCmdOption(argc, argv, "--custom_addr="); + if (!custom_addr.empty()) { + if (!ValidateIP(custom_addr)) { + LOG(WARNING) << "Wrong custom address format."; + LOG(INFO) << kUsage; + exit(1); + } + args.custom_addr = '"' + custom_addr + '"'; + } + + update_in_cache(); +} + +RPCArgs get_current_rpc_args(void) { + return g_rpc_args; +} + +void set_current_rpc_args(RPCArgs args) { + g_rpc_args = args; + update_in_cache(); +} diff --git a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm index eb538f07bf49..85e60a324b18 100644 --- a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm +++ b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm @@ -23,7 +23,8 @@ */ #import -#import "TVMRuntime.h" +#import "rpc_args.h" +#import "RPCServer.h" @interface tvmrpcLauncher : XCTestCase @@ -31,16 +32,18 @@ @interface tvmrpcLauncher : XCTestCase @implementation tvmrpcLauncher -- (void)setUp { - [super setUp]; -} +- (void)testRPC { + RPCArgs args = get_current_rpc_args(); + RPCServerMode server_mode = args.server_mode == 0 ? RPCServerMode_Tracker : + args.server_mode == 1 ? RPCServerMode_Proxy : + RPCServerMode_PureServer; -- (void)tearDown { - [super tearDown]; -} + RPCServer* server_ = [RPCServer serverWithMode:server_mode]; -- (void)testRPC { - [TVMRuntime launchSyncServer]; + RPCServer* server = [RPCServer serverWithMode:server_mode]; + [server startWithHost:@(args.host_url) + port:args.host_port + key:@(args.key)]; } @end From 72a364b514315a8aec27df5a4795850de443f020 Mon Sep 17 00:00:00 2001 From: Alexander Peskov Date: Mon, 31 May 2021 18:04:53 +0300 Subject: [PATCH 05/23] [IOS-RPC] lint fix Signed-off-by: Alexander Peskov --- apps/ios_rpc/tvmrpc/RPCServer.h | 19 +- apps/ios_rpc/tvmrpc/RPCServer.mm | 204 +++++++++--------- apps/ios_rpc/tvmrpc/ViewController.h | 8 +- apps/ios_rpc/tvmrpc/ViewController.mm | 16 +- apps/ios_rpc/tvmrpc/rpc_args.h | 22 +- apps/ios_rpc/tvmrpc/rpc_args.mm | 83 ++++--- apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm | 10 +- 7 files changed, 177 insertions(+), 185 deletions(-) diff --git a/apps/ios_rpc/tvmrpc/RPCServer.h b/apps/ios_rpc/tvmrpc/RPCServer.h index 1ad9b1f71c36..1101119a85cc 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.h +++ b/apps/ios_rpc/tvmrpc/RPCServer.h @@ -28,10 +28,10 @@ * Used to report state to listener */ typedef enum { - RPCServerStatus_Launched, // Worker thread is launched - RPCServerStatus_Stopped, // Worker thread stopped - RPCServerStatus_Connected, // Connected to Proxy/Tracker - RPCServerStatus_Disconnected, // Disconnected from Proxy/Tracker + RPCServerStatus_Launched, // Worker thread is launched + RPCServerStatus_Stopped, // Worker thread stopped + RPCServerStatus_Connected, // Connected to Proxy/Tracker + RPCServerStatus_Disconnected, // Disconnected from Proxy/Tracker RPCServerStatus_RPCSessionStarted, // RPC session is started RPCServerStatus_RPCSessionFinished // RPC session is finished } RPCServerStatus; @@ -53,12 +53,11 @@ typedef enum { */ @protocol RPCServerEventListener /// Callback to notifying about new status -- (void)onError:(NSString*) msg; +- (void)onError:(NSString*)msg; /// Callback to notifying about error -- (void)onStatusChanged:(RPCServerStatus) status; +- (void)onStatusChanged:(RPCServerStatus)status; @end - /*! * \brief RPC Server instance * Contains internal worker thread plus @@ -69,13 +68,13 @@ typedef enum { * \brief Create server with specified sevicing mode * \param mode Mode of server */ -+ (instancetype)serverWithMode:(RPCServerMode) mode; ++ (instancetype)serverWithMode:(RPCServerMode)mode; /*! * \brief Set listener delegate * \param delegate Event listener delegate to set */ -- (void)setDelegate:(id) delegate; +- (void)setDelegate:(id)delegate; /*! * \brief Start RPC server with options. Non blocking method @@ -83,7 +82,7 @@ typedef enum { * \param port Port of Tracker/Proxy to connect. Skipped for PureServer mode * \param key device key to use in RPC server */ -- (void)startWithHost:(NSString*) host port:(int) port key:(NSString*) key; +- (void)startWithHost:(NSString*)host port:(int)port key:(NSString*)key; /*! * \brief Stop RPC server. Non blocking method diff --git a/apps/ios_rpc/tvmrpc/RPCServer.mm b/apps/ios_rpc/tvmrpc/RPCServer.mm index 0e95b47e6470..ab55346f40cb 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.mm +++ b/apps/ios_rpc/tvmrpc/RPCServer.mm @@ -26,14 +26,14 @@ #include #include -#include #include +#include // To get device WiFi IP +#include #include -#include #include -#include +#include // TVM internal header to access Magic keys like kRPCMagic and others #include "../../../src/runtime/rpc/rpc_endpoint.h" @@ -65,12 +65,13 @@ FEventHandler CreateServerEventHandler(NSOutputStream* outputStream, std::string std::string remote_key) { const PackedFunc* event_handler_factor = Registry::Get("rpc.CreateEventDrivenServer"); ICHECK(event_handler_factor != nullptr) - << "You are using tvm_runtime module built without RPC support. " - << "Please rebuild it with USE_RPC flag."; + << "You are using tvm_runtime module built without RPC support. " + << "Please rebuild it with USE_RPC flag."; PackedFunc writer_func([outputStream](TVMArgs args, TVMRetValue* rv) { - TVMByteArray *data = args[0].ptr(); - int64_t nbytes = [outputStream write:reinterpret_cast(data->data) maxLength:data->size]; + TVMByteArray* data = args[0].ptr(); + int64_t nbytes = [outputStream write:reinterpret_cast(data->data) + maxLength:data->size]; if (nbytes < 0) { NSLog(@"%@", [outputStream streamError].localizedDescription); throw tvm::Error("Stream error"); @@ -87,21 +88,21 @@ PackedFunc writer_func([outputStream](TVMArgs args, TVMRetValue* rv) { */ static std::string getWiFiAddress() { std::string address = "unknown"; - ifaddrs *interfaces = nullptr; + ifaddrs* interfaces = nullptr; int success = getifaddrs(&interfaces); if (success == 0) { - // Loop through linked list of interfaces - ifaddrs *temp_addr = interfaces; - while(temp_addr != NULL) { - if(temp_addr->ifa_addr->sa_family == AF_INET) { - // Check if interface is en0 which is the wifi connection on the iPhone - if(std::string(temp_addr->ifa_name) == "en0") { - address = std::string(inet_ntoa(((sockaddr_in *)temp_addr->ifa_addr)->sin_addr)); - } - } - temp_addr = temp_addr->ifa_next; + // Loop through linked list of interfaces + ifaddrs* temp_addr = interfaces; + while (temp_addr != NULL) { + if (temp_addr->ifa_addr->sa_family == AF_INET) { + // Check if interface is en0 which is the wifi connection on the iPhone + if (std::string(temp_addr->ifa_name) == "en0") { + address = std::string(inet_ntoa(((sockaddr_in*)temp_addr->ifa_addr)->sin_addr)); + } } + temp_addr = temp_addr->ifa_next; + } } freeifaddrs(interfaces); @@ -111,9 +112,8 @@ PackedFunc writer_func([outputStream](TVMArgs args, TVMRetValue* rv) { } // namespace runtime } // namespace tvm - // Forward declaration -@interface RPCTrackerClien : RPCServer +@interface RPCTrackerClien : RPCServer @end // Forward declaration @@ -125,7 +125,7 @@ @interface RPCServerPure : RPCServer * Will automatically connect/reconnect to RPCProxy server */ @implementation RPCServer { -@protected + @protected // The key of the server. NSString* key_; // The url of host. @@ -133,7 +133,7 @@ @implementation RPCServer { // The port of host. NSInteger port_; // Event listener - id delegate_; + id delegate_; // Worker thread NSThread* worker_thread_; // Triger to continue processing @@ -158,13 +158,10 @@ @implementation RPCServer { tvm::runtime::FEventHandler handler_; } -+ (instancetype)serverWithMode:(RPCServerMode) mode { - if (mode == RPCServerMode_PureServer) - return [[RPCServerPure alloc] init]; - if (mode == RPCServerMode_Proxy) - return [[RPCServer alloc] init]; - if (mode == RPCServerMode_Tracker) - return [[RPCTrackerClien alloc] init]; ++ (instancetype)serverWithMode:(RPCServerMode)mode { + if (mode == RPCServerMode_PureServer) return [[RPCServerPure alloc] init]; + if (mode == RPCServerMode_Proxy) return [[RPCServer alloc] init]; + if (mode == RPCServerMode_Tracker) return [[RPCTrackerClien alloc] init]; return nil; } @@ -177,10 +174,18 @@ - (instancetype)init { /*! * Internal setters methods */ -- (void)setDelegate:(id) delegate { delegate_ = delegate; } -- (void)setKey:(NSString*) key { key_ = key; } -- (void)setUrl:(NSString*) url { url_ = url; } -- (void)setPort:(NSInteger) port { port_ = port; } +- (void)setDelegate:(id)delegate { + delegate_ = delegate; +} +- (void)setKey:(NSString*)key { + key_ = key; +} +- (void)setUrl:(NSString*)url { + url_ = url; +} +- (void)setPort:(NSInteger)port { + port_ = port; +} /*! * \brief Main event listener method. All stream event handling starts here @@ -216,13 +221,12 @@ - (void)stream:(NSStream*)strm handleEvent:(NSStreamEvent)event { } } --(void)notifyError:(NSString*) msg { +- (void)notifyError:(NSString*)msg { NSLog(@"[IOS-RPC] ERROR: %@", msg); - if (delegate_ != nil) - [delegate_ onError:msg]; + if (delegate_ != nil) [delegate_ onError:msg]; } --(void)notifyState:(RPCServerStatus) state { +- (void)notifyState:(RPCServerStatus)state { if (state == RPCServerStatus_Launched) { // Notify host runner script with actual address NSLog(@"[IOS-RPC] IP: %s", server_ip_.c_str()); @@ -231,8 +235,7 @@ -(void)notifyState:(RPCServerStatus) state { // Notify host runner script with current status NSLog(@"[IOS-RPC] STATE: %d", state); // Notify listener - if (delegate_ != nil) - [delegate_ onStatusChanged:state]; + if (delegate_ != nil) [delegate_ onStatusChanged:state]; } - (void)onReadAvailable { @@ -270,7 +273,7 @@ - (void)onReadAvailable { } // always try to write try { - TVMByteArray arr {recvBuffer_.data(), recvBuffer_.size()}; + TVMByteArray arr{recvBuffer_.data(), recvBuffer_.size()}; flag = handler_(arr, flag); if (flag == 2) { [self onShutdownReceived]; @@ -290,7 +293,7 @@ - (void)onWriteAvailable { [self notifyState:RPCServerStatus_Connected]; if (initialized_) { try { - TVMByteArray dummy {nullptr, 0}; + TVMByteArray dummy{nullptr, 0}; int flag = handler_(dummy, 2); if (flag == 2) { [self onShutdownReceived]; @@ -324,12 +327,11 @@ - (void)open { initialized_ = false; initBytes_ = os.str(); initSendPtr_ = 0; - + // Initialize the network. CFReadStreamRef readStream; CFWriteStreamRef writeStream; - CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)url_, port_, - &readStream, &writeStream); + CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)url_, port_, &readStream, &writeStream); inputStream_ = (NSInputStream*)readStream; outputStream_ = (NSOutputStream*)writeStream; [inputStream_ setDelegate:self]; @@ -356,18 +358,20 @@ - (void)close { [self notifyState:RPCServerStatus_Disconnected]; } -- (void)startWithHost:(NSString*) host port: (int) port key:(NSString*) key { +- (void)startWithHost:(NSString*)host port:(int)port key:(NSString*)key { key_ = [key copy]; port_ = port; url_ = [host copy]; - + // process in separate thead with runloop worker_thread_ = [[NSThread alloc] initWithBlock:^{ @autoreleasepool { [self open]; [self notifyState:RPCServerStatus_Launched]; shouldKeepRunning = YES; - while (shouldKeepRunning && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + while (shouldKeepRunning && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]) + ; [self notifyState:RPCServerStatus_Stopped]; } }]; @@ -375,8 +379,7 @@ - (void)startWithHost:(NSString*) host port: (int) port key:(NSString*) key { } - (void)stop { - if (worker_thread_ == nil) - return; + if (worker_thread_ == nil) return; [self performSelector:@selector(stop_) onThread:worker_thread_ withObject:nil waitUntilDone:NO]; worker_thread_ = nil; @@ -394,9 +397,9 @@ @implementation RPCServerPure { CFSocketNativeHandle socket_; } -- (void)handleConnect:(const CFSocketNativeHandle) hdl { +- (void)handleConnect:(const CFSocketNativeHandle)hdl { socket_ = hdl; - + // Initialize the data states. std::string full_key = std::string("server:") + [key_ UTF8String]; std::ostringstream os; @@ -408,7 +411,7 @@ - (void)handleConnect:(const CFSocketNativeHandle) hdl { initialized_ = false; initBytes_ = os.str(); initSendPtr_ = 0; - + // Initialize the network. CFReadStreamRef readStream; CFWriteStreamRef writeStream; @@ -427,18 +430,18 @@ - (void)handleConnect:(const CFSocketNativeHandle) hdl { ICHECK(handler_ != nullptr); } -static void handleConnect(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { +static void handleConnect(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, + const void* data, void* info) { RPCServerPure* it = static_cast(info); [it handleConnect:*static_cast(data)]; } - (void)open { - CFSocketContext ctx {}; + CFSocketContext ctx{}; ctx.info = self; - CFSocketRef myipv4cfsock = CFSocketCreate( - kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, - kCFSocketAcceptCallBack, handleConnect, &ctx); + CFSocketRef myipv4cfsock = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, + kCFSocketAcceptCallBack, handleConnect, &ctx); struct sockaddr_in sin; int rpc_port = 9090; // TODO: hardcoded. Should try bind in range of ports @@ -446,21 +449,20 @@ - (void)open { sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_port = htons(rpc_port); - sin.sin_addr.s_addr= INADDR_ANY; + sin.sin_addr.s_addr = INADDR_ANY; - CFDataRef sincfd = CFDataCreate( - kCFAllocatorDefault, - (UInt8 *)&sin, - sizeof(sin)); + CFDataRef sincfd = CFDataCreate(kCFAllocatorDefault, (UInt8*)&sin, sizeof(sin)); if (CFSocketSetAddress(myipv4cfsock, sincfd) != 0) - @throw [NSException exceptionWithName:@"SocketError" - reason:[NSString stringWithFormat:@"Can not bind to port %d", rpc_port] - userInfo:nil]; + @throw [NSException + exceptionWithName:@"SocketError" + reason:[NSString stringWithFormat:@"Can not bind to port %d", rpc_port] + userInfo:nil]; CFRelease(sincfd); server_port_ = rpc_port; - CFRunLoopSourceRef socketsource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, myipv4cfsock, 0); + CFRunLoopSourceRef socketsource = + CFSocketCreateRunLoopSource(kCFAllocatorDefault, myipv4cfsock, 0); CFRunLoopAddSource(CFRunLoopGetCurrent(), socketsource, kCFRunLoopDefaultMode); } @@ -492,9 +494,9 @@ @implementation RPCTrackerClien { TrackerClientState state_; } -- (void)toSend:(NSData*) data { +- (void)toSend:(NSData*)data { // try to send - NSInteger sent_size = [outputStream_ write:(uint8_t*)data.bytes maxLength:data.length]; + NSInteger sent_size = [outputStream_ write:(uint8_t*)data.bytes maxLength:data.length]; // assume that all data is sent if (sent_size != data.length) @throw [NSException exceptionWithName:@"SocketError" @@ -502,16 +504,16 @@ - (void)toSend:(NSData*) data { userInfo:nil]; } -- (void)toSendPacked:(NSData*) data { +- (void)toSendPacked:(NSData*)data { // try to send int packet_size = data.length; - NSInteger sent_size = [outputStream_ write:(uint8_t*)&packet_size maxLength:sizeof(packet_size)]; + NSInteger sent_size = [outputStream_ write:(uint8_t*)&packet_size maxLength:sizeof(packet_size)]; if (sent_size != sizeof(packet_size)) @throw [NSException exceptionWithName:@"SocketError" reason:[NSString stringWithFormat:@"Unable to send data"] userInfo:nil]; - - NSInteger sent_data = [outputStream_ write:(uint8_t*)data.bytes maxLength:data.length]; + + NSInteger sent_data = [outputStream_ write:(uint8_t*)data.bytes maxLength:data.length]; // assume that all data is sent if (sent_data != data.length) @throw [NSException exceptionWithName:@"SocketError" @@ -526,10 +528,9 @@ - (void)onReadAvailable { size_t nbytes = [inputStream_ read:(uint8_t*)recvBuffer_.data() + prev_size maxLength:recvBuffer_.size() - prev_size]; recvBuffer_.resize(nbytes + prev_size); - - if (recvBuffer_.size() < required_data_size) - return; - + + if (recvBuffer_.size() < required_data_size) return; + switch (state_) { case HandshakeToRecv: { int code = tvm::runtime::kRPCTrackerMagic; @@ -537,13 +538,13 @@ - (void)onReadAvailable { required_data_size = sizeof(code); break; } - + if (recvBuffer_.size() != sizeof(code) || *(int*)recvBuffer_.data() != code) { [self notifyError:@"Wrong responce, server is not tracker."]; [self close]; break; } - + recvBuffer_.erase(recvBuffer_.begin(), recvBuffer_.begin() + sizeof(code)); required_data_size = 0; [self notifyState:RPCServerStatus_Connected]; @@ -557,7 +558,7 @@ - (void)onReadAvailable { required_data_size = sizeof(packet_size); break; } - + packet_size = *(int*)recvBuffer_.data(); if (recvBuffer_.size() < sizeof(packet_size) + packet_size) { required_data_size = sizeof(packet_size) + packet_size; @@ -569,8 +570,9 @@ - (void)onReadAvailable { [self close]; break; } - - recvBuffer_.erase(recvBuffer_.begin(), recvBuffer_.begin() + sizeof(packet_size) + packet_size); + + recvBuffer_.erase(recvBuffer_.begin(), + recvBuffer_.begin() + sizeof(packet_size) + packet_size); required_data_size = 0; state_ = ReportResToSend; break; @@ -582,7 +584,7 @@ - (void)onReadAvailable { required_data_size = sizeof(packet_size); break; } - + packet_size = *(int*)recvBuffer_.data(); if (recvBuffer_.size() < sizeof(packet_size) + packet_size) { required_data_size = sizeof(packet_size) + packet_size; @@ -594,8 +596,9 @@ - (void)onReadAvailable { [self close]; break; } - - recvBuffer_.erase(recvBuffer_.begin(), recvBuffer_.begin() + sizeof(packet_size) + packet_size); + + recvBuffer_.erase(recvBuffer_.begin(), + recvBuffer_.begin() + sizeof(packet_size) + packet_size); required_data_size = 0; state_ = WaitConnection; break; @@ -605,8 +608,7 @@ - (void)onReadAvailable { break; } - if (outputStream_.hasSpaceAvailable) - [self onWriteAvailable]; + if (outputStream_.hasSpaceAvailable) [self onWriteAvailable]; } - (void)onWriteAvailable { @@ -620,8 +622,8 @@ - (void)onWriteAvailable { } case ServerInfoToSend: { std::ostringstream ss; - ss << "[" << static_cast(tvm::runtime::TrackerCode::kUpdateInfo) << ", {\"key\": \"server:" << key_.UTF8String - << "\"}]"; + ss << "[" << static_cast(tvm::runtime::TrackerCode::kUpdateInfo) + << ", {\"key\": \"server:" << key_.UTF8String << "\"}]"; std::string data_s = ss.str(); NSData* data = [NSData dataWithBytes:data_s.data() length:data_s.length()]; [self toSendPacked:data]; @@ -631,15 +633,15 @@ - (void)onWriteAvailable { case ReportResToSend: { std::mt19937 gen(std::random_device{}()); std::uniform_real_distribution dis(0.0, 1.0); - + // TODO: All values are hardcoded int port = 9090; std::string custom_addr = "null"; std::string matchkey = std::string(key_.UTF8String) + ":" + std::to_string(dis(gen)); - + std::ostringstream ss; - ss << "[" << static_cast(tvm::runtime::TrackerCode::kPut) << ", \"" << key_.UTF8String << "\", [" << port - << ", \"" << matchkey << "\"], " << custom_addr << "]"; + ss << "[" << static_cast(tvm::runtime::TrackerCode::kPut) << ", \"" << key_.UTF8String + << "\", [" << port << ", \"" << matchkey << "\"], " << custom_addr << "]"; std::string data_s = ss.str(); NSData* data = [NSData dataWithBytes:data_s.data() length:data_s.length()]; @@ -651,12 +653,11 @@ - (void)onWriteAvailable { // Nothing break; } - - if (inputStream_.hasBytesAvailable) - [self onReadAvailable]; + + if (inputStream_.hasBytesAvailable) [self onReadAvailable]; } --(void)open { +- (void)open { // create RPC pure server // * set self as delegate (to register back when servicing is finished) // * mute printing status @@ -665,14 +666,13 @@ -(void)open { rpc_server_.key = key_; rpc_server_.delegate = self; [rpc_server_ open]; - + // Initialize the network. CFReadStreamRef readStream; CFWriteStreamRef writeStream; - CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)url_, port_, - &readStream, &writeStream); - + CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)url_, port_, &readStream, &writeStream); + inputStream_ = (NSInputStream*)readStream; outputStream_ = (NSOutputStream*)writeStream; [inputStream_ setDelegate:self]; @@ -693,12 +693,12 @@ - (void)onEndEvent { [self close]; } -- (void)onError:(NSString*) msg { +- (void)onError:(NSString*)msg { // transfer error form rpc_server_ to real delegate [self notifyError:msg]; } -- (void)onStatusChanged:(RPCServerStatus) status { +- (void)onStatusChanged:(RPCServerStatus)status { if (status == RPCServerStatus_RPCSessionFinished) { [self notifyState:status]; state_ = ReportResToSend; diff --git a/apps/ios_rpc/tvmrpc/ViewController.h b/apps/ios_rpc/tvmrpc/ViewController.h index 7f664074857e..f634e60f959b 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.h +++ b/apps/ios_rpc/tvmrpc/ViewController.h @@ -25,7 +25,7 @@ #import "RPCServer.h" @interface ViewController : UIViewController { - RPCServer* server_; + RPCServer* server_; } @property(weak, nonatomic) IBOutlet UITextField* proxyURL; @@ -36,8 +36,8 @@ - (IBAction)connect:(id)sender; - (IBAction)disconnect:(id)sender; -@property (retain, nonatomic) IBOutlet UIButton *DisconnectButton; -@property (retain, nonatomic) IBOutlet UIButton *ConnectButton; -@property (retain, nonatomic) IBOutlet UISegmentedControl *ModeSelector; +@property(retain, nonatomic) IBOutlet UIButton* DisconnectButton; +@property(retain, nonatomic) IBOutlet UIButton* ConnectButton; +@property(retain, nonatomic) IBOutlet UISegmentedControl* ModeSelector; @end diff --git a/apps/ios_rpc/tvmrpc/ViewController.mm b/apps/ios_rpc/tvmrpc/ViewController.mm index e136fb08ee00..d5b0149ce0e3 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.mm +++ b/apps/ios_rpc/tvmrpc/ViewController.mm @@ -36,9 +36,9 @@ - (void)viewDidLoad { self.proxyURL.text = @(args.host_url); self.proxyPort.text = @(args.host_port).stringValue; self.proxyKey.text = @(args.key); - + self.ModeSelector.selectedSegmentIndex = args.server_mode; - + // Connect to tracker immediately if (args.immediate_connect) { [self disableUIInteraction]; @@ -74,7 +74,7 @@ - (void)open { [server_ startWithHost:self.proxyURL.text port:self.proxyPort.text.intValue key:self.proxyKey.text]; - + NSLog(@"Connecting to the proxy server..."); self.infoText.text = @""; self.statusLabel.text = @"Connecting..."; @@ -101,15 +101,14 @@ - (IBAction)disconnect:(id)sender { [self close]; } - #pragma mark - UITextFieldDelegate -- (BOOL)textFieldShouldReturn:(UITextField *)textField { +- (BOOL)textFieldShouldReturn:(UITextField*)textField { [[self view] endEditing:YES]; // to hide keyboard on ret key return FALSE; } -- (void)textFieldDidEndEditing:(UITextField *)textField { +- (void)textFieldDidEndEditing:(UITextField*)textField { // Update values in app arg cache RPCArgs args = get_current_rpc_args(); args.host_url = [self.proxyURL.text UTF8String]; @@ -118,16 +117,15 @@ - (void)textFieldDidEndEditing:(UITextField *)textField { set_current_rpc_args(args); } - #pragma mark - RPCServerEvenlListener -- (void)onError:(NSString*) msg { +- (void)onError:(NSString*)msg { dispatch_sync(dispatch_get_main_queue(), ^{ self.infoText.text = [NSString stringWithFormat:@"Error: %@", msg]; }); } -- (void)onStatusChanged:(RPCServerStatus) status { +- (void)onStatusChanged:(RPCServerStatus)status { dispatch_sync(dispatch_get_main_queue(), ^{ switch (status) { case RPCServerStatus_Connected: diff --git a/apps/ios_rpc/tvmrpc/rpc_args.h b/apps/ios_rpc/tvmrpc/rpc_args.h index 9bbf147cdadd..e174da27f775 100644 --- a/apps/ios_rpc/tvmrpc/rpc_args.h +++ b/apps/ios_rpc/tvmrpc/rpc_args.h @@ -29,18 +29,18 @@ extern "C" { */ typedef struct RPCArgs_t { const char* host_url; /// Tracker or Proxy address (actually ip) - int host_port; /// Tracker or Proxy port - + int host_port; /// Tracker or Proxy port + const char* key; /// device key to report - const char* custom_addr; /// custom adress to report into Tracker. Ignored for other server modes. - int port; /// begin of port range to try to bind RPC port - int port_end; /// end of port range to try to bind RPC port - + const char* + custom_addr; /// custom adress to report into Tracker. Ignored for other server modes. + int port; /// begin of port range to try to bind RPC port + int port_end; /// end of port range to try to bind RPC port + char immediate_connect; /// 0 - UI interaction, 1 - automatically connect on launch - char server_mode; /// 0 - connect to Tracker, 1 - connect to Proxy, 2 - pure RPC server + char server_mode; /// 0 - connect to Tracker, 1 - connect to Proxy, 2 - pure RPC server } RPCArgs; - /*! * \brief Get current global RPC args */ @@ -55,10 +55,10 @@ void set_current_rpc_args(RPCArgs args); * \brief Pars command line args and update current global RPC args * Also update values in app cache */ -void update_rpc_args(int argc, char * argv[]); +void update_rpc_args(int argc, char* argv[]); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif -#endif // TVM_APPS_IOS_RPC_ARGS_H_ +#endif // TVM_APPS_IOS_RPC_ARGS_H_ diff --git a/apps/ios_rpc/tvmrpc/rpc_args.mm b/apps/ios_rpc/tvmrpc/rpc_args.mm index 5194ab8f020f..38ddf0488a00 100644 --- a/apps/ios_rpc/tvmrpc/rpc_args.mm +++ b/apps/ios_rpc/tvmrpc/rpc_args.mm @@ -21,50 +21,50 @@ #import -#import "../../../src/support/utils.h" #import "../../../src/support/socket.h" +#import "../../../src/support/utils.h" #import using std::string; -const char* kUsage = "\n" -"iOS tvmrpc application supported flags:\n" -"--host_url - The tracker/proxy address, Default=0.0.0.0\n" -"--host_port - The tracker/proxy port, Default=9190\n" -"--port - The port of the RPC, Default=9090\n" -"--port_end - The end search port of the RPC, Default=9099\n" -"--key - The key used to identify the device type in tracker. Default=\"\"\n" -"--custom_addr - Custom IP Address to Report to RPC Tracker. Default=\"\"\n" -"--immediate_connect - No UI interconnection, connect to tracker immediately. Default=False\n" -"--server_mode - Server mode. Can be \"pure_server\", \"proxy\" or \"tracker\". Default=pure_server \n" -"\n"; +const char* kUsage = + "\n" + "iOS tvmrpc application supported flags:\n" + "--host_url - The tracker/proxy address, Default=0.0.0.0\n" + "--host_port - The tracker/proxy port, Default=9190\n" + "--port - The port of the RPC, Default=9090\n" + "--port_end - The end search port of the RPC, Default=9099\n" + "--key - The key used to identify the device type in tracker. Default=\"\"\n" + "--custom_addr - Custom IP Address to Report to RPC Tracker. Default=\"\"\n" + "--immediate_connect - No UI interconnection, connect to tracker immediately. Default=False\n" + "--server_mode - Server mode. Can be \"pure_server\", \"proxy\" or \"tracker\". " + "Default=pure_server \n" + "\n"; struct RPCArgs_cpp { string host_url = "0.0.0.0"; int host_port = 9190; - + string key; string custom_addr = ""; int port = 9090; int port_end = 9099; - + bool immediate_connect = false; char server_mode = 0; - + operator RPCArgs() const { - return RPCArgs { - .host_url = host_url.c_str(), - .host_port = host_port, - .key = key.c_str(), - .custom_addr = custom_addr.c_str(), - .port = port, - .port_end = port_end, - .immediate_connect = immediate_connect, - .server_mode = server_mode - }; + return RPCArgs{.host_url = host_url.c_str(), + .host_port = host_port, + .key = key.c_str(), + .custom_addr = custom_addr.c_str(), + .port = port, + .port_end = port_end, + .immediate_connect = immediate_connect, + .server_mode = server_mode}; }; - + RPCArgs_cpp& operator=(const RPCArgs& args) { host_url = args.host_url; host_port = args.host_port; @@ -81,28 +81,29 @@ operator RPCArgs() const { struct RPCArgs_cpp g_rpc_args; static void restore_from_cache() { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - auto get_string_from_cache = [defaults] (const char* key) { + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + + auto get_string_from_cache = [defaults](const char* key) { NSString* ns_key = [NSString stringWithUTF8String:key]; NSString* ns_val = [defaults stringForKey:ns_key]; return std::string(ns_val != nil ? [ns_val UTF8String] : ""); }; - - auto get_int_from_cache = [defaults] (const char* key) { + + auto get_int_from_cache = [defaults](const char* key) { NSString* ns_key = [NSString stringWithUTF8String:key]; return static_cast([defaults integerForKey:ns_key]); }; - + g_rpc_args.host_url = get_string_from_cache("tmvrpc_url"); g_rpc_args.host_port = get_int_from_cache("tmvrpc_port"); g_rpc_args.key = get_string_from_cache("tmvrpc_key"); } static void update_in_cache() { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - [defaults setObject:[NSString stringWithUTF8String:g_rpc_args.host_url.c_str()] forKey:@"tmvrpc_url"]; + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + + [defaults setObject:[NSString stringWithUTF8String:g_rpc_args.host_url.c_str()] + forKey:@"tmvrpc_url"]; [defaults setInteger:g_rpc_args.host_port forKey:@"tmvrpc_port"]; [defaults setObject:[NSString stringWithUTF8String:g_rpc_args.key.c_str()] forKey:@"tmvrpc_key"]; } @@ -127,12 +128,12 @@ string GetCmdOption(int argc, char* argv[], string option, bool key = false) { void update_rpc_args(int argc, char* argv[]) { restore_from_cache(); - RPCArgs_cpp &args = g_rpc_args; - + RPCArgs_cpp& args = g_rpc_args; + using tvm::support::IsNumber; using tvm::support::ValidateIP; constexpr int MAX_PORT_NUM = 65535; - + const string immediate_connect = GetCmdOption(argc, argv, "--immediate_connect", true); if (!immediate_connect.empty()) { args.immediate_connect = true; @@ -207,13 +208,11 @@ void update_rpc_args(int argc, char* argv[]) { } args.custom_addr = '"' + custom_addr + '"'; } - + update_in_cache(); } -RPCArgs get_current_rpc_args(void) { - return g_rpc_args; -} +RPCArgs get_current_rpc_args(void) { return g_rpc_args; } void set_current_rpc_args(RPCArgs args) { g_rpc_args = args; diff --git a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm index 85e60a324b18..25c08cf43fd2 100644 --- a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm +++ b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm @@ -23,8 +23,8 @@ */ #import -#import "rpc_args.h" #import "RPCServer.h" +#import "rpc_args.h" @interface tvmrpcLauncher : XCTestCase @@ -34,16 +34,12 @@ @implementation tvmrpcLauncher - (void)testRPC { RPCArgs args = get_current_rpc_args(); - RPCServerMode server_mode = args.server_mode == 0 ? RPCServerMode_Tracker : - args.server_mode == 1 ? RPCServerMode_Proxy : - RPCServerMode_PureServer; + RPCServerMode server_mode = static_cast(args.server_mode); RPCServer* server_ = [RPCServer serverWithMode:server_mode]; RPCServer* server = [RPCServer serverWithMode:server_mode]; - [server startWithHost:@(args.host_url) - port:args.host_port - key:@(args.key)]; + [server startWithHost:@(args.host_url) port:args.host_port key:@(args.key)]; } @end From cb3c00cff1dc30e04c8ffa8f8c072893afa54d64 Mon Sep 17 00:00:00 2001 From: Alexander Peskov Date: Thu, 3 Jun 2021 16:43:49 +0300 Subject: [PATCH 06/23] [IOS-RPC] Uniform servers Also: - Disabled bit-code - Enabled ARC - Use custom DSO loader by default - Single button to connect/disconnect - Add verbose flag Signed-off-by: Alexander Peskov --- apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj | 20 +- .../AppIcon.appiconset/Contents.json | 79 +- .../ios_rpc/tvmrpc/Base.lproj/Main.storyboard | 33 +- apps/ios_rpc/tvmrpc/{rpc_args.h => RPCArgs.h} | 32 +- .../tvmrpc/{rpc_args.mm => RPCArgs.mm} | 53 +- apps/ios_rpc/tvmrpc/RPCServer.h | 22 +- apps/ios_rpc/tvmrpc/RPCServer.mm | 940 ++++++++++-------- apps/ios_rpc/tvmrpc/TVMRuntime.mm | 2 +- apps/ios_rpc/tvmrpc/ViewController.h | 6 +- apps/ios_rpc/tvmrpc/ViewController.mm | 63 +- apps/ios_rpc/tvmrpc/main.m | 2 +- apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm | 36 +- 12 files changed, 697 insertions(+), 591 deletions(-) rename apps/ios_rpc/tvmrpc/{rpc_args.h => RPCArgs.h} (67%) rename apps/ios_rpc/tvmrpc/{rpc_args.mm => RPCArgs.mm} (79%) diff --git a/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj b/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj index bd8a33c4d867..1f5d30abb5b0 100644 --- a/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj +++ b/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj @@ -27,7 +27,7 @@ /* Begin PBXBuildFile section */ 016B19C22657B390002E1719 /* RPCServer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 016B19C12657B390002E1719 /* RPCServer.mm */; }; - 01A1DB432652CBA700655BBC /* rpc_args.mm in Sources */ = {isa = PBXBuildFile; fileRef = 01A1DB412652CBA700655BBC /* rpc_args.mm */; }; + 01A1DB432652CBA700655BBC /* RPCArgs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 01A1DB412652CBA700655BBC /* RPCArgs.mm */; }; 01A9B7B3265BD1FD000D092F /* libtvm_runtime.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */; }; 01A9B7B8265BD307000D092F /* libtvm_runtime.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; C02637501F1C25E8007247A9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C026374F1F1C25E8007247A9 /* main.m */; }; @@ -68,8 +68,8 @@ /* Begin PBXFileReference section */ 016B19C02657B390002E1719 /* RPCServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RPCServer.h; sourceTree = ""; }; 016B19C12657B390002E1719 /* RPCServer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RPCServer.mm; sourceTree = ""; }; - 01A1DB402652CBA700655BBC /* rpc_args.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rpc_args.h; sourceTree = ""; }; - 01A1DB412652CBA700655BBC /* rpc_args.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = rpc_args.mm; sourceTree = ""; }; + 01A1DB402652CBA700655BBC /* RPCArgs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RPCArgs.h; sourceTree = ""; }; + 01A1DB412652CBA700655BBC /* RPCArgs.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RPCArgs.mm; sourceTree = ""; }; 01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libtvm_runtime.dylib; path = "${TVM_BUILD_DIR}/libtvm_runtime.dylib"; sourceTree = ""; }; C026374B1F1C25E8007247A9 /* tvmrpc.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvmrpc.app; sourceTree = BUILT_PRODUCTS_DIR; }; C026374F1F1C25E8007247A9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -134,8 +134,8 @@ children = ( 016B19C02657B390002E1719 /* RPCServer.h */, 016B19C12657B390002E1719 /* RPCServer.mm */, - 01A1DB402652CBA700655BBC /* rpc_args.h */, - 01A1DB412652CBA700655BBC /* rpc_args.mm */, + 01A1DB402652CBA700655BBC /* RPCArgs.h */, + 01A1DB412652CBA700655BBC /* RPCArgs.mm */, C02637651F1C2690007247A9 /* TVMRuntime.mm */, C02637511F1C25E8007247A9 /* AppDelegate.h */, C02637521F1C25E8007247A9 /* AppDelegate.m */, @@ -299,7 +299,7 @@ buildActionMask = 2147483647; files = ( C02637691F1C26AF007247A9 /* ViewController.mm in Sources */, - 01A1DB432652CBA700655BBC /* rpc_args.mm in Sources */, + 01A1DB432652CBA700655BBC /* RPCArgs.mm in Sources */, 016B19C22657B390002E1719 /* RPCServer.mm in Sources */, C02637531F1C25E8007247A9 /* AppDelegate.m in Sources */, C02637661F1C2690007247A9 /* TVMRuntime.mm in Sources */, @@ -370,6 +370,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -422,6 +423,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -449,7 +451,6 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_OBJC_ARC = NO; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = NO; DEVELOPMENT_TEAM = 3FR42MXLK9; @@ -474,7 +475,7 @@ PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmrpc; PRODUCT_NAME = "$(TARGET_NAME)"; TVM_BUILD_DIR = "path-to-tvm-ios-build-folder"; - USE_CUSTOM_DSO_LOADER = 0; + USE_CUSTOM_DSO_LOADER = 1; WARNING_CFLAGS = "-Wno-shorten-64-to-32"; _DSO_LOADER_NAME_0 = ""; _DSO_LOADER_NAME_1 = "-lmacho_dyld"; @@ -485,7 +486,6 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_OBJC_ARC = NO; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = NO; DEVELOPMENT_TEAM = 3FR42MXLK9; @@ -510,7 +510,7 @@ PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmrpc; PRODUCT_NAME = "$(TARGET_NAME)"; TVM_BUILD_DIR = "path-to-tvm-ios-build-folder"; - USE_CUSTOM_DSO_LOADER = 0; + USE_CUSTOM_DSO_LOADER = 1; WARNING_CFLAGS = "-Wno-shorten-64-to-32"; _DSO_LOADER_NAME_0 = ""; _DSO_LOADER_NAME_1 = "-lmacho_dyld"; diff --git a/apps/ios_rpc/tvmrpc/Assets.xcassets/AppIcon.appiconset/Contents.json b/apps/ios_rpc/tvmrpc/Assets.xcassets/AppIcon.appiconset/Contents.json index 1d060ed28827..9221b9bb1a35 100644 --- a/apps/ios_rpc/tvmrpc/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/apps/ios_rpc/tvmrpc/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -2,92 +2,97 @@ "images" : [ { "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" + "scale" : "3x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" + "scale" : "3x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" + "scale" : "3x", + "size" : "40x40" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" + "scale" : "2x", + "size" : "60x60" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" + "scale" : "3x", + "size" : "60x60" }, { "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" + "scale" : "1x", + "size" : "20x20" }, { "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" + "scale" : "1x", + "size" : "29x29" }, { "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" + "scale" : "1x", + "size" : "40x40" }, { "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" }, { "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" + "scale" : "1x", + "size" : "76x76" }, { "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" + "scale" : "2x", + "size" : "76x76" }, { "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/apps/ios_rpc/tvmrpc/Base.lproj/Main.storyboard b/apps/ios_rpc/tvmrpc/Base.lproj/Main.storyboard index c2dfc6269c51..3ae0c28b4389 100644 --- a/apps/ios_rpc/tvmrpc/Base.lproj/Main.storyboard +++ b/apps/ios_rpc/tvmrpc/Base.lproj/Main.storyboard @@ -17,7 +17,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -48,16 +48,6 @@ - + - diff --git a/apps/ios_rpc/tvmrpc/rpc_args.h b/apps/ios_rpc/tvmrpc/RPCArgs.h similarity index 67% rename from apps/ios_rpc/tvmrpc/rpc_args.h rename to apps/ios_rpc/tvmrpc/RPCArgs.h index e174da27f775..ccdd0282b169 100644 --- a/apps/ios_rpc/tvmrpc/rpc_args.h +++ b/apps/ios_rpc/tvmrpc/RPCArgs.h @@ -28,17 +28,29 @@ extern "C" { * \brief Struct representing arguments of iOS RPC app */ typedef struct RPCArgs_t { - const char* host_url; /// Tracker or Proxy address (actually ip) - int host_port; /// Tracker or Proxy port + /// Tracker or Proxy address (actually ip) + const char* host_url; - const char* key; /// device key to report - const char* - custom_addr; /// custom adress to report into Tracker. Ignored for other server modes. - int port; /// begin of port range to try to bind RPC port - int port_end; /// end of port range to try to bind RPC port + /// Tracker or Proxy port + int host_port; - char immediate_connect; /// 0 - UI interaction, 1 - automatically connect on launch - char server_mode; /// 0 - connect to Tracker, 1 - connect to Proxy, 2 - pure RPC server + /// device key to report + const char* key; + + /// custom adress to report into Tracker. Ignored for other server modes. + const char* custom_addr; + + /// Verbose mode. Will print status messages to std out. + /// 0 - no prints , 1 - print state to output + char verbose; + + /// Immediate server launch. No UI interaction. + /// 0 - UI interaction, 1 - automatically connect on launch + char immediate_connect; + + /// Server mode: + /// 0 - connect to Tracker, 1 - connect to Proxy, 2 - pure RPC server + char server_mode; } RPCArgs; /*! @@ -53,7 +65,7 @@ void set_current_rpc_args(RPCArgs args); /*! * \brief Pars command line args and update current global RPC args - * Also update values in app cache + * Also updates values in app cache */ void update_rpc_args(int argc, char* argv[]); diff --git a/apps/ios_rpc/tvmrpc/rpc_args.mm b/apps/ios_rpc/tvmrpc/RPCArgs.mm similarity index 79% rename from apps/ios_rpc/tvmrpc/rpc_args.mm rename to apps/ios_rpc/tvmrpc/RPCArgs.mm index 38ddf0488a00..21b576a4afc0 100644 --- a/apps/ios_rpc/tvmrpc/rpc_args.mm +++ b/apps/ios_rpc/tvmrpc/RPCArgs.mm @@ -17,7 +17,7 @@ * under the License. */ -#import "rpc_args.h" +#import "RPCArgs.h" #import @@ -31,14 +31,13 @@ const char* kUsage = "\n" "iOS tvmrpc application supported flags:\n" - "--host_url - The tracker/proxy address, Default=0.0.0.0\n" - "--host_port - The tracker/proxy port, Default=9190\n" - "--port - The port of the RPC, Default=9090\n" - "--port_end - The end search port of the RPC, Default=9099\n" - "--key - The key used to identify the device type in tracker. Default=\"\"\n" - "--custom_addr - Custom IP Address to Report to RPC Tracker. Default=\"\"\n" - "--immediate_connect - No UI interconnection, connect to tracker immediately. Default=False\n" - "--server_mode - Server mode. Can be \"pure_server\", \"proxy\" or \"tracker\". " + "--host_url The tracker/proxy address, Default=0.0.0.0\n" + "--host_port The tracker/proxy port, Default=9190\n" + "--key The key used to identify the device type in tracker. Default=\"\"\n" + "--custom_addr Custom IP Address to Report to RPC Tracker. Default=\"\"\n" + "--immediate_connect No UI interconnection, connect to tracker immediately. Default=False\n" + "--verbose Allow to print status info to std out. Default=False\n" + "--server_mode Server mode. Can be \"pure_server\", \"proxy\" or \"tracker\". " "Default=pure_server \n" "\n"; @@ -48,10 +47,9 @@ string key; string custom_addr = ""; - int port = 9090; - int port_end = 9099; bool immediate_connect = false; + bool verbose = false; char server_mode = 0; operator RPCArgs() const { @@ -59,8 +57,7 @@ operator RPCArgs() const { .host_port = host_port, .key = key.c_str(), .custom_addr = custom_addr.c_str(), - .port = port, - .port_end = port_end, + .verbose = verbose, .immediate_connect = immediate_connect, .server_mode = server_mode}; }; @@ -70,8 +67,7 @@ operator RPCArgs() const { host_port = args.host_port; key = args.key; custom_addr = args.custom_addr; - port = args.port; - port_end = args.port_end; + verbose = args.verbose; immediate_connect = args.immediate_connect; server_mode = args.server_mode; return *this; @@ -135,9 +131,10 @@ void update_rpc_args(int argc, char* argv[]) { constexpr int MAX_PORT_NUM = 65535; const string immediate_connect = GetCmdOption(argc, argv, "--immediate_connect", true); - if (!immediate_connect.empty()) { - args.immediate_connect = true; - } + args.immediate_connect = !immediate_connect.empty(); + + const string verbose = GetCmdOption(argc, argv, "--verbose", true); + args.verbose = !verbose.empty(); const string server_mode = GetCmdOption(argc, argv, "--server_mode=", false); if (!server_mode.empty()) { @@ -174,26 +171,6 @@ void update_rpc_args(int argc, char* argv[]) { args.host_port = stoi(host_port); } - const string port = GetCmdOption(argc, argv, "--port="); - if (!port.empty()) { - if (!IsNumber(port) || stoi(port) > MAX_PORT_NUM) { - LOG(WARNING) << "Wrong port number."; - LOG(INFO) << kUsage; - exit(1); - } - args.port = stoi(port); - } - - const string port_end = GetCmdOption(argc, argv, "--port_end="); - if (!port_end.empty()) { - if (!IsNumber(port_end) || stoi(port_end) > MAX_PORT_NUM) { - LOG(WARNING) << "Wrong port_end number."; - LOG(INFO) << kUsage; - exit(1); - } - args.port_end = stoi(port_end); - } - const string key = GetCmdOption(argc, argv, "--key="); if (!key.empty()) { args.key = key; diff --git a/apps/ios_rpc/tvmrpc/RPCServer.h b/apps/ios_rpc/tvmrpc/RPCServer.h index 1101119a85cc..b30d161214b1 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.h +++ b/apps/ios_rpc/tvmrpc/RPCServer.h @@ -64,25 +64,27 @@ typedef enum { */ @interface RPCServer : NSObject +/// Event listener delegate to set +@property(retain) id delegate; +/// Device key to report during RPC session +@property(retain) NSString* key; +/// Host address of Proxy/Tracker server (generally IPv4). Ignored for PureServer mode. +@property(retain) NSString* host; +/// Port of Proxy/Tracker server. Ignored for PureServer mode. +@property int port; +/// Triger to enable printing of server state info +@property BOOL verbose; + /*! * \brief Create server with specified sevicing mode * \param mode Mode of server */ + (instancetype)serverWithMode:(RPCServerMode)mode; -/*! - * \brief Set listener delegate - * \param delegate Event listener delegate to set - */ -- (void)setDelegate:(id)delegate; - /*! * \brief Start RPC server with options. Non blocking method - * \param host Host address of Tracker/Proxy to connect. Skipped for PureServer mode - * \param port Port of Tracker/Proxy to connect. Skipped for PureServer mode - * \param key device key to use in RPC server */ -- (void)startWithHost:(NSString*)host port:(int)port key:(NSString*)key; +- (void)start; /*! * \brief Stop RPC server. Non blocking method diff --git a/apps/ios_rpc/tvmrpc/RPCServer.mm b/apps/ios_rpc/tvmrpc/RPCServer.mm index ab55346f40cb..b1d623ccc136 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.mm +++ b/apps/ios_rpc/tvmrpc/RPCServer.mm @@ -92,13 +92,12 @@ PackedFunc writer_func([outputStream](TVMArgs args, TVMRetValue* rv) { int success = getifaddrs(&interfaces); if (success == 0) { - // Loop through linked list of interfaces ifaddrs* temp_addr = interfaces; while (temp_addr != NULL) { if (temp_addr->ifa_addr->sa_family == AF_INET) { // Check if interface is en0 which is the wifi connection on the iPhone if (std::string(temp_addr->ifa_name) == "en0") { - address = std::string(inet_ntoa(((sockaddr_in*)temp_addr->ifa_addr)->sin_addr)); + address = inet_ntoa(((sockaddr_in*)temp_addr->ifa_addr)->sin_addr); } } temp_addr = temp_addr->ifa_next; @@ -112,107 +111,181 @@ PackedFunc writer_func([outputStream](TVMArgs args, TVMRetValue* rv) { } // namespace runtime } // namespace tvm -// Forward declaration -@interface RPCTrackerClien : RPCServer -@end - -// Forward declaration -@interface RPCServerPure : RPCServer -@end +// Base class for any type of RPC servicing +@interface RPCServerBase : RPCServer /*! - * \brief Base implementation of server to work with RPCProxy - * Will automatically connect/reconnect to RPCProxy server + * Methods to implement on inherited classes */ -@implementation RPCServer { - @protected - // The key of the server. - NSString* key_; - // The url of host. - NSString* url_; - // The port of host. - NSInteger port_; - // Event listener - id delegate_; +- (bool)onReadHandler; // return true - continue feeding, false - stop, try to drain output buffer +- (bool)onWriteHandler; // return true - continue draining, false - no data to write +- (void)onEndEncountered; // called on disconnect or session desided that it's shutdown time +- (void)open; // Initiate listening objects like i/o streams and other resources +- (void)close; // Deinitialize resources opend in "open" method +@end + +@implementation RPCServerBase { // Worker thread NSThread* worker_thread_; - // Triger to continue processing + // Triger to continue RunLoop processing inside worker_thread_ BOOL shouldKeepRunning; - // Ip of rpc server (actually ip of ios device) - std::string server_ip_; - // Port of rpc server - int server_port_; // Input socket stream + @protected NSInputStream* inputStream_; // Output socket stream NSOutputStream* outputStream_; - // Temporal receive buffer. + // Temporal buffer with data to send + std::string sendBuffer_; + // Temporal receive buffer std::string recvBuffer_; - // Whether connection is initialized. - bool initialized_; - // Initial bytes to be send to remote - std::string initBytes_; - // Send pointer of initial bytes. - size_t initSendPtr_; - // Event handler. - tvm::runtime::FEventHandler handler_; + // Requested data size to accumulate in recvBuffer_ before continue processing + int requiredToRecv_; } -+ (instancetype)serverWithMode:(RPCServerMode)mode { - if (mode == RPCServerMode_PureServer) return [[RPCServerPure alloc] init]; - if (mode == RPCServerMode_Proxy) return [[RPCServer alloc] init]; - if (mode == RPCServerMode_Tracker) return [[RPCTrackerClien alloc] init]; - return nil; +/*! + * Start internal worker thread with RunLoop and submit correspoding open handlers into it + * Not blocking + */ +- (void)start { + worker_thread_ = [[NSThread alloc] initWithBlock:^{ + @autoreleasepool { + [self notifyState:RPCServerStatus_Launched]; + [self open]; + shouldKeepRunning = YES; + while (shouldKeepRunning && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]) + ; + [self notifyState:RPCServerStatus_Stopped]; + } + }]; + [worker_thread_ start]; } -- (instancetype)init { - [super init]; - server_ip_ = tvm::runtime::getWiFiAddress(); - return self; +/*! + * Send message to workel thread runloop to finish processing + * Not blocking + */ +- (void)stop { + if (worker_thread_ == nil) return; + + [self performSelector:@selector(stop_) onThread:worker_thread_ withObject:nil waitUntilDone:NO]; + worker_thread_ = nil; // TODO: is it valide? may be better to do that inside NSThread? +} + +- (void)stop_ { + [self close]; + shouldKeepRunning = NO; } /*! - * Internal setters methods + * Base implementation to selup i/o streams + * Will connect to host and port specified in corresponding properties */ -- (void)setDelegate:(id)delegate { - delegate_ = delegate; +- (void)open { + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.host, self.port, &readStream, + &writeStream); + inputStream_ = (__bridge NSInputStream*)readStream; + outputStream_ = (__bridge NSOutputStream*)writeStream; + [inputStream_ setDelegate:self]; + [outputStream_ setDelegate:self]; + [inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ open]; + [inputStream_ open]; } -- (void)setKey:(NSString*)key { - key_ = key; + +/*! + * Base implementation to selup i/o streams + * Will assign i/o streams to provided socket connection. + */ +- (void)openWithSocket:(CFSocketNativeHandle)sock { + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + CFStreamCreatePairWithSocket(NULL, sock, &readStream, &writeStream); + inputStream_ = (__bridge NSInputStream*)readStream; + outputStream_ = (__bridge NSOutputStream*)writeStream; + [inputStream_ setDelegate:self]; + [outputStream_ setDelegate:self]; + [inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ open]; + [inputStream_ open]; +} + +/*! + * Close i/o streams assosiated with connection + */ +- (void)close { + [inputStream_ close]; + [outputStream_ close]; + [inputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [inputStream_ setDelegate:nil]; + [outputStream_ setDelegate:nil]; + inputStream_ = nil; + outputStream_ = nil; } -- (void)setUrl:(NSString*)url { - url_ = url; + +/*! + * Try to read data from stream and call processing hadnler + */ +- (void)tryToRead { + const int kBufferSize = 4 << 10; // 4kB buffer + const int prev_size = recvBuffer_.size(); + recvBuffer_.resize(kBufferSize); + size_t nbytes = [inputStream_ read:(uint8_t*)recvBuffer_.data() + prev_size + maxLength:recvBuffer_.size() - prev_size]; + recvBuffer_.resize(nbytes + prev_size); + + // feed while it accept or requested particulat buffer size + while (!recvBuffer_.empty() && requiredToRecv_ <= recvBuffer_.size() && [self onReadHandler]) + ; } -- (void)setPort:(NSInteger)port { - port_ = port; + +/*! + * Try to write remaining data to stream and call processing hadnler + */ +- (void)tryToWrite { + if (!sendBuffer_.empty()) { + size_t nbytes = [outputStream_ write:(uint8_t*)sendBuffer_.data() maxLength:sendBuffer_.size()]; + sendBuffer_.erase(0, nbytes); + } + // call write handler while it want be called and space is available + while (sendBuffer_.empty() && [outputStream_ hasSpaceAvailable] && [self onWriteHandler]) + ; } /*! - * \brief Main event listener method. All stream event handling starts here + * Main event handler of socket stream events */ - (void)stream:(NSStream*)strm handleEvent:(NSStreamEvent)event { std::string buffer; switch (event) { case NSStreamEventOpenCompleted: { + // Nothing break; } case NSStreamEventHasBytesAvailable: if (strm == inputStream_) { - [self onReadAvailable]; + [self tryToRead]; + if ([outputStream_ hasSpaceAvailable]) [self tryToWrite]; } break; case NSStreamEventHasSpaceAvailable: { if (strm == outputStream_) { - [self onWriteAvailable]; + [self tryToWrite]; + if ([inputStream_ hasBytesAvailable]) [self tryToRead]; } break; } case NSStreamEventErrorOccurred: { - NSLog(@"%@", [strm streamError].localizedDescription); + [self notifyError:[strm streamError].localizedDescription]; break; } case NSStreamEventEndEncountered: { - [self onEndEvent]; + [self onEndEncountered]; break; } default: { @@ -221,52 +294,179 @@ - (void)stream:(NSStream*)strm handleEvent:(NSStreamEvent)event { } } +#pragma mark - Helpers + +/*! + * Set buffer to send into stream. Try to send immediatly or submit to lazy sending + * Non blocking operation + */ +- (void)toSend:(NSData*)data { + sendBuffer_.append(static_cast(data.bytes), data.length); + + // try to flush buffer + NSInteger sent_size = [outputStream_ write:(uint8_t*)sendBuffer_.data() + maxLength:sendBuffer_.size()]; + sendBuffer_.erase(0, sent_size); +} + +/*! + * Set buffer to send in packet format [size, data]. Behaviour is same as for toSend. + */ +- (void)toSendPacked:(NSData*)data { + int packet_size = data.length; + [self toSend:[NSData dataWithBytes:&packet_size length:sizeof(packet_size)]]; + [self toSend:data]; +} + +/*! + */ +- (NSData*)requestInputDataWithSize:(NSInteger)size { + if (recvBuffer_.size() < size) { + requiredToRecv_ = size; + return nil; + } + NSData* res = [NSData dataWithBytes:recvBuffer_.data() length:size]; + recvBuffer_.erase(0, size); + return res; +} + +/*! + */ +- (NSData*)requestInputDataPacked { + int size; + if (recvBuffer_.size() < sizeof(size)) { + requiredToRecv_ = sizeof(size); + return nil; + } + size = *(int*)recvBuffer_.data(); + if (recvBuffer_.size() < sizeof(size) + size) { + requiredToRecv_ = sizeof(size) + size; + return nil; + } + NSData* res = [NSData dataWithBytes:recvBuffer_.data() + sizeof(size) length:size]; + recvBuffer_.erase(0, sizeof(size) + size); + return res; +}; + +#pragma mark - Notifiers + +/*! + * Notify external listener about error. + * Also print error message to std out in case of Verbose mode + */ - (void)notifyError:(NSString*)msg { - NSLog(@"[IOS-RPC] ERROR: %@", msg); - if (delegate_ != nil) [delegate_ onError:msg]; + // Duplicate error message in std output. Host launcher script may listen it. + if (self.verbose) NSLog(@"[IOS-RPC] ERROR: %@", msg); + if (self.delegate) [self.delegate onError:msg]; } +/*! + * Notify external listener about server state changes. + * Also print information to std out in case of Verbose mode + */ - (void)notifyState:(RPCServerStatus)state { - if (state == RPCServerStatus_Launched) { - // Notify host runner script with actual address - NSLog(@"[IOS-RPC] IP: %s", server_ip_.c_str()); - NSLog(@"[IOS-RPC] PORT: %d", server_port_); + // Duplicate sattus changing in std output. Host launcher script may listen it. + if (self.verbose) NSLog(@"[IOS-RPC] STATE: %d", state); + if (self.delegate != nil) [self.delegate onStatusChanged:state]; +} + +@end + +@interface RPCServerProxy : RPCServerBase +@end + +typedef enum { + RPCServerProxyState_Idle, + RPCServerProxyState_HandshakeToSend, + RPCServerProxyState_HandshakeToRecv, + RPCServerProxyState_Processing, +} RPCServerProxyState; + +@implementation RPCServerProxy { + /// Original TVM RPC event handler + tvm::runtime::FEventHandler handler_; + @protected + /// Sate of Proxy client implementation + RPCServerProxyState state_; +} + +- (instancetype)init { + if (self = [super init]) { + handler_ = nullptr; + state_ = RPCServerProxyState_Idle; } - // Notify host runner script with current status - NSLog(@"[IOS-RPC] STATE: %d", state); - // Notify listener - if (delegate_ != nil) [delegate_ onStatusChanged:state]; -} - -- (void)onReadAvailable { - if (!initialized_) { - using tvm::runtime::kRPCMagic; - int code; - size_t nbytes = [inputStream_ read:reinterpret_cast(&code) maxLength:sizeof(code)]; - if (nbytes != sizeof(code)) { - [self notifyError:@"Fail to receive remote confirmation code."]; - [self close]; - } else if (code == kRPCMagic + 2) { - [self notifyError:@"Proxy server cannot find client that matches the key"]; - [self close]; - } else if (code == kRPCMagic + 1) { - [self notifyError:@"Proxy server already have another server with same key"]; - [self close]; - } else if (code != kRPCMagic) { - [self notifyError:@"Given address is not a TVM RPC Proxy"]; - [self close]; - } else { - initialized_ = true; - ICHECK(handler_ != nullptr); + return self; +} + +/*! + * Implement matching of internat state on state available for outside users + */ +- (void)setState:(RPCServerProxyState)new_state { + // Send Connected notification because Proxy doesn't responce until client connected. + if (new_state == RPCServerProxyState_HandshakeToRecv) + [self notifyState:RPCServerStatus_Connected]; + if (new_state == RPCServerProxyState_Idle) [self notifyState:RPCServerStatus_Disconnected]; + if (state_ == RPCServerProxyState_HandshakeToRecv && new_state == RPCServerProxyState_Processing) + [self notifyState:RPCServerStatus_RPCSessionStarted]; + if (state_ == RPCServerProxyState_Processing && new_state == RPCServerProxyState_Idle) + [self notifyState:RPCServerStatus_RPCSessionStarted]; + + state_ = new_state; +} + +- (bool)onWriteHandler { + switch (state_) { + case RPCServerProxyState_HandshakeToSend: { + // Send together kRPCMagic and server descriptor because of Proxy + int code = tvm::runtime::kRPCMagic; + [self toSend:[NSData dataWithBytes:&code length:sizeof(code)]]; + + std::string full_key = std::string("server:") + self.key.UTF8String; + [self toSendPacked:[NSData dataWithBytes:full_key.data() length:full_key.size()]]; + + self.state = RPCServerProxyState_HandshakeToRecv; + return TRUE; } + case RPCServerProxyState_Processing: { + try { + TVMByteArray dummy{nullptr, 0}; + int flag = handler_(dummy, 2); + if (flag == 0) { + [self onEndEncountered]; + } + return flag == 2; + } catch (const tvm::Error& e) { + [self close]; + } + break; + } + default: + // Nothing + break; } - const int kBufferSize = 4 << 10; - if (initialized_) { - while ([inputStream_ hasBytesAvailable]) { - recvBuffer_.resize(kBufferSize); - uint8_t* bptr = reinterpret_cast(&recvBuffer_[0]); - size_t nbytes = [inputStream_ read:bptr maxLength:kBufferSize]; - recvBuffer_.resize(nbytes); + return FALSE; +} + +- (bool)onReadHandler { + switch (state_) { + case RPCServerProxyState_HandshakeToRecv: { + NSData* data = [self requestInputDataWithSize:sizeof(int)]; + if (data == nil) return FALSE; + + if (*(int*)data.bytes != tvm::runtime::kRPCMagic) { + [self notifyError:@"Wrong responce, is not RPC client."]; + [self close]; + return FALSE; + break; + } + + handler_ = tvm::runtime::CreateServerEventHandler(outputStream_, "iphone", "%toinit"); + + self.state = RPCServerProxyState_Processing; + return TRUE; + break; + } + case RPCServerProxyState_Processing: { int flag = 1; if ([outputStream_ hasSpaceAvailable]) { flag |= 2; @@ -275,435 +475,319 @@ - (void)onReadAvailable { try { TVMByteArray arr{recvBuffer_.data(), recvBuffer_.size()}; flag = handler_(arr, flag); - if (flag == 2) { - [self onShutdownReceived]; + recvBuffer_.clear(); + if (flag == 0) { + [self onEndEncountered]; } + return flag == 1; } catch (const tvm::Error& e) { [self close]; } + break; } + default: + // Nothing + break; } + return FALSE; } -- (void)onWriteAvailable { - if (initSendPtr_ < initBytes_.length()) { - initSendPtr_ += [outputStream_ write:reinterpret_cast(&initBytes_[initSendPtr_]) - maxLength:(initBytes_.length() - initSendPtr_)]; - } - [self notifyState:RPCServerStatus_Connected]; - if (initialized_) { - try { - TVMByteArray dummy{nullptr, 0}; - int flag = handler_(dummy, 2); - if (flag == 2) { - [self onShutdownReceived]; - } - } catch (const tvm::Error& e) { - [self close]; - } - } -} - -- (void)onShutdownReceived { - [self close]; -} - -- (void)onEndEvent { +- (void)onEndEncountered { + // Automatic reconnection when session is finished. [self close]; - [self notifyState:RPCServerStatus_RPCSessionFinished]; - // Basic behaviour is to reconnect [self open]; } - (void)open { - // Initialize the data states. - std::string full_key = std::string("server:") + [key_ UTF8String]; - std::ostringstream os; - int rpc_magic = tvm::runtime::kRPCMagic; - os.write(reinterpret_cast(&rpc_magic), sizeof(rpc_magic)); - int keylen = static_cast(full_key.length()); - os.write(reinterpret_cast(&keylen), sizeof(keylen)); - os.write(full_key.c_str(), full_key.length()); - initialized_ = false; - initBytes_ = os.str(); - initSendPtr_ = 0; - - // Initialize the network. - CFReadStreamRef readStream; - CFWriteStreamRef writeStream; - CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)url_, port_, &readStream, &writeStream); - inputStream_ = (NSInputStream*)readStream; - outputStream_ = (NSOutputStream*)writeStream; - [inputStream_ setDelegate:self]; - [outputStream_ setDelegate:self]; - [inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ open]; - [inputStream_ open]; - - handler_ = tvm::runtime::CreateServerEventHandler(outputStream_, full_key, "%toinit"); - ICHECK(handler_ != nullptr); + [super open]; + self.state = RPCServerProxyState_HandshakeToSend; } - (void)close { - [inputStream_ close]; - [outputStream_ close]; - [inputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [inputStream_ setDelegate:nil]; - [outputStream_ setDelegate:nil]; - inputStream_ = nil; - outputStream_ = nil; + [super close]; handler_ = nullptr; - [self notifyState:RPCServerStatus_Disconnected]; + self.state = RPCServerProxyState_Idle; } -- (void)startWithHost:(NSString*)host port:(int)port key:(NSString*)key { - key_ = [key copy]; - port_ = port; - url_ = [host copy]; - - // process in separate thead with runloop - worker_thread_ = [[NSThread alloc] initWithBlock:^{ - @autoreleasepool { - [self open]; - [self notifyState:RPCServerStatus_Launched]; - shouldKeepRunning = YES; - while (shouldKeepRunning && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode - beforeDate:[NSDate distantFuture]]) - ; - [self notifyState:RPCServerStatus_Stopped]; - } - }]; - [worker_thread_ start]; -} +@end -- (void)stop { - if (worker_thread_ == nil) return; +@interface RPCServerPure : RPCServerProxy +@property(readonly) int rpc_port; +@end - [self performSelector:@selector(stop_) onThread:worker_thread_ withObject:nil waitUntilDone:NO]; - worker_thread_ = nil; +@implementation RPCServerPure { + // Socket to listen incomming connections + CFSocketRef socket_; + /// Current socket connection handler + CFSocketNativeHandle connection_; + /// Port range to try bind to socket + int port_range_start; + int port_range_end; } -- (void)stop_ { - [self close]; - shouldKeepRunning = NO; +- (instancetype)init { + if (self = [super init]) { + connection_ = 0; + port_range_start = 9090; + port_range_end = 9099; + } + return self; } -@end - -@implementation RPCServerPure { - // Socket with incoming conenction (only for Pure RPC server) - CFSocketNativeHandle socket_; -} - -- (void)handleConnect:(const CFSocketNativeHandle)hdl { - socket_ = hdl; - - // Initialize the data states. - std::string full_key = std::string("server:") + [key_ UTF8String]; - std::ostringstream os; - int rpc_magic = tvm::runtime::kRPCMagic; - os.write(reinterpret_cast(&rpc_magic), sizeof(rpc_magic)); - int keylen = static_cast(full_key.length()); - os.write(reinterpret_cast(&keylen), sizeof(keylen)); - os.write(full_key.c_str(), full_key.length()); - initialized_ = false; - initBytes_ = os.str(); - initSendPtr_ = 0; - - // Initialize the network. - CFReadStreamRef readStream; - CFWriteStreamRef writeStream; - CFStreamCreatePairWithSocket(NULL, socket_, &readStream, &writeStream); +- (void)setState:(RPCServerProxyState)new_state { + if (state_ == RPCServerProxyState_Idle && new_state == RPCServerProxyState_HandshakeToSend) { + if (self.verbose) { + // Notify host runner script with actual address + NSLog(@"[IOS-RPC] IP: %s", tvm::runtime::getWiFiAddress().c_str()); + NSLog(@"[IOS-RPC] PORT: %d", _rpc_port); + } + [self notifyState:RPCServerStatus_Connected]; + } + if (new_state == RPCServerProxyState_Idle) [self notifyState:RPCServerStatus_Disconnected]; + if (state_ == RPCServerProxyState_HandshakeToRecv && new_state == RPCServerProxyState_Processing) + [self notifyState:RPCServerStatus_RPCSessionStarted]; + if (state_ == RPCServerProxyState_Processing && new_state == RPCServerProxyState_HandshakeToSend) + [self notifyState:RPCServerStatus_RPCSessionFinished]; - inputStream_ = (NSInputStream*)readStream; - outputStream_ = (NSOutputStream*)writeStream; - [inputStream_ setDelegate:self]; - [outputStream_ setDelegate:self]; - [inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ open]; - [inputStream_ open]; + state_ = new_state; +} - handler_ = tvm::runtime::CreateServerEventHandler(outputStream_, full_key, "%toinit"); - ICHECK(handler_ != nullptr); +- (void)handleConnect:(CFSocketNativeHandle)hdl { + connection_ = hdl; + [super openWithSocket:connection_]; + self.state = RPCServerProxyState_HandshakeToSend; } static void handleConnect(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) { - RPCServerPure* it = static_cast(info); + RPCServerPure* it = (__bridge RPCServerPure*)(info); [it handleConnect:*static_cast(data)]; } - (void)open { CFSocketContext ctx{}; - ctx.info = self; - - CFSocketRef myipv4cfsock = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, - kCFSocketAcceptCallBack, handleConnect, &ctx); - - struct sockaddr_in sin; - int rpc_port = 9090; // TODO: hardcoded. Should try bind in range of ports - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_port = htons(rpc_port); - sin.sin_addr.s_addr = INADDR_ANY; - - CFDataRef sincfd = CFDataCreate(kCFAllocatorDefault, (UInt8*)&sin, sizeof(sin)); - - if (CFSocketSetAddress(myipv4cfsock, sincfd) != 0) - @throw [NSException - exceptionWithName:@"SocketError" - reason:[NSString stringWithFormat:@"Can not bind to port %d", rpc_port] - userInfo:nil]; - CFRelease(sincfd); - server_port_ = rpc_port; - - CFRunLoopSourceRef socketsource = - CFSocketCreateRunLoopSource(kCFAllocatorDefault, myipv4cfsock, 0); + ctx.info = (__bridge void*)self; + + socket_ = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, + kCFSocketAcceptCallBack, handleConnect, &ctx); + self->_rpc_port = 0; + + // Try to bind with range + for (int port = port_range_start; port < port_range_end; port++) { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = INADDR_ANY; + + CFDataRef sincfd = CFDataCreate(kCFAllocatorDefault, (UInt8*)&sin, sizeof(sin)); + CFSocketError res = CFSocketSetAddress(socket_, sincfd); + CFRelease(sincfd); + if (res == kCFSocketSuccess) { + self->_rpc_port = port; + break; + } + } + if (self->_rpc_port == 0) { + @throw + [NSException exceptionWithName:@"SocketError" + reason:[NSString stringWithFormat:@"Unable bind socket to port" + "in range [%d, %d]", + port_range_start, port_range_end] + userInfo:nil]; + } + + CFRunLoopSourceRef socketsource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 0); CFRunLoopAddSource(CFRunLoopGetCurrent(), socketsource, kCFRunLoopDefaultMode); + + self.state = RPCServerProxyState_HandshakeToSend; +} + +- (void)closeSocket { + CFSocketInvalidate(socket_); } -- (void)onEndEvent { +- (void)close { + [super close]; + close(connection_); +} + +- (void)onEndEncountered { [self close]; [self notifyState:RPCServerStatus_RPCSessionFinished]; } @end +@interface RPCServerTracker : RPCServerBase +@end + typedef enum { - HandshakeToSend, - HandshakeToRecv, - ServerInfoToSend, - ServerInfoToRecv, - ReportResToSend, - ReportResToRecv, - UpdateKeyToSend, - UpdateKeyToRecv, - WaitConnection, -} TrackerClientState; - -@implementation RPCTrackerClien { - // RPC Server to register in tracker + RPCServerTracker_Idle, + RPCServerTracker_HandshakeToSend, + RPCServerTracker_HandshakeToRecv, + RPCServerTracker_ServerInfoToSend, + RPCServerTracker_ServerInfoToRecv, + RPCServerTracker_ReportResToSend, + RPCServerTracker_ReportResToRecv, + RPCServerTracker_UpdateKeyToSend, + RPCServerTracker_UpdateKeyToRecv, + RPCServerTracker_WaitConnection +} RPCServerTrackerState; + +@implementation RPCServerTracker { + RPCServerTrackerState state_; RPCServerPure* rpc_server_; - // Size of data required accumulate in readBuffer before processing - int required_data_size; - // State of tracker client - TrackerClientState state_; } -- (void)toSend:(NSData*)data { - // try to send - NSInteger sent_size = [outputStream_ write:(uint8_t*)data.bytes maxLength:data.length]; - // assume that all data is sent - if (sent_size != data.length) - @throw [NSException exceptionWithName:@"SocketError" - reason:[NSString stringWithFormat:@"Unable to send data"] - userInfo:nil]; -} - -- (void)toSendPacked:(NSData*)data { - // try to send - int packet_size = data.length; - NSInteger sent_size = [outputStream_ write:(uint8_t*)&packet_size maxLength:sizeof(packet_size)]; - if (sent_size != sizeof(packet_size)) - @throw [NSException exceptionWithName:@"SocketError" - reason:[NSString stringWithFormat:@"Unable to send data"] - userInfo:nil]; - - NSInteger sent_data = [outputStream_ write:(uint8_t*)data.bytes maxLength:data.length]; - // assume that all data is sent - if (sent_data != data.length) - @throw [NSException exceptionWithName:@"SocketError" - reason:[NSString stringWithFormat:@"Unable to send data"] - userInfo:nil]; -} - -- (void)onReadAvailable { - const int kBufferSize = 4 << 10; - const int prev_size = recvBuffer_.size(); - recvBuffer_.resize(kBufferSize); - size_t nbytes = [inputStream_ read:(uint8_t*)recvBuffer_.data() + prev_size - maxLength:recvBuffer_.size() - prev_size]; - recvBuffer_.resize(nbytes + prev_size); +- (void)setState:(RPCServerTrackerState)new_state { + if (state_ == RPCServerTracker_ReportResToRecv && new_state == RPCServerTracker_WaitConnection) + [self notifyState:RPCServerStatus_Connected]; + if (state_ == RPCServerTracker_WaitConnection && new_state == RPCServerTracker_Idle) + [self notifyState:RPCServerStatus_Disconnected]; - if (recvBuffer_.size() < required_data_size) return; + state_ = new_state; +} +- (bool)onWriteHandler { switch (state_) { - case HandshakeToRecv: { + case RPCServerTracker_HandshakeToSend: { int code = tvm::runtime::kRPCTrackerMagic; - if (recvBuffer_.size() < sizeof(code)) { - required_data_size = sizeof(code); - break; - } - - if (recvBuffer_.size() != sizeof(code) || *(int*)recvBuffer_.data() != code) { - [self notifyError:@"Wrong responce, server is not tracker."]; - [self close]; - break; - } - - recvBuffer_.erase(recvBuffer_.begin(), recvBuffer_.begin() + sizeof(code)); - required_data_size = 0; - [self notifyState:RPCServerStatus_Connected]; - state_ = ServerInfoToSend; + [self toSend:[NSData dataWithBytes:&code length:sizeof(code)]]; + self.state = RPCServerTracker_HandshakeToRecv; + return TRUE; break; } - case ServerInfoToRecv: { - std::string expected = std::to_string(static_cast(tvm::runtime::TrackerCode::kSuccess)); - int packet_size; - if (recvBuffer_.size() < sizeof(packet_size)) { - required_data_size = sizeof(packet_size); - break; - } - - packet_size = *(int*)recvBuffer_.data(); - if (recvBuffer_.size() < sizeof(packet_size) + packet_size) { - required_data_size = sizeof(packet_size) + packet_size; - break; - } - - if (std::string((char*)recvBuffer_.data() + sizeof(packet_size), packet_size) != expected) { - [self notifyError:@"Was not able to update info in tracker. Responce is not OK."]; - [self close]; - break; - } - - recvBuffer_.erase(recvBuffer_.begin(), - recvBuffer_.begin() + sizeof(packet_size) + packet_size); - required_data_size = 0; - state_ = ReportResToSend; + case RPCServerTracker_ServerInfoToSend: { + std::ostringstream ss; + ss << "[" << static_cast(tvm::runtime::TrackerCode::kUpdateInfo) + << ", {\"key\": \"server:" << self.key.UTF8String << "\"}]"; + std::string data_s = ss.str(); + [self toSendPacked:[NSData dataWithBytes:data_s.data() length:data_s.length()]]; + self.state = RPCServerTracker_ServerInfoToRecv; + return TRUE; break; } - case ReportResToRecv: { - std::string expected = std::to_string(static_cast(tvm::runtime::TrackerCode::kSuccess)); - int packet_size; - if (recvBuffer_.size() < sizeof(packet_size)) { - required_data_size = sizeof(packet_size); - break; - } - - packet_size = *(int*)recvBuffer_.data(); - if (recvBuffer_.size() < sizeof(packet_size) + packet_size) { - required_data_size = sizeof(packet_size) + packet_size; - break; - } + case RPCServerTracker_ReportResToSend: { + std::mt19937 gen(std::random_device{}()); + std::uniform_real_distribution dis(0.0, 1.0); - if (std::string((char*)recvBuffer_.data() + sizeof(packet_size), packet_size) != expected) { - [self notifyError:@"Was not able to put resource into tracker. Responce is not OK."]; - [self close]; - break; - } + std::string matchkey = std::string(self.key.UTF8String) + ":" + std::to_string(dis(gen)); + std::ostringstream ss; + ss << "[" << static_cast(tvm::runtime::TrackerCode::kPut) << ", \"" + << self.key.UTF8String << "\", [" << rpc_server_.rpc_port << ", \"" << matchkey + << "\"], null]"; - recvBuffer_.erase(recvBuffer_.begin(), - recvBuffer_.begin() + sizeof(packet_size) + packet_size); - required_data_size = 0; - state_ = WaitConnection; + std::string data_s = ss.str(); + [self toSendPacked:[NSData dataWithBytes:data_s.data() length:data_s.length()]]; + self.state = RPCServerTracker_ReportResToRecv; + return TRUE; break; } default: // Nothing break; } - - if (outputStream_.hasSpaceAvailable) [self onWriteAvailable]; + return FALSE; } -- (void)onWriteAvailable { +- (bool)onReadHandler { + static const std::string resp_OK = + std::to_string(static_cast(tvm::runtime::TrackerCode::kSuccess)); switch (state_) { - case HandshakeToSend: { - int code = tvm::runtime::kRPCTrackerMagic; - NSData* data = [NSData dataWithBytes:&code length:sizeof(code)]; - [self toSend:data]; - state_ = HandshakeToRecv; + case RPCServerTracker_HandshakeToRecv: { + NSData* data = [self requestInputDataWithSize:sizeof(int)]; + if (data == nil) return FALSE; + + if (*(int*)data.bytes != tvm::runtime::kRPCTrackerMagic) { + [self notifyError:@"Wrong responce, is not RPC Tracker."]; + [self close]; + return FALSE; + break; + } + self.state = RPCServerTracker_ServerInfoToSend; + return TRUE; break; } - case ServerInfoToSend: { - std::ostringstream ss; - ss << "[" << static_cast(tvm::runtime::TrackerCode::kUpdateInfo) - << ", {\"key\": \"server:" << key_.UTF8String << "\"}]"; - std::string data_s = ss.str(); - NSData* data = [NSData dataWithBytes:data_s.data() length:data_s.length()]; - [self toSendPacked:data]; - state_ = ServerInfoToRecv; + case RPCServerTracker_ServerInfoToRecv: { + NSData* data = [self requestInputDataPacked]; + if (data == nil) return FALSE; + + if (std::string((char*)data.bytes, data.length) != resp_OK) { + [self notifyError:@"Failed to Update info on tracker. Responce is not OK."]; + [self close]; + return FALSE; + break; + } + self.state = RPCServerTracker_ReportResToSend; + return TRUE; break; } - case ReportResToSend: { - std::mt19937 gen(std::random_device{}()); - std::uniform_real_distribution dis(0.0, 1.0); - - // TODO: All values are hardcoded - int port = 9090; - std::string custom_addr = "null"; - std::string matchkey = std::string(key_.UTF8String) + ":" + std::to_string(dis(gen)); + case RPCServerTracker_ReportResToRecv: { + NSData* data = [self requestInputDataPacked]; + if (data == nil) return FALSE; - std::ostringstream ss; - ss << "[" << static_cast(tvm::runtime::TrackerCode::kPut) << ", \"" << key_.UTF8String - << "\", [" << port << ", \"" << matchkey << "\"], " << custom_addr << "]"; - - std::string data_s = ss.str(); - NSData* data = [NSData dataWithBytes:data_s.data() length:data_s.length()]; - [self toSendPacked:data]; - state_ = ReportResToRecv; + if (std::string((char*)data.bytes, data.length) != resp_OK) { + [self notifyError:@"Failed to Put server into tracker. Responce is not OK."]; + [self close]; + return FALSE; + break; + } + self.state = RPCServerTracker_WaitConnection; + return TRUE; break; } default: // Nothing break; } - - if (inputStream_.hasBytesAvailable) [self onReadAvailable]; + return FALSE; } -- (void)open { - // create RPC pure server - // * set self as delegate (to register back when servicing is finished) - // * mute printing status - // * stop processing by sending stop - rpc_server_ = [[RPCServerPure alloc] init]; - rpc_server_.key = key_; - rpc_server_.delegate = self; - [rpc_server_ open]; - - // Initialize the network. - CFReadStreamRef readStream; - CFWriteStreamRef writeStream; - - CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)url_, port_, &readStream, &writeStream); - - inputStream_ = (NSInputStream*)readStream; - outputStream_ = (NSOutputStream*)writeStream; - [inputStream_ setDelegate:self]; - [outputStream_ setDelegate:self]; - [inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream_ open]; - [inputStream_ open]; +- (void)onEndEncountered { + [self close]; } - (void)close { [rpc_server_ close]; + [rpc_server_ closeSocket]; [super close]; - [self notifyState:RPCServerStatus_Disconnected]; + self.state = RPCServerTracker_Idle; } -- (void)onEndEvent { - [self close]; +- (void)open { + // Start internal Pure RPC server at first + rpc_server_ = [[RPCServerPure alloc] init]; + rpc_server_.key = self.key; + rpc_server_.delegate = self; + [rpc_server_ open]; + + [super open]; + self.state = RPCServerTracker_HandshakeToSend; } - (void)onError:(NSString*)msg { - // transfer error form rpc_server_ to real delegate + // transfer error form internal rpc_server_ to real delegate [self notifyError:msg]; } - (void)onStatusChanged:(RPCServerStatus)status { if (status == RPCServerStatus_RPCSessionFinished) { [self notifyState:status]; - state_ = ReportResToSend; - [self onWriteAvailable]; + self.state = RPCServerTracker_ReportResToSend; + [self tryToWrite]; } } +@end + +@implementation RPCServer + ++ (instancetype)serverWithMode:(RPCServerMode)mode { + if (mode == RPCServerMode_PureServer) return [[RPCServerPure alloc] init]; + if (mode == RPCServerMode_Proxy) return [[RPCServerProxy alloc] init]; + if (mode == RPCServerMode_Tracker) return [[RPCServerTracker alloc] init]; + return nil; +} @end diff --git a/apps/ios_rpc/tvmrpc/TVMRuntime.mm b/apps/ios_rpc/tvmrpc/TVMRuntime.mm index 458808e4cc19..09a1a17ffd37 100644 --- a/apps/ios_rpc/tvmrpc/TVMRuntime.mm +++ b/apps/ios_rpc/tvmrpc/TVMRuntime.mm @@ -25,7 +25,7 @@ #include -#include "rpc_args.h" +#include "RPCArgs.h" // internal TVM header #include <../../../src/runtime/file_utils.h> diff --git a/apps/ios_rpc/tvmrpc/ViewController.h b/apps/ios_rpc/tvmrpc/ViewController.h index f634e60f959b..5e2f2bbafa99 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.h +++ b/apps/ios_rpc/tvmrpc/ViewController.h @@ -24,9 +24,7 @@ #import #import "RPCServer.h" -@interface ViewController : UIViewController { - RPCServer* server_; -} +@interface ViewController : UIViewController @property(weak, nonatomic) IBOutlet UITextField* proxyURL; @property(weak, nonatomic) IBOutlet UITextField* proxyPort; @@ -35,8 +33,6 @@ @property(weak, nonatomic) IBOutlet UITextView* infoText; - (IBAction)connect:(id)sender; -- (IBAction)disconnect:(id)sender; -@property(retain, nonatomic) IBOutlet UIButton* DisconnectButton; @property(retain, nonatomic) IBOutlet UIButton* ConnectButton; @property(retain, nonatomic) IBOutlet UISegmentedControl* ModeSelector; diff --git a/apps/ios_rpc/tvmrpc/ViewController.mm b/apps/ios_rpc/tvmrpc/ViewController.mm index d5b0149ce0e3..c6269e78ceda 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.mm +++ b/apps/ios_rpc/tvmrpc/ViewController.mm @@ -22,9 +22,16 @@ */ #import "ViewController.h" -#import "rpc_args.h" - -@implementation ViewController +#import "RPCArgs.h" + +@implementation ViewController { + // server implementation + RPCServer* server_; + // verbose flag to print status info + bool verbose_; + // verbose flag to print status info + bool to_connect_; +} - (void)viewDidLoad { // To handle end editing events @@ -38,6 +45,16 @@ - (void)viewDidLoad { self.proxyKey.text = @(args.key); self.ModeSelector.selectedSegmentIndex = args.server_mode; + self->verbose_ = args.verbose; + self->to_connect_ = true; + + // Add border to button + void (^addBorder)(UIButton* btn) = ^(UIButton* btn) { + btn.layer.borderWidth = 2.0f; + btn.layer.borderColor = self.ConnectButton.currentTitleColor.CGColor; + btn.layer.cornerRadius = 10; + }; + addBorder(self.ConnectButton); // Connect to tracker immediately if (args.immediate_connect) { @@ -55,11 +72,15 @@ - (void)disableUIInteraction { field.backgroundColor = [UIColor lightGrayColor]; }; + void (^disableButton)(UIButton* btn) = ^(UIButton* btn) { + btn.enabled = NO; + btn.layer.borderColor = btn.currentTitleColor.CGColor; + }; + disable(self.proxyURL); disable(self.proxyPort); disable(self.proxyKey); - self.ConnectButton.enabled = NO; - self.DisconnectButton.enabled = NO; + disableButton(self.ConnectButton); self.ModeSelector.enabled = NO; } @@ -70,12 +91,14 @@ - (void)open { RPCServerMode server_mode = static_cast(self.ModeSelector.selectedSegmentIndex); server_ = [RPCServer serverWithMode:server_mode]; - [server_ setDelegate:self]; - [server_ startWithHost:self.proxyURL.text - port:self.proxyPort.text.intValue - key:self.proxyKey.text]; + server_.host = self.proxyURL.text; + server_.port = self.proxyPort.text.intValue; + server_.key = self.proxyKey.text; + server_.verbose = self->verbose_; + server_.delegate = self; + + [server_ start]; - NSLog(@"Connecting to the proxy server..."); self.infoText.text = @""; self.statusLabel.text = @"Connecting..."; } @@ -85,20 +108,18 @@ - (void)open { */ - (void)close { [server_ stop]; - NSLog(@"Closing the streams..."); self.statusLabel.text = @"Disconnecting..."; } #pragma mark - Button responders - +/*! + * \brief Connect/disconnect button handler + */ - (IBAction)connect:(id)sender { - [[self view] endEditing:YES]; - [self open]; -} - -- (IBAction)disconnect:(id)sender { - [[self view] endEditing:YES]; - [self close]; + [[self view] endEditing:YES]; // to hide keyboard + (to_connect_ ^= true) ? [self close] : [self open]; + [self.ConnectButton setTitle:to_connect_ ? @"Connect" : @"Disconenct" + forState:UIControlStateNormal]; } #pragma mark - UITextFieldDelegate @@ -141,8 +162,4 @@ - (void)onStatusChanged:(RPCServerStatus)status { }); } -- (void)dealloc { - [_ModeSelector release]; - [super dealloc]; -} @end diff --git a/apps/ios_rpc/tvmrpc/main.m b/apps/ios_rpc/tvmrpc/main.m index d329dd163571..4d02f02849d2 100644 --- a/apps/ios_rpc/tvmrpc/main.m +++ b/apps/ios_rpc/tvmrpc/main.m @@ -23,7 +23,7 @@ #import #import "AppDelegate.h" -#import "rpc_args.h" +#import "RPCArgs.h" int main(int argc, char * argv[]) { update_rpc_args(argc, argv); diff --git a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm index 25c08cf43fd2..fca0adcdd6a1 100644 --- a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm +++ b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm @@ -23,23 +23,47 @@ */ #import +#import "RPCArgs.h" #import "RPCServer.h" -#import "rpc_args.h" -@interface tvmrpcLauncher : XCTestCase +@interface EventListenerWrapper : NSObject +@end + +@implementation EventListenerWrapper { + void (^blk_)(RPCServerStatus); +} + +- (instancetype)initWithBlock:(void (^)(RPCServerStatus))blk { + blk_ = blk; + return self; +} +- (void)onError:(NSString*)msg { +} +- (void)onStatusChanged:(RPCServerStatus)status { + blk_(status); +} +// void (^disable)(UITextField* field) = ^(UITextField* field) { +@end + +@interface tvmrpcLauncher : XCTestCase @end @implementation tvmrpcLauncher - (void)testRPC { RPCArgs args = get_current_rpc_args(); - RPCServerMode server_mode = static_cast(args.server_mode); - RPCServer* server_ = [RPCServer serverWithMode:server_mode]; + RPCServer* server = [RPCServer serverWithMode:static_cast(args.server_mode)]; + server.host = @(args.host_url); + server.port = args.host_port; + server.key = @(args.key); + + server.delegate = [[EventListenerWrapper alloc] initWithBlock:^(RPCServerStatus sts) { + NSLog(@"Sts : %d", sts); + }]; - RPCServer* server = [RPCServer serverWithMode:server_mode]; - [server startWithHost:@(args.host_url) port:args.host_port key:@(args.key)]; + [server start]; } @end From 9e032595df064c816f6f474137c21b72404afafa Mon Sep 17 00:00:00 2001 From: Alexander Peskov Date: Tue, 15 Jun 2021 01:34:22 +0300 Subject: [PATCH 07/23] [IOS_RPC] Min changes. Fix warnings Signed-off-by: Alexander Peskov --- apps/ios_rpc/tvmrpc/RPCServer.mm | 18 +++++++++++++++++- apps/ios_rpc/tvmrpc/ViewController.mm | 2 +- apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm | 19 +++++++++++++------ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/apps/ios_rpc/tvmrpc/RPCServer.mm b/apps/ios_rpc/tvmrpc/RPCServer.mm index b1d623ccc136..73139da7f847 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.mm +++ b/apps/ios_rpc/tvmrpc/RPCServer.mm @@ -115,7 +115,7 @@ PackedFunc writer_func([outputStream](TVMArgs args, TVMRetValue* rv) { @interface RPCServerBase : RPCServer /*! - * Methods to implement on inherited classes + * Methods should be implemented in inherited classes */ - (bool)onReadHandler; // return true - continue feeding, false - stop, try to drain output buffer - (bool)onWriteHandler; // return true - continue draining, false - no data to write @@ -228,6 +228,16 @@ - (void)close { outputStream_ = nil; } +/// Unimplemented stubs +- (bool)onReadHandler { + return false; +} +- (bool)onWriteHandler { + return false; +} +- (void)onEndEncountered { +} + /*! * Try to read data from stream and call processing hadnler */ @@ -790,4 +800,10 @@ + (instancetype)serverWithMode:(RPCServerMode)mode { return nil; } +/// Unimplemented stubs +- (void)start { +} +- (void)stop { +} + @end diff --git a/apps/ios_rpc/tvmrpc/ViewController.mm b/apps/ios_rpc/tvmrpc/ViewController.mm index c6269e78ceda..fbf59599501a 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.mm +++ b/apps/ios_rpc/tvmrpc/ViewController.mm @@ -29,7 +29,7 @@ @implementation ViewController { RPCServer* server_; // verbose flag to print status info bool verbose_; - // verbose flag to print status info + // Button state. True - push will start connection, false - push will disconnect bool to_connect_; } diff --git a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm index fca0adcdd6a1..4789365573ba 100644 --- a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm +++ b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm @@ -26,24 +26,29 @@ #import "RPCArgs.h" #import "RPCServer.h" +/*! + * Simple listener to + */ @interface EventListenerWrapper : NSObject @end @implementation EventListenerWrapper { - void (^blk_)(RPCServerStatus); + void (^blk_)(); } -- (instancetype)initWithBlock:(void (^)(RPCServerStatus))blk { +- (instancetype)initWithBlock:(void (^)())blk { blk_ = blk; return self; } - (void)onError:(NSString*)msg { + blk_(); } + - (void)onStatusChanged:(RPCServerStatus)status { - blk_(status); + if (status == RPCServerStatus_Stopped) blk_(); } -// void (^disable)(UITextField* field) = ^(UITextField* field) { + @end @interface tvmrpcLauncher : XCTestCase @@ -59,11 +64,13 @@ - (void)testRPC { server.port = args.host_port; server.key = @(args.key); - server.delegate = [[EventListenerWrapper alloc] initWithBlock:^(RPCServerStatus sts) { - NSLog(@"Sts : %d", sts); + dispatch_semaphore_t s = dispatch_semaphore_create(0); + server.delegate = [[EventListenerWrapper alloc] initWithBlock:^{ + dispatch_semaphore_signal(s); }]; [server start]; + dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER); } @end From 41e2d125e8570d0965c91861338149d4c11494c3 Mon Sep 17 00:00:00 2001 From: Alexander Peskov Date: Fri, 18 Jun 2021 16:15:15 +0300 Subject: [PATCH 08/23] [IOS-RPC] Fix review comments Signed-off-by: Alexander Peskov --- apps/ios_rpc/tvmrpc/RPCArgs.h | 11 +++++---- apps/ios_rpc/tvmrpc/RPCArgs.mm | 20 ++++++++-------- apps/ios_rpc/tvmrpc/RPCServer.h | 2 ++ apps/ios_rpc/tvmrpc/RPCServer.mm | 24 ++++++++++++------- apps/ios_rpc/tvmrpc/ViewController.mm | 8 +++---- apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm | 3 ++- 6 files changed, 39 insertions(+), 29 deletions(-) diff --git a/apps/ios_rpc/tvmrpc/RPCArgs.h b/apps/ios_rpc/tvmrpc/RPCArgs.h index ccdd0282b169..e5a1ee47a019 100644 --- a/apps/ios_rpc/tvmrpc/RPCArgs.h +++ b/apps/ios_rpc/tvmrpc/RPCArgs.h @@ -20,6 +20,8 @@ #ifndef TVM_APPS_IOS_RPC_ARGS_H_ #define TVM_APPS_IOS_RPC_ARGS_H_ +#import "RPCServer.h" + #ifdef __cplusplus extern "C" { #endif @@ -42,15 +44,14 @@ typedef struct RPCArgs_t { /// Verbose mode. Will print status messages to std out. /// 0 - no prints , 1 - print state to output - char verbose; + bool verbose; /// Immediate server launch. No UI interaction. /// 0 - UI interaction, 1 - automatically connect on launch - char immediate_connect; + bool immediate_connect; - /// Server mode: - /// 0 - connect to Tracker, 1 - connect to Proxy, 2 - pure RPC server - char server_mode; + /// Server mode + RPCServerMode server_mode; } RPCArgs; /*! diff --git a/apps/ios_rpc/tvmrpc/RPCArgs.mm b/apps/ios_rpc/tvmrpc/RPCArgs.mm index 21b576a4afc0..b35eb182a40e 100644 --- a/apps/ios_rpc/tvmrpc/RPCArgs.mm +++ b/apps/ios_rpc/tvmrpc/RPCArgs.mm @@ -50,7 +50,7 @@ bool immediate_connect = false; bool verbose = false; - char server_mode = 0; + RPCServerMode server_mode = RPCServerMode_Tracker; operator RPCArgs() const { return RPCArgs{.host_url = host_url.c_str(), @@ -90,18 +90,18 @@ static void restore_from_cache() { return static_cast([defaults integerForKey:ns_key]); }; - g_rpc_args.host_url = get_string_from_cache("tmvrpc_url"); - g_rpc_args.host_port = get_int_from_cache("tmvrpc_port"); - g_rpc_args.key = get_string_from_cache("tmvrpc_key"); + g_rpc_args.host_url = get_string_from_cache("RPCArgs_url"); + g_rpc_args.host_port = get_int_from_cache("RPCArgs_port"); + g_rpc_args.key = get_string_from_cache("RPCArgs_key"); } static void update_in_cache() { NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:[NSString stringWithUTF8String:g_rpc_args.host_url.c_str()] - forKey:@"tmvrpc_url"]; - [defaults setInteger:g_rpc_args.host_port forKey:@"tmvrpc_port"]; - [defaults setObject:[NSString stringWithUTF8String:g_rpc_args.key.c_str()] forKey:@"tmvrpc_key"]; + forKey:@"RPCArgs_url"]; + [defaults setInteger:g_rpc_args.host_port forKey:@"RPCArgs_port"]; + [defaults setObject:[NSString stringWithUTF8String:g_rpc_args.key.c_str()] forKey:@"RPCArgs_key"]; } string GetCmdOption(int argc, char* argv[], string option, bool key = false) { @@ -139,11 +139,11 @@ void update_rpc_args(int argc, char* argv[]) { const string server_mode = GetCmdOption(argc, argv, "--server_mode=", false); if (!server_mode.empty()) { if (server_mode == "tracker") { - args.server_mode = 0; + args.server_mode = RPCServerMode_Tracker; } else if (server_mode == "proxy") { - args.server_mode = 1; + args.server_mode = RPCServerMode_Proxy; } else if (server_mode == "pure_server") { - args.server_mode = 2; + args.server_mode = RPCServerMode_PureServer; } else { LOG(WARNING) << "Wrong server_mode value."; LOG(INFO) << kUsage; diff --git a/apps/ios_rpc/tvmrpc/RPCServer.h b/apps/ios_rpc/tvmrpc/RPCServer.h index b30d161214b1..c1303bc17a6d 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.h +++ b/apps/ios_rpc/tvmrpc/RPCServer.h @@ -72,6 +72,8 @@ typedef enum { @property(retain) NSString* host; /// Port of Proxy/Tracker server. Ignored for PureServer mode. @property int port; +/// Custom address to report into tracker server (optional). Ignored for PureServer/Proxy modes +@property(retain) NSString* custom_addr; /// Triger to enable printing of server state info @property BOOL verbose; diff --git a/apps/ios_rpc/tvmrpc/RPCServer.mm b/apps/ios_rpc/tvmrpc/RPCServer.mm index 73139da7f847..9a7519ebffb0 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.mm +++ b/apps/ios_rpc/tvmrpc/RPCServer.mm @@ -323,7 +323,7 @@ - (void)toSend:(NSData*)data { * Set buffer to send in packet format [size, data]. Behaviour is same as for toSend. */ - (void)toSendPacked:(NSData*)data { - int packet_size = data.length; + uint32_t packet_size = data.length; [self toSend:[NSData dataWithBytes:&packet_size length:sizeof(packet_size)]]; [self toSend:data]; } @@ -343,12 +343,12 @@ - (NSData*)requestInputDataWithSize:(NSInteger)size { /*! */ - (NSData*)requestInputDataPacked { - int size; + uint32_t size; if (recvBuffer_.size() < sizeof(size)) { requiredToRecv_ = sizeof(size); return nil; } - size = *(int*)recvBuffer_.data(); + size = *(uint32_t*)recvBuffer_.data(); if (recvBuffer_.size() < sizeof(size) + size) { requiredToRecv_ = sizeof(size) + size; return nil; @@ -428,7 +428,7 @@ - (bool)onWriteHandler { switch (state_) { case RPCServerProxyState_HandshakeToSend: { // Send together kRPCMagic and server descriptor because of Proxy - int code = tvm::runtime::kRPCMagic; + int32_t code = tvm::runtime::kRPCMagic; [self toSend:[NSData dataWithBytes:&code length:sizeof(code)]]; std::string full_key = std::string("server:") + self.key.UTF8String; @@ -460,10 +460,11 @@ - (bool)onWriteHandler { - (bool)onReadHandler { switch (state_) { case RPCServerProxyState_HandshakeToRecv: { - NSData* data = [self requestInputDataWithSize:sizeof(int)]; + int32_t code = tvm::runtime::kRPCMagic; + NSData* data = [self requestInputDataWithSize:sizeof(code)]; if (data == nil) return FALSE; - if (*(int*)data.bytes != tvm::runtime::kRPCMagic) { + if (*(int32_t*)data.bytes != tvm::runtime::kRPCMagic) { [self notifyError:@"Wrong responce, is not RPC client."]; [self close]; return FALSE; @@ -663,7 +664,7 @@ - (void)setState:(RPCServerTrackerState)new_state { - (bool)onWriteHandler { switch (state_) { case RPCServerTracker_HandshakeToSend: { - int code = tvm::runtime::kRPCTrackerMagic; + int32_t code = tvm::runtime::kRPCTrackerMagic; [self toSend:[NSData dataWithBytes:&code length:sizeof(code)]]; self.state = RPCServerTracker_HandshakeToRecv; return TRUE; @@ -683,11 +684,16 @@ - (bool)onWriteHandler { std::mt19937 gen(std::random_device{}()); std::uniform_real_distribution dis(0.0, 1.0); + std::string address_to_report = "null"; + if (self.custom_addr != nil && self.custom_addr.length != 0) { + address_to_report = self.custom_addr.UTF8String; + } + std::string matchkey = std::string(self.key.UTF8String) + ":" + std::to_string(dis(gen)); std::ostringstream ss; ss << "[" << static_cast(tvm::runtime::TrackerCode::kPut) << ", \"" - << self.key.UTF8String << "\", [" << rpc_server_.rpc_port << ", \"" << matchkey - << "\"], null]"; + << self.key.UTF8String << "\", [" << rpc_server_.rpc_port << ", \"" << matchkey << "\"], " + << address_to_report << "]"; std::string data_s = ss.str(); [self toSendPacked:[NSData dataWithBytes:data_s.data() length:data_s.length()]]; diff --git a/apps/ios_rpc/tvmrpc/ViewController.mm b/apps/ios_rpc/tvmrpc/ViewController.mm index fbf59599501a..3c07f6d33895 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.mm +++ b/apps/ios_rpc/tvmrpc/ViewController.mm @@ -27,8 +27,6 @@ @implementation ViewController { // server implementation RPCServer* server_; - // verbose flag to print status info - bool verbose_; // Button state. True - push will start connection, false - push will disconnect bool to_connect_; } @@ -45,7 +43,6 @@ - (void)viewDidLoad { self.proxyKey.text = @(args.key); self.ModeSelector.selectedSegmentIndex = args.server_mode; - self->verbose_ = args.verbose; self->to_connect_ = true; // Add border to button @@ -88,13 +85,16 @@ - (void)disableUIInteraction { * \brief Start RPC server */ - (void)open { + RPCArgs args = get_current_rpc_args(); + RPCServerMode server_mode = static_cast(self.ModeSelector.selectedSegmentIndex); server_ = [RPCServer serverWithMode:server_mode]; server_.host = self.proxyURL.text; server_.port = self.proxyPort.text.intValue; server_.key = self.proxyKey.text; - server_.verbose = self->verbose_; + server_.custom_addr = [NSString stringWithUTF8String:args.custom_addr]; + server_.verbose = args.verbose; server_.delegate = self; [server_ start]; diff --git a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm index 4789365573ba..224581d49d73 100644 --- a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm +++ b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm @@ -42,6 +42,7 @@ - (instancetype)initWithBlock:(void (^)())blk { } - (void)onError:(NSString*)msg { + NSLog(msg); blk_(); } @@ -59,7 +60,7 @@ @implementation tvmrpcLauncher - (void)testRPC { RPCArgs args = get_current_rpc_args(); - RPCServer* server = [RPCServer serverWithMode:static_cast(args.server_mode)]; + RPCServer* server = [RPCServer serverWithMode:args.server_mode]; server.host = @(args.host_url); server.port = args.host_port; server.key = @(args.key); From 3216c4668177c8b4d83ade70501fd8780c867c06 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Tue, 7 Sep 2021 09:56:06 +0300 Subject: [PATCH 09/23] Fix RPC connection to tracker --- apps/ios_rpc/tvmrpc/RPCArgs.mm | 2 +- apps/ios_rpc/tvmrpc/RPCServer.mm | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/ios_rpc/tvmrpc/RPCArgs.mm b/apps/ios_rpc/tvmrpc/RPCArgs.mm index b35eb182a40e..7c6dd668fe42 100644 --- a/apps/ios_rpc/tvmrpc/RPCArgs.mm +++ b/apps/ios_rpc/tvmrpc/RPCArgs.mm @@ -46,7 +46,7 @@ int host_port = 9190; string key; - string custom_addr = ""; + string custom_addr = "null"; bool immediate_connect = false; bool verbose = false; diff --git a/apps/ios_rpc/tvmrpc/RPCServer.mm b/apps/ios_rpc/tvmrpc/RPCServer.mm index 9a7519ebffb0..0ee149febd00 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.mm +++ b/apps/ios_rpc/tvmrpc/RPCServer.mm @@ -673,7 +673,8 @@ - (bool)onWriteHandler { case RPCServerTracker_ServerInfoToSend: { std::ostringstream ss; ss << "[" << static_cast(tvm::runtime::TrackerCode::kUpdateInfo) - << ", {\"key\": \"server:" << self.key.UTF8String << "\"}]"; + << ", {\"key\": \"server:" << self.key.UTF8String << "\", \"addr\": [" + << self.custom_addr.UTF8String << ", \"" << self.port << "\"]}]"; std::string data_s = ss.str(); [self toSendPacked:[NSData dataWithBytes:data_s.data() length:data_s.length()]]; self.state = RPCServerTracker_ServerInfoToRecv; From 7eb6c0246d6dd15df9de6c422433ffebf80a3086 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Tue, 7 Sep 2021 16:22:10 +0300 Subject: [PATCH 10/23] Fix typo --- apps/ios_rpc/tvmrpc/RPCServer.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/ios_rpc/tvmrpc/RPCServer.mm b/apps/ios_rpc/tvmrpc/RPCServer.mm index 0ee149febd00..2a5ae7e30989 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.mm +++ b/apps/ios_rpc/tvmrpc/RPCServer.mm @@ -63,8 +63,8 @@ */ FEventHandler CreateServerEventHandler(NSOutputStream* outputStream, std::string name, std::string remote_key) { - const PackedFunc* event_handler_factor = Registry::Get("rpc.CreateEventDrivenServer"); - ICHECK(event_handler_factor != nullptr) + const PackedFunc* event_handler_factory = Registry::Get("rpc.CreateEventDrivenServer"); + ICHECK(event_handler_factory != nullptr) << "You are using tvm_runtime module built without RPC support. " << "Please rebuild it with USE_RPC flag."; @@ -79,7 +79,7 @@ PackedFunc writer_func([outputStream](TVMArgs args, TVMRetValue* rv) { *rv = nbytes; }); - return (*event_handler_factor)(writer_func, name, remote_key); + return (*event_handler_factory)(writer_func, name, remote_key); } /*! @@ -527,7 +527,7 @@ @interface RPCServerPure : RPCServerProxy @end @implementation RPCServerPure { - // Socket to listen incomming connections + // Socket to listen incoming connections CFSocketRef socket_; /// Current socket connection handler CFSocketNativeHandle connection_; From dbb743c45b93060d6185ca5d34b407f04801bc61 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Wed, 8 Sep 2021 08:14:45 +0300 Subject: [PATCH 11/23] Fix build for local developer profile --- apps/ios_rpc/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/ios_rpc/CMakeLists.txt b/apps/ios_rpc/CMakeLists.txt index 3c01e41de1b9..96d2d257d4ad 100644 --- a/apps/ios_rpc/CMakeLists.txt +++ b/apps/ios_rpc/CMakeLists.txt @@ -54,6 +54,10 @@ ExternalProject_Add(custom_dso_loader -DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=${CMAKE_BUILD_WITH_INSTALL_NAME_DIR} ) +if(NOT CMAKE_IOS_RPC_BUNDLE) + set(CMAKE_IOS_RPC_BUNDLE org.apache.tvmrpc) +endif() + # iOS RPC Xcode project wrapper to integrate into Cmake ExternalProject_Add(ios_rpc PREFIX ios_rpc @@ -68,10 +72,12 @@ ExternalProject_Add(ios_rpc -sdk ${CMAKE_OSX_SYSROOT} -arch ${CMAKE_OSX_ARCHITECTURES} -hideShellScriptEnvironment + -allowProvisioningUpdates build SYMROOT= IPHONEOS_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} DEVELOPMENT_TEAM=${CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM} TVM_BUILD_DIR=${CMAKE_BINARY_DIR} USE_CUSTOM_DSO_LOADER=1 + PRODUCT_BUNDLE_IDENTIFIER=${CMAKE_IOS_RPC_BUNDLE} ) From c76640e86ed2184523ac2b63d42bff9cf8232915 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Wed, 8 Sep 2021 11:15:40 +0300 Subject: [PATCH 12/23] Add tvmrpc xcode scheme --- .../xcshareddata/xcschemes/tvmrpc.xcscheme | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme diff --git a/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme b/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme new file mode 100644 index 000000000000..5835fa83b2df --- /dev/null +++ b/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 81621be3494d7b62208599f1b705d328eb3ba522 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Tue, 7 Sep 2021 16:57:34 +0300 Subject: [PATCH 13/23] Revert unnecessary change --- .../AppIcon.appiconset/Contents.json | 79 +++++++++---------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/apps/ios_rpc/tvmrpc/Assets.xcassets/AppIcon.appiconset/Contents.json b/apps/ios_rpc/tvmrpc/Assets.xcassets/AppIcon.appiconset/Contents.json index 9221b9bb1a35..1d060ed28827 100644 --- a/apps/ios_rpc/tvmrpc/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/apps/ios_rpc/tvmrpc/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -2,97 +2,92 @@ "images" : [ { "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" + "size" : "20x20", + "scale" : "2x" }, { "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" + "size" : "20x20", + "scale" : "3x" }, { "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" + "size" : "29x29", + "scale" : "2x" }, { "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" + "size" : "29x29", + "scale" : "3x" }, { "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" + "size" : "40x40", + "scale" : "2x" }, { "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" + "size" : "40x40", + "scale" : "3x" }, { "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" + "size" : "60x60", + "scale" : "2x" }, { "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" + "size" : "60x60", + "scale" : "3x" }, { "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" + "size" : "20x20", + "scale" : "1x" }, { "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" + "size" : "20x20", + "scale" : "2x" }, { "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" + "size" : "29x29", + "scale" : "1x" }, { "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" + "size" : "29x29", + "scale" : "2x" }, { "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" + "size" : "40x40", + "scale" : "1x" }, { "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" + "size" : "40x40", + "scale" : "2x" }, { "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" + "size" : "76x76", + "scale" : "1x" }, { "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" + "size" : "76x76", + "scale" : "2x" }, { "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" + "size" : "83.5x83.5", + "scale" : "2x" } ], "info" : { - "author" : "xcode", - "version" : 1 + "version" : 1, + "author" : "xcode" } -} +} \ No newline at end of file From 7cb2b842e59a2163febab940a69dc95307d916af Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Thu, 9 Sep 2021 14:21:52 +0300 Subject: [PATCH 14/23] Remove old mechanism of reloading libs --- apps/ios_rpc/tests/ios_rpc_mobilenet.py | 4 +- apps/ios_rpc/tests/ios_rpc_test.py | 44 +-------- apps/ios_rpc/tvmrpc/Info.plist | 17 ---- apps/ios_rpc/tvmrpcLauncher/Info.plist | 39 -------- apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm | 77 ---------------- python/tvm/contrib/xcode.py | 92 ------------------- 6 files changed, 3 insertions(+), 270 deletions(-) delete mode 100644 apps/ios_rpc/tvmrpcLauncher/Info.plist delete mode 100644 apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm diff --git a/apps/ios_rpc/tests/ios_rpc_mobilenet.py b/apps/ios_rpc/tests/ios_rpc_mobilenet.py index a57db729d8a6..7c297988438e 100644 --- a/apps/ios_rpc/tests/ios_rpc_mobilenet.py +++ b/apps/ios_rpc/tests/ios_rpc_mobilenet.py @@ -109,11 +109,9 @@ def run(mod, target): lib.export_library(path_dso, xcode.create_dylib, arch=arch, sdk=sdk) xcode.codesign(path_dso) - # Start RPC test server that contains the compiled library. - xcode.popen_test_rpc(proxy_host, proxy_port, key, destination=destination, libs=[path_dso]) - # connect to the proxy remote = rpc.connect(proxy_host, proxy_port, key=key) + remote.upload(path_dso) if target == "metal": dev = remote.metal(0) diff --git a/apps/ios_rpc/tests/ios_rpc_test.py b/apps/ios_rpc/tests/ios_rpc_test.py index 0f81dcce929f..57da3d45e2d1 100644 --- a/apps/ios_rpc/tests/ios_rpc_test.py +++ b/apps/ios_rpc/tests/ios_rpc_test.py @@ -81,13 +81,9 @@ def test_rpc_module(): f.export_library(path_dso2, xcode.create_dylib, arch=arch, sdk=sdk) xcode.codesign(path_dso2) - # Start RPC test server that contains the compiled library. - server = xcode.popen_test_rpc( - proxy_host, proxy_port, key, destination=destination, libs=[path_dso1, path_dso2] - ) - # connect to the proxy remote = rpc.connect(proxy_host, proxy_port, key=key) + remote.upload(path_dso1) dev = remote.metal(0) f1 = remote.load_module("dev_lib.dylib") a_np = np.random.uniform(size=1024).astype(A.dtype) @@ -99,6 +95,7 @@ def test_rpc_module(): np.testing.assert_equal(b.numpy(), a.numpy() + 1) # CPU dev = remote.cpu(0) + remote.upload(path_dso2) f2 = remote.load_module("cpu_lib.dylib") a_np = np.random.uniform(size=1024).astype(A.dtype) a = tvm.nd.array(a_np, dev) @@ -109,42 +106,5 @@ def test_rpc_module(): np.testing.assert_equal(b.numpy(), a.numpy() + 1) -def test_rpc_module_with_upload(): - server = xcode.popen_test_rpc(proxy_host, proxy_port, key, destination=destination) - - remote = rpc.connect(proxy_host, proxy_port, key=key) - try: - remote.get_function("runtime.module.loadfile_dylib_custom") - except AttributeError as e: - print(e) - print("Skip test. You are using iOS RPC without custom DSO loader enabled.") - return - - n = tvm.runtime.convert(1024) - A = te.placeholder((n,), name="A") - B = te.compute(A.shape, lambda *i: A(*i) + 1.0, name="B") - temp = utils.tempdir() - s = te.create_schedule(B.op) - xo, xi = s[B].split(B.op.axis[0], factor=64) - s[B].parallel(xi) - s[B].pragma(xo, "parallel_launch_point") - s[B].pragma(xi, "parallel_barrier_when_finish") - f = tvm.build(s, [A, B], target, name="myadd_cpu") - path_dso = temp.relpath("cpu_lib.dylib") - f.export_library(path_dso, xcode.create_dylib, arch=arch, sdk=sdk) - - dev = remote.cpu(0) - remote.upload(path_dso) - f = remote.load_module("cpu_lib.dylib") - a_np = np.random.uniform(size=1024).astype(A.dtype) - a = tvm.nd.array(a_np, dev) - b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev) - time_f = f.time_evaluator(f.entry_name, dev, number=10) - cost = time_f(a, b).mean - print("%g secs/op" % cost) - np.testing.assert_equal(b.numpy(), a.numpy() + 1) - - if __name__ == "__main__": test_rpc_module() - test_rpc_module_with_upload() diff --git a/apps/ios_rpc/tvmrpc/Info.plist b/apps/ios_rpc/tvmrpc/Info.plist index e6eb1b529052..d05247386801 100644 --- a/apps/ios_rpc/tvmrpc/Info.plist +++ b/apps/ios_rpc/tvmrpc/Info.plist @@ -1,21 +1,4 @@ - - - - - - - - - - - - - - - - - diff --git a/apps/ios_rpc/tvmrpcLauncher/Info.plist b/apps/ios_rpc/tvmrpcLauncher/Info.plist deleted file mode 100644 index 45eb19371f17..000000000000 --- a/apps/ios_rpc/tvmrpcLauncher/Info.plist +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm b/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm deleted file mode 100644 index 224581d49d73..000000000000 --- a/apps/ios_rpc/tvmrpcLauncher/tvmrpcLauncher.mm +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/*! - * \brief A hook to launch RPC server via xcodebuild test - * \file tvmrpcLauncher.mm - */ - -#import -#import "RPCArgs.h" -#import "RPCServer.h" - -/*! - * Simple listener to - */ -@interface EventListenerWrapper : NSObject -@end - -@implementation EventListenerWrapper { - void (^blk_)(); -} - -- (instancetype)initWithBlock:(void (^)())blk { - blk_ = blk; - return self; -} - -- (void)onError:(NSString*)msg { - NSLog(msg); - blk_(); -} - -- (void)onStatusChanged:(RPCServerStatus)status { - if (status == RPCServerStatus_Stopped) blk_(); -} - -@end - -@interface tvmrpcLauncher : XCTestCase -@end - -@implementation tvmrpcLauncher - -- (void)testRPC { - RPCArgs args = get_current_rpc_args(); - - RPCServer* server = [RPCServer serverWithMode:args.server_mode]; - server.host = @(args.host_url); - server.port = args.host_port; - server.key = @(args.key); - - dispatch_semaphore_t s = dispatch_semaphore_create(0); - server.delegate = [[EventListenerWrapper alloc] initWithBlock:^{ - dispatch_semaphore_signal(s); - }]; - - [server start]; - dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER); -} - -@end diff --git a/python/tvm/contrib/xcode.py b/python/tvm/contrib/xcode.py index c4b76ad04b71..bf3952587f33 100644 --- a/python/tvm/contrib/xcode.py +++ b/python/tvm/contrib/xcode.py @@ -181,95 +181,3 @@ def compile_coreml(model, model_name="main", out_dir="."): raise RuntimeError("Compile failed: %s" % res) return mlmodelc_path - - -class XCodeRPCServer(object): - """Wrapper for RPC server - - Parameters - ---------- - cmd : list of str - The command to run - - lock: FileLock - Lock on the path - """ - - def __init__(self, cmd, lock): - self.proc = subprocess.Popen(cmd) - self.lock = lock - - def join(self): - """Wait server to finish and release its resource""" - self.proc.wait() - self.lock.release() - - -def popen_test_rpc(host, port, key, destination, libs=None, options=None): - """Launch rpc server via xcodebuild test through another process. - - Parameters - ---------- - host : str - The address of RPC proxy host. - - port : int - The port of RPC proxy host - - key : str - The key of the RPC server - - destination : str - Destination device of deployment, as in xcodebuild - - libs : list of str - List of files to be packed into app/Frameworks/tvm - These can be dylibs that can be loaed remoted by RPC. - - options : list of str - Additional options to xcodebuild - - Returns - ------- - proc : Popen - The test rpc server process. - Don't do wait() on proc, since it can terminate normally. - """ - if "TVM_IOS_RPC_ROOT" in os.environ: - rpc_root = os.environ["TVM_IOS_RPC_ROOT"] - else: - curr_path = os.path.dirname(os.path.realpath(os.path.expanduser(__file__))) - rpc_root = os.path.join(curr_path, "../../../apps/ios_rpc") - proj_path = os.path.realpath(os.path.join(rpc_root, "tvmrpc.xcodeproj")) - team_id = os.environ["TVM_IOS_TEAM_ID"] - if not os.path.exists(proj_path): - raise RuntimeError( - "Cannot find tvmrpc.xcodeproj in %s," - + (" please set env TVM_IOS_RPC_ROOT correctly" % rpc_root) - ) - - # Lock the path so only one file can run - lock = utils.filelock(os.path.join(rpc_root, "ios_rpc.lock")) - - with open(os.path.join(rpc_root, "rpc_config.txt"), "w") as fo: - fo.write("%s %d %s\n" % (host, port, key)) - libs = libs if libs else [] - for file_name in libs: - fo.write("%s\n" % file_name) - - cmd = [ - "xcrun", - "xcodebuild", - "-scheme", - "tvmrpc", - "-project", - proj_path, - "-destination", - destination, - ] - if options: - cmd += options - cmd += ["test"] - cmd += [f"DEVELOPMENT_TEAM={team_id}"] - - return XCodeRPCServer(cmd, lock) From ff9355c1e3d2f57acb824ef461e4fb02b7798449 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Fri, 10 Sep 2021 07:05:10 +0300 Subject: [PATCH 15/23] Display ip and port for PureRPC mode --- apps/ios_rpc/tvmrpc/Info.plist | 17 +++++++++++++++++ apps/ios_rpc/tvmrpc/RPCServer.h | 4 ++++ apps/ios_rpc/tvmrpc/RPCServer.mm | 2 ++ apps/ios_rpc/tvmrpc/ViewController.mm | 5 ++++- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/apps/ios_rpc/tvmrpc/Info.plist b/apps/ios_rpc/tvmrpc/Info.plist index d05247386801..e6eb1b529052 100644 --- a/apps/ios_rpc/tvmrpc/Info.plist +++ b/apps/ios_rpc/tvmrpc/Info.plist @@ -1,4 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/apps/ios_rpc/tvmrpc/RPCServer.h b/apps/ios_rpc/tvmrpc/RPCServer.h index c1303bc17a6d..95d22668ad56 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.h +++ b/apps/ios_rpc/tvmrpc/RPCServer.h @@ -76,6 +76,10 @@ typedef enum { @property(retain) NSString* custom_addr; /// Triger to enable printing of server state info @property BOOL verbose; +/// RPC port opened on the device. Ignored for Proxy/Tracker modes +@property int actual_port; +/// IP address of the device. Ignored for Proxy/Tracker modes +@property(retain) NSString* device_addr; /*! * \brief Create server with specified sevicing mode diff --git a/apps/ios_rpc/tvmrpc/RPCServer.mm b/apps/ios_rpc/tvmrpc/RPCServer.mm index 2a5ae7e30989..4bd9d16dcabe 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.mm +++ b/apps/ios_rpc/tvmrpc/RPCServer.mm @@ -547,6 +547,8 @@ - (instancetype)init { - (void)setState:(RPCServerProxyState)new_state { if (state_ == RPCServerProxyState_Idle && new_state == RPCServerProxyState_HandshakeToSend) { + self.actual_port = _rpc_port; + self.device_addr = [NSString stringWithUTF8String:tvm::runtime::getWiFiAddress().c_str()]; if (self.verbose) { // Notify host runner script with actual address NSLog(@"[IOS-RPC] IP: %s", tvm::runtime::getWiFiAddress().c_str()); diff --git a/apps/ios_rpc/tvmrpc/ViewController.mm b/apps/ios_rpc/tvmrpc/ViewController.mm index 3c07f6d33895..a13f60247bcb 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.mm +++ b/apps/ios_rpc/tvmrpc/ViewController.mm @@ -94,8 +94,8 @@ - (void)open { server_.port = self.proxyPort.text.intValue; server_.key = self.proxyKey.text; server_.custom_addr = [NSString stringWithUTF8String:args.custom_addr]; - server_.verbose = args.verbose; server_.delegate = self; + NSLog(@"value: %d", server_.verbose); [server_ start]; @@ -150,6 +150,9 @@ - (void)onStatusChanged:(RPCServerStatus)status { dispatch_sync(dispatch_get_main_queue(), ^{ switch (status) { case RPCServerStatus_Connected: + if (self.ModeSelector.selectedSegmentIndex == RPCServerMode_PureServer) { + self.infoText.text = [NSString stringWithFormat:@"IP: %@\nPort: %d", server_.device_addr, server_.actual_port]; + } self.statusLabel.text = @"Connected"; break; case RPCServerStatus_Disconnected: From 7cb54c64a4e8d1f4c11a7405c11cc1fbc2abd59c Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Fri, 10 Sep 2021 08:06:07 +0300 Subject: [PATCH 16/23] Update tests --- apps/ios_rpc/tests/ios_rpc_mobilenet.py | 39 +++++++++++++---------- apps/ios_rpc/tests/ios_rpc_test.py | 41 +++++++++++++++---------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/apps/ios_rpc/tests/ios_rpc_mobilenet.py b/apps/ios_rpc/tests/ios_rpc_mobilenet.py index 7c297988438e..62b04f95eccc 100644 --- a/apps/ios_rpc/tests/ios_rpc_mobilenet.py +++ b/apps/ios_rpc/tests/ios_rpc_mobilenet.py @@ -32,20 +32,7 @@ from mxnet import gluon from PIL import Image import coremltools - -# Set to be address of tvm proxy. -proxy_host = os.environ["TVM_IOS_RPC_PROXY_HOST"] -# Set your desination via env variable. -# Should in format "platform=iOS,id=" -destination = os.environ["TVM_IOS_RPC_DESTINATION"] - -if not re.match(r"^platform=.*,id=.*$", destination): - print("Bad format: {}".format(destination)) - print("Example of expected string: platform=iOS,id=1234567890abcabcabcabc1234567890abcabcab") - sys.exit(1) - -proxy_port = 9090 -key = "iphone" +import argparse # Change target configuration, this is setting for iphone6s # arch = "x86_64" @@ -54,6 +41,12 @@ sdk = "iphoneos" target_host = "llvm -mtriple=%s-apple-darwin" % arch +MODES = { + 'proxy': rpc.connect, + 'tracker': rpc.connect_tracker, + 'pure_server': rpc.connect +} + # override metal compiler to compile to iphone @tvm.register_func("tvm_callback_metal_compile") def compile_metal(src): @@ -97,7 +90,7 @@ def get_model(model_name, data_shape): return func, params -def test_mobilenet(): +def test_mobilenet(host, port, key, mode): temp = utils.tempdir() image, synset = prepare_input() model, params = get_model("mobilenetv2_1.0", image.shape) @@ -110,7 +103,10 @@ def run(mod, target): xcode.codesign(path_dso) # connect to the proxy - remote = rpc.connect(proxy_host, proxy_port, key=key) + if mode == "tracker": + remote = MODES[mode](host, port).request(key) + else: + remote = MODES[mode](host, port, key=key) remote.upload(path_dso) if target == "metal": @@ -173,4 +169,13 @@ def annotate(func, compiler): if __name__ == "__main__": - test_mobilenet() + parser = argparse.ArgumentParser(description='Demo app demonstrates how ios_rpc works.') + parser.add_argument('--host', required=True, type=str, help='Adress of rpc server') + parser.add_argument('--port', type=int, default=9090, help='rpc port (default: 9090)') + parser.add_argument('--key', type=str, default='iphone', help='device key (default: iphone)') + parser.add_argument('--mode', type=str, default='tracker', + help='type of RPC connection (default: tracker), possible values: {}'.format(", ".join(MODES.keys()))) + + args = parser.parse_args() + assert(args.mode in MODES.keys()) + test_mobilenet(args.host, args.port, args.key, args.mode) diff --git a/apps/ios_rpc/tests/ios_rpc_test.py b/apps/ios_rpc/tests/ios_rpc_test.py index 57da3d45e2d1..52322518a974 100644 --- a/apps/ios_rpc/tests/ios_rpc_test.py +++ b/apps/ios_rpc/tests/ios_rpc_test.py @@ -28,33 +28,26 @@ from tvm import rpc from tvm.contrib import utils, xcode import numpy as np - -# Set to be address of tvm proxy. -proxy_host = os.environ["TVM_IOS_RPC_PROXY_HOST"] -# Set your destination via env variable. -# Should in format "platform=iOS,id=" -destination = os.environ["TVM_IOS_RPC_DESTINATION"] - -if not re.match(r"^platform=.*,id=.*$", destination): - print("Bad format: {}".format(destination)) - print("Example of expected string: platform=iOS,id=1234567890abcabcabcabc1234567890abcabcab") - sys.exit(1) - -proxy_port = 9090 -key = "iphone" +import argparse # Change target configuration, this is setting for iphone6s arch = "arm64" sdk = "iphoneos" target = "llvm -mtriple=%s-apple-darwin" % arch +MODES = { + 'proxy': rpc.connect, + 'tracker': rpc.connect_tracker, + 'pure_server': rpc.connect +} + # override metal compiler to compile to iphone @tvm.register_func("tvm_callback_metal_compile") def compile_metal(src): return xcode.compile_metal(src, sdk=sdk) -def test_rpc_module(): +def test_rpc_module(host, port, key, mode): # graph n = tvm.runtime.convert(1024) A = te.placeholder((n,), name="A") @@ -81,8 +74,13 @@ def test_rpc_module(): f.export_library(path_dso2, xcode.create_dylib, arch=arch, sdk=sdk) xcode.codesign(path_dso2) + print("before connection") # connect to the proxy - remote = rpc.connect(proxy_host, proxy_port, key=key) + if mode == "tracker": + remote = MODES[mode](host, port).request(key) + else: + remote = MODES[mode](host, port, key=key) + print("after connection") remote.upload(path_dso1) dev = remote.metal(0) f1 = remote.load_module("dev_lib.dylib") @@ -107,4 +105,13 @@ def test_rpc_module(): if __name__ == "__main__": - test_rpc_module() + parser = argparse.ArgumentParser(description='Demo app demonstrates how ios_rpc works.') + parser.add_argument('--host', required=True, type=str, help='Adress of rpc server') + parser.add_argument('--port', type=int, default=9090, help='rpc port (default: 9090)') + parser.add_argument('--key', type=str, default='iphone', help='device key (default: iphone)') + parser.add_argument('--mode', type=str, default='tracker', + help='type of RPC connection (default: tracker), possible values: {}'.format(", ".join(MODES.keys()))) + + args = parser.parse_args() + assert(args.mode in MODES.keys()) + test_rpc_module(args.host, args.port, args.key, args.mode) From d47314c8b6bde20f0408df4e05a8d383e9aa5926 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Fri, 10 Sep 2021 09:29:54 +0300 Subject: [PATCH 17/23] Remove tvmLauncher from ios_rpc --- apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj | 143 +----------------- .../xcshareddata/xcschemes/tvmrpc.xcscheme | 10 -- tests/lint/check_file_type.py | 1 + 3 files changed, 2 insertions(+), 152 deletions(-) diff --git a/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj b/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj index 1f5d30abb5b0..24a84397e77e 100644 --- a/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj +++ b/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj @@ -37,20 +37,9 @@ C026375E1F1C25E8007247A9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C026375C1F1C25E8007247A9 /* LaunchScreen.storyboard */; }; C02637661F1C2690007247A9 /* TVMRuntime.mm in Sources */ = {isa = PBXBuildFile; fileRef = C02637651F1C2690007247A9 /* TVMRuntime.mm */; }; C02637691F1C26AF007247A9 /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = C02637681F1C26AF007247A9 /* ViewController.mm */; }; - C05A2C891F1DCE0900D4798B /* tvmrpcLauncher.mm in Sources */ = {isa = PBXBuildFile; fileRef = C05A2C881F1DCE0900D4798B /* tvmrpcLauncher.mm */; }; D7685AD324390EAE00D1469C /* CoreML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7685AD224390EAD00D1469C /* CoreML.framework */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - C05A2C8B1F1DCE0900D4798B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C02637431F1C25E8007247A9 /* Project object */; - proxyType = 1; - remoteGlobalIDString = C026374A1F1C25E8007247A9; - remoteInfo = tvmrpc; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 01A9B7B9265BD307000D092F /* Embed Libraries */ = { isa = PBXCopyFilesBuildPhase; @@ -82,9 +71,6 @@ C026375F1F1C25E8007247A9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C02637651F1C2690007247A9 /* TVMRuntime.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TVMRuntime.mm; sourceTree = ""; }; C02637681F1C26AF007247A9 /* ViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = ""; }; - C05A2C861F1DCE0900D4798B /* tvmrpcLauncher.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = tvmrpcLauncher.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - C05A2C881F1DCE0900D4798B /* tvmrpcLauncher.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = tvmrpcLauncher.mm; sourceTree = ""; }; - C05A2C8A1F1DCE0900D4798B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D7685AD224390EAD00D1469C /* CoreML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreML.framework; path = System/Library/Frameworks/CoreML.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -98,13 +84,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C05A2C831F1DCE0900D4798B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -112,7 +91,6 @@ isa = PBXGroup; children = ( C026374D1F1C25E8007247A9 /* tvmrpc */, - C05A2C871F1DCE0900D4798B /* tvmrpcLauncher */, C026374C1F1C25E8007247A9 /* Products */, D7685AD124390EAD00D1469C /* Frameworks */, ); @@ -124,7 +102,6 @@ isa = PBXGroup; children = ( C026374B1F1C25E8007247A9 /* tvmrpc.app */, - C05A2C861F1DCE0900D4798B /* tvmrpcLauncher.xctest */, ); name = Products; sourceTree = ""; @@ -158,15 +135,6 @@ name = "Supporting Files"; sourceTree = ""; }; - C05A2C871F1DCE0900D4798B /* tvmrpcLauncher */ = { - isa = PBXGroup; - children = ( - C05A2C881F1DCE0900D4798B /* tvmrpcLauncher.mm */, - C05A2C8A1F1DCE0900D4798B /* Info.plist */, - ); - path = tvmrpcLauncher; - sourceTree = ""; - }; D7685AD124390EAD00D1469C /* Frameworks */ = { isa = PBXGroup; children = ( @@ -186,7 +154,6 @@ C02637471F1C25E8007247A9 /* Sources */, C02637481F1C25E8007247A9 /* Frameworks */, C02637491F1C25E8007247A9 /* Resources */, - C05A2C901F1E683A00D4798B /* ShellScript */, 01A9B7B9265BD307000D092F /* Embed Libraries */, ); buildRules = ( @@ -198,24 +165,6 @@ productReference = C026374B1F1C25E8007247A9 /* tvmrpc.app */; productType = "com.apple.product-type.application"; }; - C05A2C851F1DCE0900D4798B /* tvmrpcLauncher */ = { - isa = PBXNativeTarget; - buildConfigurationList = C05A2C8F1F1DCE0900D4798B /* Build configuration list for PBXNativeTarget "tvmrpcLauncher" */; - buildPhases = ( - C05A2C821F1DCE0900D4798B /* Sources */, - C05A2C831F1DCE0900D4798B /* Frameworks */, - C05A2C841F1DCE0900D4798B /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - C05A2C8C1F1DCE0900D4798B /* PBXTargetDependency */, - ); - name = tvmrpcLauncher; - productName = tvmrpcLauncher; - productReference = C05A2C861F1DCE0900D4798B /* tvmrpcLauncher.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -226,15 +175,9 @@ ORGANIZATIONNAME = dmlc; TargetAttributes = { C026374A1F1C25E8007247A9 = { - CreatedOnToolsVersion = 8.3.3; - DevelopmentTeam = B2Q598A43U; - ProvisioningStyle = Automatic; - }; - C05A2C851F1DCE0900D4798B = { CreatedOnToolsVersion = 8.3.3; DevelopmentTeam = 3FR42MXLK9; ProvisioningStyle = Automatic; - TestTargetID = C026374A1F1C25E8007247A9; }; }; }; @@ -243,6 +186,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -252,7 +196,6 @@ projectRoot = ""; targets = ( C026374A1F1C25E8007247A9 /* tvmrpc */, - C05A2C851F1DCE0900D4798B /* tvmrpcLauncher */, ); }; /* End PBXProject section */ @@ -268,31 +211,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C05A2C841F1DCE0900D4798B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - C05A2C901F1E683A00D4798B /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "libpath=${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Frameworks/tvm\nmkdir -p ${libpath}\nrm -rf ${libpath}/*\n \nif [ -f ${SRCROOT}/rpc_config.txt ]; then\n head -n 1 ${SRCROOT}/rpc_config.txt > ${libpath}/rpc_config.txt\n tail -n +2 ${SRCROOT}/rpc_config.txt | xargs -J % cp -r % ${libpath}\nfi\n\n"; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ C02637471F1C25E8007247A9 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -307,24 +227,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C05A2C821F1DCE0900D4798B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C05A2C891F1DCE0900D4798B /* tvmrpcLauncher.mm in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - C05A2C8C1F1DCE0900D4798B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C026374A1F1C25E8007247A9 /* tvmrpc */; - targetProxy = C05A2C8B1F1DCE0900D4798B /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ C02637571F1C25E8007247A9 /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -517,42 +421,6 @@ }; name = Release; }; - C05A2C8D1F1DCE0900D4798B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = 3FR42MXLK9; - HEADER_SEARCH_PATHS = ( - ../../3rdparty/dlpack/include, - ../../include, - "../../3rdparty/dmlc-core/include", - ); - INFOPLIST_FILE = tvmrpcLauncher/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmrpcLauncher; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/tvmrpc.app/tvmrpc"; - }; - name = Debug; - }; - C05A2C8E1F1DCE0900D4798B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = 3FR42MXLK9; - HEADER_SEARCH_PATHS = ( - ../../3rdparty/dlpack/include, - ../../include, - "../../3rdparty/dmlc-core/include", - ); - INFOPLIST_FILE = tvmrpcLauncher/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmrpcLauncher; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/tvmrpc.app/tvmrpc"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -574,15 +442,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - C05A2C8F1F1DCE0900D4798B /* Build configuration list for PBXNativeTarget "tvmrpcLauncher" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C05A2C8D1F1DCE0900D4798B /* Debug */, - C05A2C8E1F1DCE0900D4798B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = C02637431F1C25E8007247A9 /* Project object */; diff --git a/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme b/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme index 5835fa83b2df..b84b83a939e7 100644 --- a/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme +++ b/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme @@ -28,16 +28,6 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - Date: Fri, 10 Sep 2021 10:17:30 +0300 Subject: [PATCH 18/23] Add updating tvm_build_dir in init_proj script --- apps/ios_rpc/init_proj.py | 10 ++++++++++ apps/ios_rpc/tests/ios_rpc_test.py | 6 ++---- apps/ios_rpc/tvmrpc/ViewController.mm | 1 - 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/ios_rpc/init_proj.py b/apps/ios_rpc/init_proj.py index deee86ce4876..9961f5a55da8 100644 --- a/apps/ios_rpc/init_proj.py +++ b/apps/ios_rpc/init_proj.py @@ -19,6 +19,7 @@ default_team_id = "3FR42MXLK9" default_bundle_identifier = "org.apache.tvmrpc" +default_tvm_build_dir = "path-to-tvm-ios-build-folder" parser = argparse.ArgumentParser( description="Update tvmrpc.xcodeproj\ @@ -37,6 +38,13 @@ ), ) +parser.add_argument( + "--tvm_build_dir", + type=str, + required=True, + help="Path to directory with libtvm_runtime.dylib", +) + parser.add_argument( "--bundle_identifier", type=str, @@ -50,6 +58,7 @@ args = parser.parse_args() team_id = args.team_id +tvm_build_dir = args.tvm_build_dir bundle_identifier = args.bundle_identifier fi = open("tvmrpc.xcodeproj/project.pbxproj") @@ -57,6 +66,7 @@ fi.close() proj_config = proj_config.replace(default_team_id, team_id) +proj_config = proj_config.replace(default_tvm_build_dir, tvm_build_dir) proj_config = proj_config.replace(default_bundle_identifier, bundle_identifier) fo = open("tvmrpc.xcodeproj/project.pbxproj", "w") fo.write(proj_config) diff --git a/apps/ios_rpc/tests/ios_rpc_test.py b/apps/ios_rpc/tests/ios_rpc_test.py index 52322518a974..a361bce17949 100644 --- a/apps/ios_rpc/tests/ios_rpc_test.py +++ b/apps/ios_rpc/tests/ios_rpc_test.py @@ -74,13 +74,11 @@ def test_rpc_module(host, port, key, mode): f.export_library(path_dso2, xcode.create_dylib, arch=arch, sdk=sdk) xcode.codesign(path_dso2) - print("before connection") # connect to the proxy if mode == "tracker": remote = MODES[mode](host, port).request(key) else: remote = MODES[mode](host, port, key=key) - print("after connection") remote.upload(path_dso1) dev = remote.metal(0) f1 = remote.load_module("dev_lib.dylib") @@ -89,7 +87,7 @@ def test_rpc_module(host, port, key, mode): b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev) time_f = f1.time_evaluator(f1.entry_name, dev, number=10) cost = time_f(a, b).mean - print("%g secs/op" % cost) + print("Metal: %g secs/op" % cost) np.testing.assert_equal(b.numpy(), a.numpy() + 1) # CPU dev = remote.cpu(0) @@ -100,7 +98,7 @@ def test_rpc_module(host, port, key, mode): b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev) time_f = f2.time_evaluator(f2.entry_name, dev, number=10) cost = time_f(a, b).mean - print("%g secs/op" % cost) + print("CPU: %g secs/op" % cost) np.testing.assert_equal(b.numpy(), a.numpy() + 1) diff --git a/apps/ios_rpc/tvmrpc/ViewController.mm b/apps/ios_rpc/tvmrpc/ViewController.mm index a13f60247bcb..67dba4b356b2 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.mm +++ b/apps/ios_rpc/tvmrpc/ViewController.mm @@ -95,7 +95,6 @@ - (void)open { server_.key = self.proxyKey.text; server_.custom_addr = [NSString stringWithUTF8String:args.custom_addr]; server_.delegate = self; - NSLog(@"value: %d", server_.verbose); [server_ start]; From 9447285f06201df03979141ef3a1fa94d86950be Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Fri, 10 Sep 2021 17:19:45 +0300 Subject: [PATCH 19/23] Update default bundle --- apps/ios_rpc/init_proj.py | 14 -------------- apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/apps/ios_rpc/init_proj.py b/apps/ios_rpc/init_proj.py index 9961f5a55da8..9044a9e8cbbf 100644 --- a/apps/ios_rpc/init_proj.py +++ b/apps/ios_rpc/init_proj.py @@ -18,7 +18,6 @@ import re default_team_id = "3FR42MXLK9" -default_bundle_identifier = "org.apache.tvmrpc" default_tvm_build_dir = "path-to-tvm-ios-build-folder" parser = argparse.ArgumentParser( @@ -45,21 +44,9 @@ help="Path to directory with libtvm_runtime.dylib", ) -parser.add_argument( - "--bundle_identifier", - type=str, - required=False, - default=default_bundle_identifier, - help="The new bundle identifier\n\ - (example: {})".format( - default_bundle_identifier - ), -) - args = parser.parse_args() team_id = args.team_id tvm_build_dir = args.tvm_build_dir -bundle_identifier = args.bundle_identifier fi = open("tvmrpc.xcodeproj/project.pbxproj") proj_config = fi.read() @@ -67,7 +54,6 @@ proj_config = proj_config.replace(default_team_id, team_id) proj_config = proj_config.replace(default_tvm_build_dir, tvm_build_dir) -proj_config = proj_config.replace(default_bundle_identifier, bundle_identifier) fo = open("tvmrpc.xcodeproj/project.pbxproj", "w") fo.write(proj_config) fo.close() diff --git a/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj b/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj index 24a84397e77e..61427d0ca248 100644 --- a/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj +++ b/apps/ios_rpc/tvmrpc.xcodeproj/project.pbxproj @@ -376,7 +376,7 @@ "${TVM_BUILD_DIR}", ); OTHER_LDFLAGS = "${_DSO_LOADER_NAME_${USE_CUSTOM_DSO_LOADER}}"; - PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmrpc; + PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmiosrpc; PRODUCT_NAME = "$(TARGET_NAME)"; TVM_BUILD_DIR = "path-to-tvm-ios-build-folder"; USE_CUSTOM_DSO_LOADER = 1; @@ -411,7 +411,7 @@ "${TVM_BUILD_DIR}", ); OTHER_LDFLAGS = "${_DSO_LOADER_NAME_${USE_CUSTOM_DSO_LOADER}}"; - PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmrpc; + PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmiosrpc; PRODUCT_NAME = "$(TARGET_NAME)"; TVM_BUILD_DIR = "path-to-tvm-ios-build-folder"; USE_CUSTOM_DSO_LOADER = 1; From 8526a9a73d7028c91b706af56f460b032df92f27 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Fri, 10 Sep 2021 12:24:59 +0300 Subject: [PATCH 20/23] Update README.md for ios_rpc --- apps/ios_rpc/README.md | 300 +++++++++++++++++------- apps/ios_rpc/tests/ios_rpc_mobilenet.py | 1 - apps/ios_rpc/tests/ios_rpc_test.py | 2 - python/tvm/contrib/xcode.py | 23 -- 4 files changed, 211 insertions(+), 115 deletions(-) diff --git a/apps/ios_rpc/README.md b/apps/ios_rpc/README.md index 4255ed92e264..32fbfb682c01 100644 --- a/apps/ios_rpc/README.md +++ b/apps/ios_rpc/README.md @@ -17,84 +17,21 @@ # iOS TVM RPC -This folder contains iOS RPC app that allows us to launch an rpc server on a iOS device(e.g. ipython) -and connect to it through python script and do testing on the python side as normal TVM RPC. -You will need XCode and an iOS device to use this. - -## RPC proxy -Start the RPC proxy by running in a terminal: - - python -m tvm.exec.rpc_proxy - -On success, you should see something like this: - - INFO:root:RPCProxy: client port bind to 0.0.0.0:9090 - INFO:root:RPCProxy: Websock port bind to 8888 - -IP-address of this machine will be used to initialize ```TVM_IOS_RPC_PROXY_HOST``` -environment variable (see below). +This folder contains iOS RPC app that allows us to launch an rpc server on a iOS +device. You will need XCode and an iOS device to use this. + +## Table of Contents +* [Building](#building) + * [Building TVM runtime and custom DSO loader plugin](#building-tvm-runtime-and-custom-dso-loader-plugin) + * [Building iOS TVM RPC application](#building-ios-tvm-rpc-application) +* [Workflow](#workflow) + * [Pure RPC](#pure-rpc) + * [iOS RPC App with proxy](#ios-rpc-app-with-proxy) + * [iOS RPC App with tracker](#ios-rpc-app-with-tracker) +* [Communication without Wi-Fi and speed up in case of slow Wi-Fi](#communication-without-wi-fi-and-speed-up-in-case-of-slow-wi-fi) ## Building -Before start, please run ```init_proj.py``` to update XCode developer metadata. After this step, open -```tvmrpc.xcodeproj``` by using XCode, build the App and install the App on the phone. Usually, we -**do not** use the iOS App directly. - -To test an App, you can fill ``Address`` field with IP-address of RPC proxy -(see above), and press ``Connect to Proxy``. - -On success, "Disconnected" will change to "Connected". -On RPC proxy side you can see the next message in a log: - - INFO:root:Handler ready TCPSocketProxy::server:iphone - -Now App can be closed by pressing the home button (or even removed from a device). - -## Workflow -Due to security restriction of iOS10. We cannot upload dynamic libraries to the App and load it from sandbox. -Instead, we need to build a list of libraries, pack them into the app bundle, launch the RPC server and -connect to test the bundled libraries. We use ```xcodebuild test``` to automate this process. There is also -one more approach to workaround this limitation, for more details please take a look into section -[Custom DSO loader integration](#custom-dso-loader-plugin). - -The test script [tests/ios_rpc_test.py](tests/ios_rpc_test.py) is a good template for the workflow. With this -script, we don't need to manually operate the iOS App, this script will build the app, run it and collect the results -automatically. - - To run the script, you need to configure the following environment variables - -- ```TVM_IOS_CODESIGN``` The signature you use to codesign the app and libraries (e.g. ```iPhone Developer: Name (XXXX)```) -- ```TVM_IOS_TEAM_ID``` The developer Team ID available at https://developer.apple.com/account/#/membership -- ```TVM_IOS_RPC_ROOT``` The root directory of the iOS rpc project -- ```TVM_IOS_RPC_PROXY_HOST``` The RPC proxy address (see above) -- ```TVM_IOS_RPC_DESTINATION``` The Xcode target device (e.g. ```platform=iOS,id=xxxx```) - -See instructions of how to find UUID of the iOS device: - -- https://www.innerfence.com/howto/find-iphone-unique-device-identifier-udid - -## How it works -Let us explain how it works, the project look for ```rpc_config.txt``` file in the project root folder. -The ```rpc_config.txt``` file should be in the following format: -``` - -[path to dylib1] -[path to dylib2] -... -``` -The build script will copy all the dynamic libraries into bundle ```tvmrpc.app/Frameworks/tvm```, -which you will be able to load via RPC using ```remote.load_module```. -It will also create an ```tvmrpc.app/Frameworks/tvm/rpc_config.txt``` containing the first line. - -When we run the testcase, the testcase read the configuration from ```tvmrpc.app/Frameworks/tvm/rpc_config.txt``` -and connect to the specified RPC proxy, start serving loop. - -So if we want to start the RPC from XCode IDE, simply manually modify ```rpc_config.txt``` file and click test. -Then connect to the proxy via the python script. - -We can also use the RPC App directly, by typing in the address and press connect to connect to the proxy. -However, the restriction is we can only load the modules that are bundled to the App. - -## Custom DSO loader plugin +### Building TVM runtime and custom DSO loader plugin While iOS platform itself doesn't allow us to run an unsigned binary, where is a partial ability to run JIT code on real iOS devices. While application is running under debug session, system allows allocating memory with write and execute permissions (requirements of debugger). So we can use this feature to load binary on RPC side. For this @@ -103,7 +40,13 @@ This custom `dlopen` mechanic is integrated into TVM RPC as plugin and registere application. The custom implementation of `dlopen` and other functions from `dlfcn.h` header are placed in separate repository, -and will be downloaded automatically during cmake build for iOS. To run cmake build you may use next flags: +and will be downloaded automatically during cmake build for iOS. + +Also, it is necessary to build `tvm_runtime.dylib` for our iOS device. The iOS +TVM RPC application will be linked with this library. Use the following cmake +flags: + +To run cmake build you may use next flags: ```shell export DEVELOPER_DIR=/Applications/Xcode.app # iOS SDK is part of Xcode bundle. Have to set it as default Dev Env cmake .. @@ -114,19 +57,198 @@ cmake .. -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=ON - -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=XXXXXXXXXX # insert your Team ID -DUSE_IOS_RPC=ON # to enable build iOS RPC application from TVM project tree -cmake --build . --target custom_dso_loader ios_rpc # Will use custom DSO loader by default -# Resulting iOS RPC app bundle will be placed in: -# apps/ios_rpc/ios_rpc/src/ios_rpc-build/[CONFIG]-iphoneos/tvmrpc.app + -DUSE_METAL=ON # to enable Metal runtime + +cmake --build . --target custom_dso_loader tvm_runtime +``` + +### Building iOS TVM RPC application +Before start, please run [init_proj.py](./init_proj.py) to update XCode developer metadata: +```shell +python3 init_proj.py --team_id XXXXXXXXXX --tvm_build_dir "/path/to/tvm/ios/build/folder" +``` +You can get value of your `team_id` in the following ways: +- **You have registered Apple Developer Profile**. In this case you developer + Team ID available at https://developer.apple.com/account/#/membership +- You are using your local developer profile. In this case run the command above + with `XXXXXXXXXX` instead of Team ID. Then open `tvmrpc.xcodeproj` by using + XCode, click on the project name (`tvmrpc`) on the left panel. Then select + target `tvmrpc`. At the bottom of this panel go to `Signing & Capabilities` + tab and in the field `Team` select your local developer profile + (`Your Name (Personal Team)`). + + On the first run of the application you may see message `Could not launch + "tvmrpc"` in the XCode and message `Untrusted Developer` on your device. In + this case it will be necessary to check the certificate. Open + `Settings -> General -> Device Management -> Apple Development: + -> Trust "Apple Development: "` and click `Trust`. After than you + should rerun your application in the XCode. + +After this step, open `tvmrpc.xcodeproj` by using XCode, build the App and +install the App on the phone. + +## Workflow +Due to security restriction of iOS10. We cannot upload dynamic libraries to the +App and load it from sandbox. Instead, we need to build a list of libraries, +pack them into the app bundle, launch the RPC server and connect to test the +bundled libraries. We use one approach to workaround this limitation, for more +details please take a look into section +[Building TVM runtime and custom DSO loader integration](#building-tvm-runtime-and-custom-DSO-loader-plugin). + +The test script [tests/ios_rpc_test.py](tests/ios_rpc_test.py) and +[tests/ios_rpc_mobilenet.py](tests/ios_rpc_mobilenet.py) are good templates for +demonstrating the workflow. + +We have three different modes for iOS RPC server: +- [Pure RPC](#pure-rpc): In this mode RPC server open port on the device and listening. Then + client connects to the server directly without any mediators. +- [iOS RPC application with Proxy](#ios-rpc-app-with-proxy): RPC server and RPC client communicates through + `rpc_proxy`. The RPC server on iOS device notify `rpc_proxy` which was run on + host machine about itself and wait for incoming connections. Communications + between client and server works through `rpc_proxy`. +- [iOS RPC application with Tracker](#ios-rpc-app-with-tracker): RPC server registered in the `rpc_tracker` + and client connects to the RPC server through `rpc_tracker`. + +### Pure RPC +Start RPC server on your iOS device: +- Enable verbose output. +- Push on the `Connect` button. + +After that you supposed to see something like this in the app on the device: +``` +IP: +Port: ``` -To enable using of Custom DSO Plugin during xcode build outsde of Cmake you should specify two additional variables. -You can do it manually inside Xcode IDE or via command line args for `xcodebuild`. Make sure that `custom_dso_loader` -target from previous step is already built. -* TVM_BUILD_DIR=path-to-tvm-ios-build-dir -* USE_CUSTOM_DSO_LOADER=1 +Printed `IP` is the IP address of your device and `PORT` is the number of port +which was open for RPC connection. Next you should use them for connect your RPC +client to the server. -iOS RPC application with enabled custom DSO loader is able to process modules passed via regular -`remote.upload("my_module.dylib")` mechanics. For example take a look inside `test_rpc_module_with_upload` test case -of file [ios_rpc_test.py](tests/ios_rpc_test.py). +Let's check that direct RPC connection works and we can upload a library with +model and execute it on the device. For this purpose we will use +[ios_rpc_test.py](tests/ios_rpc_test.py). Run it: +```shell +python3 tests/ios_rpc_test.py --host --port --mode "pure_server" +``` +This will compile TVM IR to shared libraries (CPU and Metal) and run vector +addition on your iOS device. You are supposed to see something like this: +``` +Metal: 0.000338692 secs/op +CPU: 0.000219308 secs/op +``` + +### iOS RPC App with proxy +Start the RPC proxy by running in a terminal: +```shell +python3 -m tvm.exec.rpc_proxy --host 0.0.0.0 --port 9090 +``` + +On success, you should see something like this: +``` +INFO:root:RPCProxy: client port bind to 0.0.0.0:9090 +INFO:root:RPCProxy: Websock port bind to 8888 +``` +Connect your iOS device to the RPC proxy via the iOS TVM RPC application. Set +the `Address` and `Port` fields to the address and port of the RPC tracker +respectively. Select mode `Proxy` and push `Connect` button. In success the +text on the button will be changed to `Disconnect` and `Disconnected` in the top +of the screen will be changed to `Connected`. +On RPC proxy side you can see the next message in a log: +``` +INFO:root:Handler ready TCPSocketProxy::server:iphone +``` +Then we can check that RPC connection works and we can upload a library with +model and execute it on the target device. For this purpose we will use +[ios_rpc_test.py](tests/ios_rpc_test.py). Run it: +```shell +python3 tests/ios_rpc_test.py --host --port 9090 --mode "proxy" +``` +The output should be the same as it was in previous section. + +### iOS RPC App with tracker +First start an RPC tracker using +```shell +python3 -m tvm.exec.rpc_tracker --host 0.0.0.0 --port 9190 +``` +On success, you should see something like this: +``` +INFO:RPCTracker:bind to 0.0.0.0:9190 +``` +Connect your iOS device to the RPC tracker via the iOS TVM RPC applcation. Set +the `Address` and `Port` fields to the address and port of the RPC tracker +respectively. Select mode `Tracker` and push `Connect` button. In success the +text on the button will be changed to `Disconnect` and `Disconnected` in the top +of the screen will be changed to `Connected`. On the host side you can check the +connect by the following command: +```shell +python3 -m tvm.exec.query_rpc_tracker --port 9190 +``` +You are supposed to see something like this: +``` +Tracker address 127.0.0.1:9190 + +Server List +---------------------------- +server-address key +---------------------------- +192.168.1.57:9190 server:iphone +---------------------------- + +Queue Status +------------------------------ +key total free pending +------------------------------ +iphone 1 1 0 +------------------------------ +``` + +Then we can check that RPC connection works and we can upload a library with +model and execute it on the target device. For this purpose we will use +[ios_rpc_test.py](tests/ios_rpc_test.py). Run it: +```shell +python3 tests/ios_rpc_test.py --host --port 9190 --mode "tracker" +``` +The output will be the same as in section +[Pure RPC](#pure-rpc). + +## Communication without Wi-Fi and speed up in case of slow Wi-Fi +Connection to the RPC server through `usbmux` can be used then you have slow, +unstable or don't have any Wi-Fi connection. `usbmux` is used for binding local +TCP port to port on the device and transfer packages between these ports by USB +cable. + +First of all you should install `usbmux` to your system. You can do it with +brew: +```shell +brew install usbmuxd +``` +After that you can use `iproxy` program for binding ports. You can use it for +all described workflows. Let's take a look how it works for [Pure RPC](#pure-rpc). + +First, start RPC server on your iOS device. You may see something like this in +the app on the device: +``` +IP: unknown +Port: +``` +**Note.** Here `IP: unknown` because there was no Internet connection on the iOS +device. +Printed `Port` is the port of the RPC server on your iOS device. We will use it +in binding ports. Run `iproxy`, specify local port which should be used for +communication with device and the printed port on the device: +```shell +iproxy : +``` +After this command you should see something like this: +``` +Creating listening port for device port +waiting for connection +``` +Now we can check that RPC connection through `usbmux` works and we can upload a +library with model and execute it on the device. For this purpose we will use +[ios_rpc_test.py](tests/ios_rpc_test.py). Run it: +```shell +python3 tests/ios_rpc_test.py --host 0.0.0.0 --port --mode pure_server +``` +The output should be the same as in all previous runs. diff --git a/apps/ios_rpc/tests/ios_rpc_mobilenet.py b/apps/ios_rpc/tests/ios_rpc_mobilenet.py index 62b04f95eccc..f4f9256390a8 100644 --- a/apps/ios_rpc/tests/ios_rpc_mobilenet.py +++ b/apps/ios_rpc/tests/ios_rpc_mobilenet.py @@ -100,7 +100,6 @@ def run(mod, target): lib = relay.build(mod, target=target, target_host=target_host, params=params) path_dso = temp.relpath("deploy.dylib") lib.export_library(path_dso, xcode.create_dylib, arch=arch, sdk=sdk) - xcode.codesign(path_dso) # connect to the proxy if mode == "tracker": diff --git a/apps/ios_rpc/tests/ios_rpc_test.py b/apps/ios_rpc/tests/ios_rpc_test.py index a361bce17949..d15a012769f7 100644 --- a/apps/ios_rpc/tests/ios_rpc_test.py +++ b/apps/ios_rpc/tests/ios_rpc_test.py @@ -62,7 +62,6 @@ def test_rpc_module(host, port, key, mode): f = tvm.build(s, [A, B], "metal", target_host=target, name="myadd") path_dso1 = temp.relpath("dev_lib.dylib") f.export_library(path_dso1, xcode.create_dylib, arch=arch, sdk=sdk) - xcode.codesign(path_dso1) s = te.create_schedule(B.op) xo, xi = s[B].split(B.op.axis[0], factor=64) @@ -72,7 +71,6 @@ def test_rpc_module(host, port, key, mode): f = tvm.build(s, [A, B], target, name="myadd_cpu") path_dso2 = temp.relpath("cpu_lib.dylib") f.export_library(path_dso2, xcode.create_dylib, arch=arch, sdk=sdk) - xcode.codesign(path_dso2) # connect to the proxy if mode == "tracker": diff --git a/python/tvm/contrib/xcode.py b/python/tvm/contrib/xcode.py index bf3952587f33..c44a2fe4a136 100644 --- a/python/tvm/contrib/xcode.py +++ b/python/tvm/contrib/xcode.py @@ -45,29 +45,6 @@ def xcrun(cmd): return out.strip() -def codesign(lib): - """Codesign the shared libary - - This is an required step for library to be loaded in - the app. - - Parameters - ---------- - lib : The path to the library. - """ - if "TVM_IOS_CODESIGN" not in os.environ: - raise RuntimeError("Require environment variable TVM_IOS_CODESIGN " " to be the signature") - signature = os.environ["TVM_IOS_CODESIGN"] - cmd = ["codesign", "--force", "--sign", signature] - cmd += [lib] - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (out, _) = proc.communicate() - if proc.returncode != 0: - msg = "Codesign error:\n" - msg += py_str(out) - raise RuntimeError(msg) - - def create_dylib(output, objects, arch, sdk="macosx"): """Create dynamic library. From 35589e443a8e1419280363629f1d41cf4161ecd5 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Wed, 15 Sep 2021 07:38:14 +0300 Subject: [PATCH 21/23] Fix lint --- apps/ios_rpc/tests/ios_rpc_mobilenet.py | 26 ++++++++++--------- apps/ios_rpc/tests/ios_rpc_test.py | 26 ++++++++++--------- .../xcshareddata/xcschemes/tvmrpc.xcscheme | 17 ++++++++++++ apps/ios_rpc/tvmrpc/ViewController.mm | 3 ++- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/apps/ios_rpc/tests/ios_rpc_mobilenet.py b/apps/ios_rpc/tests/ios_rpc_mobilenet.py index f4f9256390a8..3985b1175f0d 100644 --- a/apps/ios_rpc/tests/ios_rpc_mobilenet.py +++ b/apps/ios_rpc/tests/ios_rpc_mobilenet.py @@ -41,11 +41,7 @@ sdk = "iphoneos" target_host = "llvm -mtriple=%s-apple-darwin" % arch -MODES = { - 'proxy': rpc.connect, - 'tracker': rpc.connect_tracker, - 'pure_server': rpc.connect -} +MODES = {"proxy": rpc.connect, "tracker": rpc.connect_tracker, "pure_server": rpc.connect} # override metal compiler to compile to iphone @tvm.register_func("tvm_callback_metal_compile") @@ -168,13 +164,19 @@ def annotate(func, compiler): if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Demo app demonstrates how ios_rpc works.') - parser.add_argument('--host', required=True, type=str, help='Adress of rpc server') - parser.add_argument('--port', type=int, default=9090, help='rpc port (default: 9090)') - parser.add_argument('--key', type=str, default='iphone', help='device key (default: iphone)') - parser.add_argument('--mode', type=str, default='tracker', - help='type of RPC connection (default: tracker), possible values: {}'.format(", ".join(MODES.keys()))) + parser = argparse.ArgumentParser(description="Demo app demonstrates how ios_rpc works.") + parser.add_argument("--host", required=True, type=str, help="Adress of rpc server") + parser.add_argument("--port", type=int, default=9090, help="rpc port (default: 9090)") + parser.add_argument("--key", type=str, default="iphone", help="device key (default: iphone)") + parser.add_argument( + "--mode", + type=str, + default="tracker", + help="type of RPC connection (default: tracker), possible values: {}".format( + ", ".join(MODES.keys()) + ), + ) args = parser.parse_args() - assert(args.mode in MODES.keys()) + assert args.mode in MODES.keys() test_mobilenet(args.host, args.port, args.key, args.mode) diff --git a/apps/ios_rpc/tests/ios_rpc_test.py b/apps/ios_rpc/tests/ios_rpc_test.py index d15a012769f7..ae0fcb5fdc80 100644 --- a/apps/ios_rpc/tests/ios_rpc_test.py +++ b/apps/ios_rpc/tests/ios_rpc_test.py @@ -35,11 +35,7 @@ sdk = "iphoneos" target = "llvm -mtriple=%s-apple-darwin" % arch -MODES = { - 'proxy': rpc.connect, - 'tracker': rpc.connect_tracker, - 'pure_server': rpc.connect -} +MODES = {"proxy": rpc.connect, "tracker": rpc.connect_tracker, "pure_server": rpc.connect} # override metal compiler to compile to iphone @tvm.register_func("tvm_callback_metal_compile") @@ -101,13 +97,19 @@ def test_rpc_module(host, port, key, mode): if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Demo app demonstrates how ios_rpc works.') - parser.add_argument('--host', required=True, type=str, help='Adress of rpc server') - parser.add_argument('--port', type=int, default=9090, help='rpc port (default: 9090)') - parser.add_argument('--key', type=str, default='iphone', help='device key (default: iphone)') - parser.add_argument('--mode', type=str, default='tracker', - help='type of RPC connection (default: tracker), possible values: {}'.format(", ".join(MODES.keys()))) + parser = argparse.ArgumentParser(description="Demo app demonstrates how ios_rpc works.") + parser.add_argument("--host", required=True, type=str, help="Adress of rpc server") + parser.add_argument("--port", type=int, default=9090, help="rpc port (default: 9090)") + parser.add_argument("--key", type=str, default="iphone", help="device key (default: iphone)") + parser.add_argument( + "--mode", + type=str, + default="tracker", + help="type of RPC connection (default: tracker), possible values: {}".format( + ", ".join(MODES.keys()) + ), + ) args = parser.parse_args() - assert(args.mode in MODES.keys()) + assert args.mode in MODES.keys() test_rpc_module(args.host, args.port, args.key, args.mode) diff --git a/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme b/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme index b84b83a939e7..42a53a2336a7 100644 --- a/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme +++ b/apps/ios_rpc/tvmrpc.xcodeproj/xcshareddata/xcschemes/tvmrpc.xcscheme @@ -1,4 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/apps/ios_rpc/tvmrpc/ViewController.mm b/apps/ios_rpc/tvmrpc/ViewController.mm index 67dba4b356b2..3b1f06e141eb 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.mm +++ b/apps/ios_rpc/tvmrpc/ViewController.mm @@ -150,7 +150,8 @@ - (void)onStatusChanged:(RPCServerStatus)status { switch (status) { case RPCServerStatus_Connected: if (self.ModeSelector.selectedSegmentIndex == RPCServerMode_PureServer) { - self.infoText.text = [NSString stringWithFormat:@"IP: %@\nPort: %d", server_.device_addr, server_.actual_port]; + self.infoText.text = [NSString + stringWithFormat:@"IP: %@\nPort: %d", server_.device_addr, server_.actual_port]; } self.statusLabel.text = @"Connected"; break; From d43039cdcb10b1cf3d5fbbbc0d313c0b16b24988 Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Mon, 20 Sep 2021 12:07:36 +0300 Subject: [PATCH 22/23] Apply comments --- apps/ios_rpc/README.md | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/ios_rpc/README.md b/apps/ios_rpc/README.md index 32fbfb682c01..f97b9ea970b7 100644 --- a/apps/ios_rpc/README.md +++ b/apps/ios_rpc/README.md @@ -32,21 +32,23 @@ device. You will need XCode and an iOS device to use this. ## Building ### Building TVM runtime and custom DSO loader plugin -While iOS platform itself doesn't allow us to run an unsigned binary, where is a partial ability to run JIT code -on real iOS devices. While application is running under debug session, system allows allocating memory with write -and execute permissions (requirements of debugger). So we can use this feature to load binary on RPC side. For this -purpose we use custom version of `dlopen` function which doesn't check signature and permissions for module loading. -This custom `dlopen` mechanic is integrated into TVM RPC as plugin and registered to execution only inside iOS RPC -application. +While iOS platform itself doesn't allow us to run an unsigned binary, there is a +partial ability to run JIT code on real iOS devices. While application is +running under debug session, system allows allocating memory with write and +execute permissions (a debugger requirement). So we can use this feature to +implement the `tvm.rpc.server.load_module` PackedFunc, used to load code over +RPC. For this purpose we use custom version of `dlopen` function which doesn't +check signature and permissions for module loading. This custom `dlopen` +mechanic is integrated into TVM RPC as plugin and registered to execution only +inside iOS RPC application. The custom implementation of `dlopen` and other functions from `dlfcn.h` header are placed in separate repository, and will be downloaded automatically during cmake build for iOS. -Also, it is necessary to build `tvm_runtime.dylib` for our iOS device. The iOS -TVM RPC application will be linked with this library. Use the following cmake -flags: +Also, it is necessary to build `libtvm_runtime.dylib` for our iOS device. The +iOS TVM RPC application will be linked with this library. -To run cmake build you may use next flags: +Run the build using the following commands: ```shell export DEVELOPER_DIR=/Applications/Xcode.app # iOS SDK is part of Xcode bundle. Have to set it as default Dev Env cmake .. @@ -71,11 +73,11 @@ python3 init_proj.py --team_id XXXXXXXXXX --tvm_build_dir "/path/to/tvm/ios/buil You can get value of your `team_id` in the following ways: - **You have registered Apple Developer Profile**. In this case you developer Team ID available at https://developer.apple.com/account/#/membership -- You are using your local developer profile. In this case run the command above - with `XXXXXXXXXX` instead of Team ID. Then open `tvmrpc.xcodeproj` by using - XCode, click on the project name (`tvmrpc`) on the left panel. Then select - target `tvmrpc`. At the bottom of this panel go to `Signing & Capabilities` - tab and in the field `Team` select your local developer profile +- You are using your local developer profile. In this case, leave `XXXXXXXXXX` + in the command instead of substituting a Team ID. Then open `tvmrpc.xcodeproj` + by using XCode, click on the project name (`tvmrpc`) on the left panel. Then + select target `tvmrpc`. At the bottom of this panel go to `Signing & + Capabilities` tab and in the field `Team` select your local developer profile (`Your Name (Personal Team)`). On the first run of the application you may see message `Could not launch @@ -92,8 +94,8 @@ install the App on the phone. Due to security restriction of iOS10. We cannot upload dynamic libraries to the App and load it from sandbox. Instead, we need to build a list of libraries, pack them into the app bundle, launch the RPC server and connect to test the -bundled libraries. We use one approach to workaround this limitation, for more -details please take a look into section +bundled libraries. For more on the approach we use to work around this +limitation, please take a look into section [Building TVM runtime and custom DSO loader integration](#building-tvm-runtime-and-custom-DSO-loader-plugin). The test script [tests/ios_rpc_test.py](tests/ios_rpc_test.py) and @@ -112,7 +114,6 @@ We have three different modes for iOS RPC server: ### Pure RPC Start RPC server on your iOS device: -- Enable verbose output. - Push on the `Connect` button. After that you supposed to see something like this in the app on the device: From f6a2f8f5062796cc827e7b7110519b5a3eb9f0cb Mon Sep 17 00:00:00 2001 From: Egor Churaev Date: Wed, 22 Sep 2021 09:28:26 +0300 Subject: [PATCH 23/23] Rename PureRPC to Standalone --- apps/ios_rpc/README.md | 15 ++++++++------- apps/ios_rpc/tests/ios_rpc_mobilenet.py | 2 +- apps/ios_rpc/tests/ios_rpc_test.py | 2 +- apps/ios_rpc/tvmrpc/RPCArgs.mm | 8 ++++---- apps/ios_rpc/tvmrpc/RPCServer.h | 12 ++++++------ apps/ios_rpc/tvmrpc/RPCServer.mm | 14 +++++++------- apps/ios_rpc/tvmrpc/ViewController.mm | 2 +- 7 files changed, 28 insertions(+), 27 deletions(-) diff --git a/apps/ios_rpc/README.md b/apps/ios_rpc/README.md index f97b9ea970b7..c268d15d0179 100644 --- a/apps/ios_rpc/README.md +++ b/apps/ios_rpc/README.md @@ -25,7 +25,7 @@ device. You will need XCode and an iOS device to use this. * [Building TVM runtime and custom DSO loader plugin](#building-tvm-runtime-and-custom-dso-loader-plugin) * [Building iOS TVM RPC application](#building-ios-tvm-rpc-application) * [Workflow](#workflow) - * [Pure RPC](#pure-rpc) + * [Standalone RPC](#standalone-rpc) * [iOS RPC App with proxy](#ios-rpc-app-with-proxy) * [iOS RPC App with tracker](#ios-rpc-app-with-tracker) * [Communication without Wi-Fi and speed up in case of slow Wi-Fi](#communication-without-wi-fi-and-speed-up-in-case-of-slow-wi-fi) @@ -103,7 +103,7 @@ The test script [tests/ios_rpc_test.py](tests/ios_rpc_test.py) and demonstrating the workflow. We have three different modes for iOS RPC server: -- [Pure RPC](#pure-rpc): In this mode RPC server open port on the device and listening. Then +- [Standalone RPC](#standalone-rpc): In this mode RPC server open port on the device and listening. Then client connects to the server directly without any mediators. - [iOS RPC application with Proxy](#ios-rpc-app-with-proxy): RPC server and RPC client communicates through `rpc_proxy`. The RPC server on iOS device notify `rpc_proxy` which was run on @@ -112,7 +112,7 @@ We have three different modes for iOS RPC server: - [iOS RPC application with Tracker](#ios-rpc-app-with-tracker): RPC server registered in the `rpc_tracker` and client connects to the RPC server through `rpc_tracker`. -### Pure RPC +### Standalone RPC Start RPC server on your iOS device: - Push on the `Connect` button. @@ -130,7 +130,7 @@ Let's check that direct RPC connection works and we can upload a library with model and execute it on the device. For this purpose we will use [ios_rpc_test.py](tests/ios_rpc_test.py). Run it: ```shell -python3 tests/ios_rpc_test.py --host --port --mode "pure_server" +python3 tests/ios_rpc_test.py --host --port --mode "standalone" ``` This will compile TVM IR to shared libraries (CPU and Metal) and run vector addition on your iOS device. You are supposed to see something like this: @@ -211,7 +211,7 @@ model and execute it on the target device. For this purpose we will use python3 tests/ios_rpc_test.py --host --port 9190 --mode "tracker" ``` The output will be the same as in section -[Pure RPC](#pure-rpc). +[Standalone RPC](#standalone-rpc). ## Communication without Wi-Fi and speed up in case of slow Wi-Fi Connection to the RPC server through `usbmux` can be used then you have slow, @@ -225,7 +225,8 @@ brew: brew install usbmuxd ``` After that you can use `iproxy` program for binding ports. You can use it for -all described workflows. Let's take a look how it works for [Pure RPC](#pure-rpc). +all described workflows. Let's take a look how it works for +[Standalone RPC](#standalone-rpc). First, start RPC server on your iOS device. You may see something like this in the app on the device: @@ -250,6 +251,6 @@ Now we can check that RPC connection through `usbmux` works and we can upload a library with model and execute it on the device. For this purpose we will use [ios_rpc_test.py](tests/ios_rpc_test.py). Run it: ```shell -python3 tests/ios_rpc_test.py --host 0.0.0.0 --port --mode pure_server +python3 tests/ios_rpc_test.py --host 0.0.0.0 --port --mode standalone ``` The output should be the same as in all previous runs. diff --git a/apps/ios_rpc/tests/ios_rpc_mobilenet.py b/apps/ios_rpc/tests/ios_rpc_mobilenet.py index 3985b1175f0d..e4caa329bdf5 100644 --- a/apps/ios_rpc/tests/ios_rpc_mobilenet.py +++ b/apps/ios_rpc/tests/ios_rpc_mobilenet.py @@ -41,7 +41,7 @@ sdk = "iphoneos" target_host = "llvm -mtriple=%s-apple-darwin" % arch -MODES = {"proxy": rpc.connect, "tracker": rpc.connect_tracker, "pure_server": rpc.connect} +MODES = {"proxy": rpc.connect, "tracker": rpc.connect_tracker, "standalone": rpc.connect} # override metal compiler to compile to iphone @tvm.register_func("tvm_callback_metal_compile") diff --git a/apps/ios_rpc/tests/ios_rpc_test.py b/apps/ios_rpc/tests/ios_rpc_test.py index ae0fcb5fdc80..733ab912ecfc 100644 --- a/apps/ios_rpc/tests/ios_rpc_test.py +++ b/apps/ios_rpc/tests/ios_rpc_test.py @@ -35,7 +35,7 @@ sdk = "iphoneos" target = "llvm -mtriple=%s-apple-darwin" % arch -MODES = {"proxy": rpc.connect, "tracker": rpc.connect_tracker, "pure_server": rpc.connect} +MODES = {"proxy": rpc.connect, "tracker": rpc.connect_tracker, "standalone": rpc.connect} # override metal compiler to compile to iphone @tvm.register_func("tvm_callback_metal_compile") diff --git a/apps/ios_rpc/tvmrpc/RPCArgs.mm b/apps/ios_rpc/tvmrpc/RPCArgs.mm index 7c6dd668fe42..7f5d68d7dde5 100644 --- a/apps/ios_rpc/tvmrpc/RPCArgs.mm +++ b/apps/ios_rpc/tvmrpc/RPCArgs.mm @@ -37,8 +37,8 @@ "--custom_addr Custom IP Address to Report to RPC Tracker. Default=\"\"\n" "--immediate_connect No UI interconnection, connect to tracker immediately. Default=False\n" "--verbose Allow to print status info to std out. Default=False\n" - "--server_mode Server mode. Can be \"pure_server\", \"proxy\" or \"tracker\". " - "Default=pure_server \n" + "--server_mode Server mode. Can be \"standalone\", \"proxy\" or \"tracker\". " + "Default=standalone \n" "\n"; struct RPCArgs_cpp { @@ -142,8 +142,8 @@ void update_rpc_args(int argc, char* argv[]) { args.server_mode = RPCServerMode_Tracker; } else if (server_mode == "proxy") { args.server_mode = RPCServerMode_Proxy; - } else if (server_mode == "pure_server") { - args.server_mode = RPCServerMode_PureServer; + } else if (server_mode == "standalone") { + args.server_mode = RPCServerMode_Standalone; } else { LOG(WARNING) << "Wrong server_mode value."; LOG(INFO) << kUsage; diff --git a/apps/ios_rpc/tvmrpc/RPCServer.h b/apps/ios_rpc/tvmrpc/RPCServer.h index 95d22668ad56..dcb0d348df9e 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.h +++ b/apps/ios_rpc/tvmrpc/RPCServer.h @@ -40,12 +40,12 @@ typedef enum { * \brief Enum with modes of servicing supported by RPCServer */ typedef enum { - /// Tracker mode. Same as Pure Server plus register it into Tracker. + /// Tracker mode. Same as Standalone Server plus register it into Tracker. RPCServerMode_Tracker, /// Proxy mode. Connect to proxy server and wait response. RPCServerMode_Proxy, - /// Pure RPC server mode. Open port with RPC server and wait incoming connection. - RPCServerMode_PureServer + /// Standalone RPC server mode. Open port with RPC server and wait incoming connection. + RPCServerMode_Standalone } RPCServerMode; /*! @@ -68,11 +68,11 @@ typedef enum { @property(retain) id delegate; /// Device key to report during RPC session @property(retain) NSString* key; -/// Host address of Proxy/Tracker server (generally IPv4). Ignored for PureServer mode. +/// Host address of Proxy/Tracker server (generally IPv4). Ignored for Standalone mode. @property(retain) NSString* host; -/// Port of Proxy/Tracker server. Ignored for PureServer mode. +/// Port of Proxy/Tracker server. Ignored for Standalone mode. @property int port; -/// Custom address to report into tracker server (optional). Ignored for PureServer/Proxy modes +/// Custom address to report into tracker server (optional). Ignored for Standalone/Proxy modes @property(retain) NSString* custom_addr; /// Triger to enable printing of server state info @property BOOL verbose; diff --git a/apps/ios_rpc/tvmrpc/RPCServer.mm b/apps/ios_rpc/tvmrpc/RPCServer.mm index 4bd9d16dcabe..b381d1afd1ff 100644 --- a/apps/ios_rpc/tvmrpc/RPCServer.mm +++ b/apps/ios_rpc/tvmrpc/RPCServer.mm @@ -522,11 +522,11 @@ - (void)close { @end -@interface RPCServerPure : RPCServerProxy +@interface RPCServerStandalone : RPCServerProxy @property(readonly) int rpc_port; @end -@implementation RPCServerPure { +@implementation RPCServerStandalone { // Socket to listen incoming connections CFSocketRef socket_; /// Current socket connection handler @@ -573,7 +573,7 @@ - (void)handleConnect:(CFSocketNativeHandle)hdl { static void handleConnect(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) { - RPCServerPure* it = (__bridge RPCServerPure*)(info); + RPCServerStandalone* it = (__bridge RPCServerStandalone*)(info); [it handleConnect:*static_cast(data)]; } @@ -651,7 +651,7 @@ @interface RPCServerTracker : RPCServerBase @implementation RPCServerTracker { RPCServerTrackerState state_; - RPCServerPure* rpc_server_; + RPCServerStandalone* rpc_server_; } - (void)setState:(RPCServerTrackerState)new_state { @@ -776,8 +776,8 @@ - (void)close { } - (void)open { - // Start internal Pure RPC server at first - rpc_server_ = [[RPCServerPure alloc] init]; + // Start internal Standalone RPC server at first + rpc_server_ = [[RPCServerStandalone alloc] init]; rpc_server_.key = self.key; rpc_server_.delegate = self; [rpc_server_ open]; @@ -803,7 +803,7 @@ - (void)onStatusChanged:(RPCServerStatus)status { @implementation RPCServer + (instancetype)serverWithMode:(RPCServerMode)mode { - if (mode == RPCServerMode_PureServer) return [[RPCServerPure alloc] init]; + if (mode == RPCServerMode_Standalone) return [[RPCServerStandalone alloc] init]; if (mode == RPCServerMode_Proxy) return [[RPCServerProxy alloc] init]; if (mode == RPCServerMode_Tracker) return [[RPCServerTracker alloc] init]; return nil; diff --git a/apps/ios_rpc/tvmrpc/ViewController.mm b/apps/ios_rpc/tvmrpc/ViewController.mm index 3b1f06e141eb..3f8c647fa4f2 100644 --- a/apps/ios_rpc/tvmrpc/ViewController.mm +++ b/apps/ios_rpc/tvmrpc/ViewController.mm @@ -149,7 +149,7 @@ - (void)onStatusChanged:(RPCServerStatus)status { dispatch_sync(dispatch_get_main_queue(), ^{ switch (status) { case RPCServerStatus_Connected: - if (self.ModeSelector.selectedSegmentIndex == RPCServerMode_PureServer) { + if (self.ModeSelector.selectedSegmentIndex == RPCServerMode_Standalone) { self.infoText.text = [NSString stringWithFormat:@"IP: %@\nPort: %d", server_.device_addr, server_.actual_port]; }