ANE/api_exploration.m

168 lines
8.0 KiB
Objective-C

#import <Foundation/Foundation.h>
#import <CoreML/CoreML.h>
#import <objc/runtime.h>
#import <objc/message.h>
#import <dlfcn.h>
#import <mach/mach_time.h>
#import <IOSurface/IOSurface.h>
static mach_timebase_info_data_t g_tb;
static double ticksToMs(uint64_t t) { return (double)t * g_tb.numer / g_tb.denom / 1e6; }
int main() {
@autoreleasepool {
mach_timebase_info(&g_tb);
dlopen("/System/Library/PrivateFrameworks/AppleNeuralEngine.framework/AppleNeuralEngine", RTLD_NOW);
// === Approach 1: MLModelAsset from compiled .mlmodelc data ===
// First compile a known-working model to .mlmodelc
printf("=== Approach 1: MLModelAsset in-memory ===\n");
NSError *e = nil;
NSURL *src = [NSURL fileURLWithPath:@"/tmp/ane_sram_1024ch_64sp.mlpackage"];
NSURL *compiled = [MLModel compileModelAtURL:src error:&e];
if (e) { printf("Compile failed: %s\n", [[e description] UTF8String]); return 1; }
printf("Compiled to: %s\n", [[compiled path] UTF8String]);
// Read the model.mlmodel spec from the compiled bundle
// The spec is typically in coremldata.bin or model.mlmodel
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *files = [fm contentsOfDirectoryAtPath:[compiled path] error:nil];
printf("Files in .mlmodelc:\n");
for (NSString *f in files) printf(" %s\n", [f UTF8String]);
// Try loading with MLModelAsset
// MLModelAsset has modelAssetWithURL: on macOS 15
if (@available(macOS 14.0, *)) {
// Read the spec data
NSString *specPath = [[compiled path] stringByAppendingPathComponent:@"coremldata.bin"];
if (![fm fileExistsAtPath:specPath]) {
specPath = [[compiled path] stringByAppendingPathComponent:@"model.mlmodel"];
}
NSData *specData = [NSData dataWithContentsOfFile:specPath];
printf("Spec data: %lu bytes from %s\n", (unsigned long)[specData length],
[[specPath lastPathComponent] UTF8String]);
// Try MLModelAsset
Class assetClass = NSClassFromString(@"MLModelAsset");
if (assetClass) {
printf("MLModelAsset class found\n");
// List methods
unsigned int count;
Method *methods = class_copyMethodList(object_getClass(assetClass), &count);
for (unsigned int i = 0; i < count; i++)
printf(" + %s\n", sel_getName(method_getName(methods[i])));
free(methods);
}
}
// === Approach 2: Read a .mlmodelc, extract MIL, feed to _ANEInMemoryModelDescriptor ===
printf("\n=== Approach 2: Inspect MIL in compiled model ===\n");
// Look for model.mil or any MIL file
NSDirectoryEnumerator *en = [fm enumeratorAtPath:[compiled path]];
NSString *f;
while ((f = [en nextObject])) {
NSString *full = [[compiled path] stringByAppendingPathComponent:f];
BOOL isDir;
[fm fileExistsAtPath:full isDirectory:&isDir];
if (!isDir) {
NSDictionary *attrs = [fm attributesOfItemAtPath:full error:nil];
printf(" %s (%llu bytes)\n", [f UTF8String],
[[attrs objectForKey:NSFileSize] unsignedLongLongValue]);
}
}
// Try to find and read model.mil
NSString *milPath = [[compiled path] stringByAppendingPathComponent:@"model.mil"];
if ([fm fileExistsAtPath:milPath]) {
NSString *milText = [NSString stringWithContentsOfFile:milPath encoding:NSUTF8StringEncoding error:nil];
printf("\n=== model.mil contents (first 2000 chars) ===\n");
printf("%s\n", [[milText substringToIndex:MIN(2000, [milText length])] UTF8String]);
}
// Also check for mlmodelc structure
NSString *aneDir = nil;
en = [fm enumeratorAtPath:[compiled path]];
while ((f = [en nextObject])) {
if ([f hasSuffix:@".espresso.net"] || [f hasSuffix:@".hwx"] || [f hasSuffix:@".mil"]) {
printf(" FOUND: %s\n", [f UTF8String]);
}
}
// === Approach 3: Try _ANEInMemoryModelDescriptor with actual MIL from compiled model ===
printf("\n=== Approach 3: _ANEInMemoryModelDescriptor ===\n");
Class ANEInMemDesc = NSClassFromString(@"_ANEInMemoryModelDescriptor");
if (ANEInMemDesc) {
printf("Class exists. Methods:\n");
unsigned int count;
Method *methods = class_copyMethodList(object_getClass(ANEInMemDesc), &count);
for (unsigned int i = 0; i < count; i++) {
SEL s = method_getName(methods[i]);
printf(" + %s (args: %d)\n", sel_getName(s), method_getNumberOfArguments(methods[i]));
}
free(methods);
methods = class_copyMethodList(ANEInMemDesc, &count);
printf("Instance methods:\n");
for (unsigned int i = 0; i < count; i++) {
SEL s = method_getName(methods[i]);
const char *enc = method_getTypeEncoding(methods[i]);
printf(" - %s [%s]\n", sel_getName(s), enc ? enc : "?");
}
free(methods);
// If model.mil exists, try feeding it
if ([fm fileExistsAtPath:milPath]) {
NSString *milText = [NSString stringWithContentsOfFile:milPath encoding:NSUTF8StringEncoding error:nil];
printf("\nTrying modelWithMILText: with actual model.mil...\n");
id desc = ((id(*)(Class,SEL,id,id,id))objc_msgSend)(
ANEInMemDesc, @selector(modelWithMILText:weights:optionsPlist:),
milText, nil, nil);
printf("Result: %s\n", desc ? [[desc description] UTF8String] : "nil");
// Try with NSData
NSData *milData = [milText dataUsingEncoding:NSUTF8StringEncoding];
desc = ((id(*)(Class,SEL,id,id,id))objc_msgSend)(
ANEInMemDesc, @selector(modelWithMILText:weights:optionsPlist:),
milData, nil, nil);
printf("Result (NSData): %s\n", desc ? [[desc description] UTF8String] : "nil");
}
} else {
printf("_ANEInMemoryModelDescriptor NOT FOUND\n");
}
// === Approach 4: Hook into what CoreML actually sends to ANE ===
printf("\n=== Approach 4: Trace CoreML -> ANE path ===\n");
// Load the model the normal working way and inspect the _ANEModel
MLModelConfiguration *config = [[MLModelConfiguration alloc] init];
config.computeUnits = MLComputeUnitsAll;
MLModel *model = [MLModel modelWithContentsOfURL:compiled configuration:config error:&e];
if (e) { printf("MLModel load failed: %s\n", [[e description] UTF8String]); return 1; }
// Try to get internal model object
printf("MLModel: %s\n", [[model description] UTF8String]);
// Check if we can access the ANE model through the MLModel
// Try KVC for internal properties
@try {
id engine = [model valueForKey:@"engine"];
printf("engine: %s\n", engine ? [[engine description] UTF8String] : "nil");
} @catch(NSException *ex) {
printf("No 'engine' key\n");
}
@try {
id proxy = [model valueForKey:@"proxy"];
printf("proxy: %s\n", proxy ? [NSStringFromClass([proxy class]) UTF8String] : "nil");
} @catch(NSException *ex) {
printf("No 'proxy' key\n");
}
// Check MLNeuralNetworkEngine or MLANEEngine
Class aneEngine = NSClassFromString(@"MLANEEngine");
Class nnEngine = NSClassFromString(@"MLNeuralNetworkEngine");
Class milEngine = NSClassFromString(@"MLMILComputeEngine");
printf("MLANEEngine: %s\n", aneEngine ? "exists" : "not found");
printf("MLNeuralNetworkEngine: %s\n", nnEngine ? "exists" : "not found");
printf("MLMILComputeEngine: %s\n", milEngine ? "exists" : "not found");
}
return 0;
}