Created
June 19, 2012 07:14
-
-
Save fjolnir/2952745 to your computer and use it in GitHub Desktop.
A remote debugging/inspection tool for Objective-C
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <netdb.h> | |
#include <dispatch/dispatch.h> | |
#include <readline/readline.h> | |
#include <readline/history.h> | |
bool running = true; | |
int sockfd, portNum; | |
struct sockaddr_in serverAddr; | |
struct hostent *server; | |
char buffer[1024]; | |
dispatch_source_t readSource; | |
dispatch_queue_t queue; | |
bool multiline = false; | |
static int returnKey(int count, int key) { | |
if(!multiline) | |
rl_done = 1; | |
printf("\n"); | |
} | |
static int toggleMultiline(int count, int key) { | |
if(multiline == false) { | |
multiline = true; | |
printf("\r~ "); | |
} else { | |
rl_done = 1; | |
printf("\n"); | |
multiline = false; | |
} | |
} | |
int main(int argc, char *argv[]) | |
{ | |
bzero((char *)&serverAddr, sizeof(serverAddr)); | |
if(argc < 3) { | |
fprintf(stderr, "Usage: client <server> <port>\n"); | |
return 1; | |
} | |
// Connect to the server | |
portNum = atoi(argv[2]); | |
sockfd = socket(AF_INET, SOCK_STREAM, 0); | |
if(sockfd < 0) { | |
fprintf(stderr, "ERROR opening socket\n"); | |
return 1; | |
} | |
server = gethostbyname(argv[1]); | |
if(server == NULL) { | |
fprintf(stderr,"ERROR, no such host\n"); | |
return 1; | |
} | |
serverAddr.sin_family = AF_INET; | |
bcopy((char *)server->h_addr, (char *)&serverAddr.sin_addr.s_addr, server->h_length); | |
serverAddr.sin_port = htons(portNum); | |
if(connect(sockfd,(struct sockaddr *) &serverAddr,sizeof(serverAddr)) < 0) { | |
fprintf(stderr, "ERROR connecting\n"); | |
return 1; | |
} | |
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); | |
readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0, queue); | |
dispatch_source_set_event_handler(readSource, ^{ | |
char buf[1024]; | |
bzero(buf,sizeof(buf)); | |
int n = read(sockfd,buf, sizeof(buf)-1); | |
if(n < 0) | |
fprintf(stderr, "ERROR reading from socket\n"); | |
// Print the output without messing up the prompt | |
int savedPoint = rl_point; | |
char *savedLine = rl_copy_text(0, rl_end); | |
rl_set_prompt(""); | |
rl_replace_line("", 0); | |
rl_redisplay(); | |
fprintf(stderr, "\r%s", buf); | |
rl_set_prompt("> "); | |
rl_replace_line(savedLine, 0); | |
rl_point = savedPoint; | |
rl_redisplay(); | |
free(savedLine); | |
}); | |
dispatch_resume(readSource); | |
rl_bind_key('\n', returnKey); | |
rl_bind_key('\r', returnKey); | |
rl_bind_key('\t', toggleMultiline); | |
char *line; | |
while((line = readline("> ")) != NULL) { | |
if(strlen(line) > 0) { | |
if(strcmp(line, "q") == 0 || strcmp(line, "quit") == 0) | |
break; | |
ssize_t n = write(sockfd,line,strlen(line)); | |
if(n < 0) | |
fprintf(stderr, "ERROR writing to socket"); | |
add_history(line); | |
} | |
free(line); | |
} | |
close(sockfd); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- A couple of functions to make working with the view hierarchy nicer | |
function keyWindow() | |
return UIApplication:sharedApplication():keyWindow() | |
end | |
function rootView() | |
return keyWindow():rootViewController():view() | |
end | |
function topView(controller) | |
local controller = controller or keyWindow():rootViewController() | |
if controller == nil then | |
return keyWindow() | |
end | |
local presented = controller:presentedViewController() | |
if presented ~= nil then | |
return topView(presented) | |
elseif controller:childViewControllers():count() > 0 then | |
return topView(controller:childViewController():objectAtIndex(0)) | |
end | |
return controller:view() | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
objc = require("objc") | |
objc.fallbackOnMsgSend = false -- Prevent exceptions on typos | |
objc.debug = true | |
-- Make it nicer to use objc | |
setmetatable(_G, {__index=objc}) | |
ffi = require("ffi") | |
require("debughelpers") | |
local portNum = 4391 | |
ffi.cdef[[ | |
typedef unsigned long size_t; | |
typedef long ssize_t; | |
typedef unsigned char __uint8_t; | |
typedef unsigned short __uint16_t; | |
typedef unsigned int __uint32_t; | |
typedef __uint8_t sa_family_t; | |
typedef size_t socklen_t; | |
struct sockaddr { | |
__uint8_t sa_len; | |
sa_family_t sa_family; | |
char sa_data[14]; | |
}; | |
typedef __uint32_t in_addr_t; | |
typedef __uint16_t in_port_t; | |
struct in_addr { | |
in_addr_t s_addr; | |
}; | |
struct sockaddr_in { | |
__uint8_t sin_len; | |
sa_family_t sin_family; | |
in_port_t sin_port; | |
struct in_addr sin_addr; | |
char sin_zero[8]; | |
}; | |
int socket(int, int, int); | |
int bind(int, const struct sockaddr *, socklen_t); | |
int listen(int, int); | |
int accept(int, struct sockaddr * __restrict, socklen_t * __restrict); | |
ssize_t read(int fildes, void *buf, size_t nbyte); | |
ssize_t write(int fildes, const void *buf, size_t nbyte); | |
uint16_t htons(uint16_t hostshort); | |
// GCD | |
struct dispatch_source_type_s; | |
struct dispatch_source_s; | |
struct dispatch_queue_s; | |
typedef const struct dispatch_source_type_s *dispatch_source_type_t; | |
typedef struct dispatch_source_s *dispatch_source_t; | |
typedef dispatch_source_t dispatch_object_t; // Not the actual definition, ..but it's long | |
typedef struct dispatch_queue_s *dispatch_queue_t; | |
typedef int dispatch_fd_t; | |
typedef void (*dispatch_function_t)(void *); | |
const struct dispatch_source_type_s _dispatch_source_type_read; | |
dispatch_source_t dispatch_source_create(dispatch_source_type_t type, | |
uintptr_t handle, unsigned long mask, | |
dispatch_queue_t queue); | |
void dispatch_source_set_event_handler_f( | |
dispatch_source_t source, | |
dispatch_function_t handler); | |
void dispatch_resume(dispatch_object_t object); | |
void dispatch_suspend(dispatch_object_t object); | |
void dispatch_source_cancel(dispatch_source_t source); | |
dispatch_queue_t dispatch_get_global_queue(long priority, unsigned long flags); | |
typedef unsigned long dispatch_io_type_t; | |
typedef struct dispatch_io_s *dispatch_io_t; | |
typedef long off_t; | |
typedef struct dispatch_data_s *dispatch_data_t; | |
dispatch_io_t dispatch_io_create( | |
dispatch_io_type_t type, | |
dispatch_fd_t fd, | |
dispatch_queue_t queue, | |
id cleanup_handler); | |
void dispatch_io_read( | |
dispatch_io_t channel, | |
off_t offset, | |
size_t length, | |
dispatch_queue_t queue, | |
id io_handler); | |
void dispatch_write( | |
dispatch_fd_t fd, | |
dispatch_data_t data, | |
dispatch_queue_t queue, | |
id handler); | |
void dispatch_async_f( | |
dispatch_queue_t queue, | |
void *context, | |
dispatch_function_t work); | |
struct dispatch_queue_s _dispatch_main_q; | |
int dup(int fildes); | |
int dup2(int fildes, int filedes2); | |
int usleep(unsigned int useconds); | |
int pipe(int fildes[2]); | |
int close(int filedes); | |
typedef void NSUncaughtExceptionHandler(id exception); | |
void NSSetUncaughtExceptionHandler(NSUncaughtExceptionHandler *); | |
NSUncaughtExceptionHandler * NSGetUncaughtExceptionHandler (void); | |
id objc_begin_catch(void *exc_buf); | |
void objc_end_catch(void); | |
]] | |
local C = ffi.C | |
local function dispatch_get_main_queue() | |
return ffi.cast("dispatch_queue_t", C._dispatch_main_q) | |
end | |
function sleep(ms) | |
C.usleep(ms*1000) | |
end | |
local DISPATCH_IO_STREAM = 0 | |
local DISPATCH_SOURCE_TYPE_READ = C._dispatch_source_type_read; | |
local queue = dispatch_get_main_queue() -- C.dispatch_get_global_queue(0,0) -- Get a normal priority queue | |
local connectedSocket | |
-- Reroute stdout to a socket | |
io.stdout:write(" ") -- open stdout | |
local origStdOut = C.dup(1) | |
local pipe = ffi.new("int[2]") | |
C.pipe(pipe) | |
C.dup2(pipe[1], 1) | |
C.close(pipe[1]) | |
local stdoutsrc = C.dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, pipe[0], 0, queue); | |
C.dispatch_source_set_event_handler_f(stdoutsrc, function(ctx) | |
local buffer = ffi.new("char[512]") | |
ffi.fill(buffer, ffi.sizeof(buffer)) | |
local n = C.read(pipe[0], buffer, 511) | |
io.stderr:write(ffi.string(buffer)) | |
local output = ffi.string(buffer) | |
C.write(connectedSocket, output, #output) | |
end) | |
C.dispatch_resume(stdoutsrc) | |
-- Set up the server | |
local AF_INET = 2 | |
local SOCK_STREAM = 1 | |
local INADDR_ANY = 0 | |
local cli_addr = ffi.new("struct sockaddr_in") | |
local clilen = ffi.new("socklen_t[1]") | |
local sockfd = C.socket(AF_INET, SOCK_STREAM, 0) | |
if sockfd < 0 then | |
error("Couldn't open socket") | |
end | |
local serv_addr = ffi.new("struct sockaddr_in") | |
serv_addr.sin_family = AF_INET; | |
serv_addr.sin_addr.s_addr = INADDR_ANY | |
serv_addr.sin_port = C.htons(portNum); | |
if C.bind(sockfd, ffi.cast("struct sockaddr*", serv_addr), ffi.sizeof(serv_addr)) < 0 then | |
error("Error binding socket") | |
end | |
local connectionSource | |
local function connectionHandler(ctx) | |
local buffer = ffi.new("char[512]") | |
ffi.fill(buffer, ffi.sizeof(buffer)) | |
local n = C.read(connectedSocket, buffer, 511) | |
if n <= 0 then | |
io.stderr:write("couldnt read from socket\n") | |
C.dispatch_source_cancel(connectionSource) | |
else | |
local response = ffi.string(buffer) | |
if #response > 0 then | |
local toExec, syntaxErr = loadstring(response) | |
if toExec ~= nil then | |
success,ret = pcall(toExec) | |
if success == false then | |
print("error: ", ret) | |
end | |
else | |
print(syntaxErr) | |
end | |
end | |
end | |
end | |
-- Listen for connections | |
C.listen(sockfd, 0) | |
local listensrc = C.dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0, queue); | |
C.dispatch_source_set_event_handler_f(listensrc, function(ctx) | |
connectedSocket = C.accept(sockfd, ffi.cast("struct sockaddr *", cli_addr), clilen) | |
if connectedSocket < 0 then | |
error("Error on accept") | |
end | |
if connectionSource ~= nil then | |
C.dispatch_source_cancel(connectionSource) | |
connectionSource = nil | |
end | |
connectionSource = C.dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, connectedSocket, 0, queue); | |
C.dispatch_source_set_event_handler_f(connectionSource, connectionHandler) | |
C.dispatch_resume(connectionSource) | |
end) | |
C.dispatch_resume(listensrc) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment