Objective-C Reflection

For glDebug I generate a lot of code so save having to write and update it, both for the call intercept library, and the application. More recently in the application, I’ve added an event list similar to the one in PIX where it shows the parameters.
Screen Shot 2014-04-28 at 04.37.46

So to generate this, I generate an overload of my base command class for every method, and changing the description function to return based on the parameter type and function name.

Then to create the class, I just get the class by the function name prefixed with “GLDebug_”.

Class createClass = NSClassFromString([NSString stringWithFormat:@"GLDebug_%@", commandName]);
 
//
if(createClass == nil)
{
	createClass = [CommandData class];
}
 
//				
CommandData * commandData = [[createClass alloc] initWithPacket:packet];

Now in my outline view I can just use the description string. One limitation however is I want to replace GLenum with the correct enum string. However it appears there are some enums sharing the same name. I may need to generate a spreadsheet and fill out the enums manually.

POSIX semaphores

POSIX semaphores are a real pain. Resolving all the issues I was having with transferring packets for my glDebug application and library, I decided to move the sending and receiving onto a separate thread.

While this improve performance, allowing my OpenGL application to run at 60FPS instead of 38FPS, if it crashed because of some changes I was doing, the semaphore would hang around.

I found the way to solve this is to call unlink.

//
dispatch_queue_t gQueue;
pthread_mutex_t gMutex;
sem_t * gSemaphore;
uint32_t gMTU;
 
//
void glDebugComm_init(const uint32_t inMTU)
{
	int err;
 
	//
	gMTU = inMTU;
 
	//
	gSemaphore = sem_open("glDebug", O_CREAT, 0, 0);
	assert(gSemaphore != SEM_FAILED);
 
	err = sem_unlink("glDebug");
	assert(err == 0);
 
	//
	err = pthread_mutex_init(&gMutex, NULL);
	assert(err == 0);
 
	//
	gQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
	dispatch_async(gQueue,
		^(void)
		{
			while(TRUE)
			{
				glDebugComm_dispatch_thread();
			}
		}
	);
}

This prevented the issue. I think it was happening originally because I’m using named semaphores, and both the application and the debugging lib share the same communication library which use the same name, so when triggered on one, it could accept on the other first.

This does pose potential uses for a cross application semaphore though.

Intercepting Objective-C Calls

Continuing on from my previous post about capturing OpenGL calls in C, I now needed some method to intercept the creation and destruction of contexts. While this is easy for the CGL library since it worked the same as intercepting the OpenGL calls, this wasn’t as easy as I thought it would be for NSOpenGLContext.
Screen Shot 2014-04-27 at 17.56.11 1
I eventually found a C methods for retrieving and replacing objective-c functions with C functions, I think they were class_replaceMethod and class_getInstanceMethod. However this was flawed, every so often I would run the application and permission errors from the operating system, crashing the program.

And even when they did work, I was unable to replace the dealloc method without getting a recursive feedback.

But while reading about these methods, I came by another method that involves not replacing a call, but exchanging it for another. So I wrote a class extension for NSOpenGLContext, with my own variations of the methods I wanted to intercept. Luckily, dealloc just worked for the extension and did not required swizzling.

@implementation NSOpenGLContext (glDebug)
 
//
- (id)init_glDebug_WithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share
{
	NSLog(@"initWithFormat:shareContext: intercepted\n");
	return [self init_glDebug_WithFormat:format shareContext:share];
}
 
//
- (id)init_glDebug_WithCGLContextObj:(void *)context
{
	NSLog(@"initWithCGLContextObj intercepted\n");
	return [self init_glDebug_WithCGLContextObj:context];
}
 
- (void)dealloc
{
	NSLog(@"Dealloc intercepted\n");
}
 
@end

While this fixed the recursive problem, I still occasionally received permission errors. And while googling for the answer, I found that objective-c classes have a static +(void)load method that the code was suppose to be put in, not the library constructor.

@implementation NSOpenGLContext (glDebug)
 
//
+ (void)load
{
	[NSOpenGLContext swizzleMethod1:@selector(initWithCGLContextObj:)
						withMethod2:@selector(init_glDebug_WithCGLContextObj:)];
 
	[NSOpenGLContext swizzleMethod1:@selector(initWithFormat:shareContext:)
						withMethod2:@selector(init_glDebug_WithFormat:shareContext:)];
}
 
//
+ (void)swizzleMethod1:(SEL)inOriginal withMethod2:(SEL)inSwizzled
{
	Method originalMethod = class_getInstanceMethod([NSOpenGLContext class], inOriginal);
	Method swizzledMethod = class_getInstanceMethod([NSOpenGLContext class], inSwizzled);
	method_exchangeImplementations(originalMethod, swizzledMethod);
}
 
//
+ (void)swizzleStaticMethod1:(SEL)inOriginal withMethod2:(SEL)inSwizzled
{
	Method originalMethod = class_getClassMethod([NSOpenGLContext class], inOriginal);
	Method swizzledMethod = class_getClassMethod([NSOpenGLContext class], inSwizzled);
	method_exchangeImplementations(originalMethod, swizzledMethod);
}
 
@end

This worked great and I haven’t had any troubles since.

Intercepting OpenGL Calls

To capture the OpenGL commands is fairly easy, all you need to do is link a library that implements the OpenGL function before it links the actual library.
Screen Shot 2014-04-28 at 09.46.51

Originally I had planned to use the environment variable DYLD_INSERT_LIBRARIES to achieve this, however I could not get it to work with my Cocoa application. So I decided to just compile it with it and let the user decide whether or not they want to run the executable and insert the library at runtime, or add it to their project to link first.

The next challenge is that now you have intercepted the call, you still need to forward it onto where it was going originally. To do this, I used the link library constructor to retrieve and store this pointer for later use to reduce overhead.

#include <stdio.h>
#include <dlfcn.h>
 
//
typedef void (*_fn_glViewport) (GLint x, GLint y, GLsizei width, GLsizei height);
_fn_glViewport g_glViewport = NULL;
 
//
__attribute__((constructor)) void DllMain()
{
	g_glViewport = (_fn_glViewport)dlsym(RTLD_NEXT, "glViewport");
}
 
//
void glViewport (GLint x, GLint y, GLsizei width, GLsizei height)
{
	printf("Intercepted glViewport call\n");
	g_glViewport(x, y, width, height);
}

The RTLD_NEXT parameter tells dlsym to retrieve the method from the next linked dependencies. Ideally you probably want to check the pointer to make sure it isn’t NULL before calling it, however since glViewport comes from OpenGL 1.0 and still exists in the latest version, I don’t bother.

glDebug and the callstack

I’ve recently been working on an OpenGL debugger for OSX similar to PIX so I can better get to the root problem bugs.

I will basically intercept the OpenGL calls and transmit them to a debugger that will check for errors, and allow me to view all active resources within a frame.

One bit of code that will be helpful is knowing where each call originated from. Luckily I found some methods that retrieve the symbol names for each level in the callstack. While this doesn’t give me a file or line number, I can get function and class name.

void glDebugLib_ReportCallstack(const uint64_t inContext)
{
	struct glDebugPacket * packet = glDebug_packet_create(glDebugPacketType_Command, "glCallstackReportCIP", inContext);
	{
		void * callstack[128];
		int frameCount;
 
		//
		frameCount = backtrace(callstack, 128);
 
		//
		if(frameCount > 0)
		{
			char ** frameStrings = backtrace_symbols(callstack, frameCount);
 
			if(frameStrings != NULL)
			{
				glDebug_packet_add_uint32(packet, frameCount - 1);
 
				// The first index is the current method
				for(int i = 1; i < frameCount; ++i)
				{
					glDebug_packet_add_string(packet, frameStrings[i]);
				}
 
				free(frameStrings);
			}
		}
	}
	glDebugComm_send_packet(packet);
}

At some point in the future I’ll look into retrieving the file and line number associated with the symbols.