IOHIDUnitTestUtility.m [plain text]
//
// IOHIDUnitTestUtility.c
// IOHIDFamily
//
// Created by YG on 10/24/16.
//
//
#include "IOHIDUnitTestUtility.h"
#include <dispatch/private.h>
#include <mach/mach_time.h>
#include <AssertMacros.h>
#import <spawn.h>
#include <sys/time.h>
#include <IOKit/hid/IOHIDLibPrivate.h>
int __IOHIDUnitTestAttributeInit(pthread_attr_t * p_pthread_attr, int priority, int policy);
void * __IOHIDUnitTestCreateRunLoopThread(void * context);
int __IOHIDUnitTestAttributeInit(pthread_attr_t * p_pthread_attr, int priority, int policy)
{
int err;
struct sched_param param;
err = pthread_attr_init(p_pthread_attr);
require_noerr(err, exit);
pthread_attr_setschedpolicy(p_pthread_attr, policy);
require_noerr(err, exit);
err = pthread_attr_getschedparam(p_pthread_attr, ¶m);
require_noerr(err, exit);
param.sched_priority = priority;
err = pthread_attr_setschedparam(p_pthread_attr, ¶m);
require_noerr(err, exit);
err = pthread_attr_setdetachstate(p_pthread_attr, PTHREAD_CREATE_JOINABLE);
require_noerr(err, exit);
exit:
if ( err != 0 )
pthread_attr_destroy( p_pthread_attr );
return err;
}
dispatch_queue_t IOHIDUnitTestCreateRootQueue (int priority, int poolSize) {
int ret;
dispatch_queue_t queue = nil;
pthread_attr_t attribute;
struct sched_param param;
ret = pthread_attr_init(&attribute);
if (ret) {
return queue;
}
ret = pthread_attr_setschedpolicy(&attribute, SCHED_RR);
if (ret) {
goto exit;
}
ret = pthread_attr_getschedparam(&attribute, ¶m);
if (ret) {
goto exit;
}
param.sched_priority = priority;
ret = pthread_attr_setschedparam(&attribute, ¶m);
if (ret) {
goto exit;
}
ret = pthread_attr_setdetachstate(&attribute, PTHREAD_CREATE_JOINABLE);
if (ret) {
goto exit;
}
queue = dispatch_pthread_root_queue_create("IOHIDUnitTestCreateRootQueue - Root", dispatch_pthread_root_queue_flags_pool_size(poolSize), &attribute, NULL);
exit:
pthread_attr_destroy(&attribute);
return queue;
}
uint64_t IOHIDInitTestAbsoluteTimeToNanosecond (uint64_t abs) {
static mach_timebase_info_data_t timebase;
if (!timebase.denom) {
mach_timebase_info(&timebase);
}
return (abs * timebase.numer) / (timebase.denom);
}
dispatch_semaphore_t __sema = NULL;
CFRunLoopRef __runLoop = NULL;
void * __IOHIDUnitTestCreateRunLoopThread(void * context __unused)
{
CFRunLoopRef runloop = CFRunLoopGetCurrent();
CFRunLoopSourceRef dummySource = NULL;
CFRunLoopSourceContext dummyContext = {};
dummySource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &dummyContext);
require(dummySource, exit);
__runLoop = runloop;
CFRunLoopAddSource(runloop, dummySource, kCFRunLoopDefaultMode);
dispatch_semaphore_signal(__sema);
CFRunLoopRun();
CFRunLoopRemoveSource(runloop, dummySource, kCFRunLoopDefaultMode);
exit:
if ( dummySource )
CFRelease(dummySource);
return NULL;
}
pthread_t hidThread;
CFRunLoopRef IOHIDUnitTestCreateRunLoop (int priority) {
pthread_attr_t attribute;
if (__IOHIDUnitTestAttributeInit (&attribute, priority, SCHED_RR)) {
return NULL;
}
__sema = dispatch_semaphore_create(0);
if (!pthread_create(&hidThread, &attribute,__IOHIDUnitTestCreateRunLoopThread, NULL)) {
dispatch_semaphore_wait(__sema, DISPATCH_TIME_FOREVER);
}
__sema = nil;
if (__runLoop) {
CFRetain(__runLoop);
}
return __runLoop;
}
void IOHIDUnitTestDestroyRunLoop (CFRunLoopRef runloop) {
CFRunLoopStop(runloop);
CFRelease(runloop);
}
char * const hidutil[] = { "/usr/local/bin/hidutil", "dump", NULL };
const char * spindump[] = { "/usr/sbin/spindump", "-notarget", "5", "10", "-o", NULL, NULL};
char *const logcollect[] = { "/usr/bin/log", "show", "--debug", "--predicate", "senderImagePath contains \"IOHIDFamily\" or subsystem == \"com.apple.iohid\"", NULL };
char *const ioreg[] = { "/usr/sbin/ioreg", "-lw0", NULL };
const char * tailspin[] = { "/usr/bin/tailspin", "save", "-r", "hidxctest", "-o" , "-n" , NULL, NULL};
void IOHIDUnitTestRunPosixCommand(char *const argv[], NSString *stdoutFile)
{
int status;
pid_t pid;
posix_spawn_file_actions_t child_fd_actions = 0;
if (stdoutFile) {
// for reading stdout
posix_spawn_file_actions_init(&child_fd_actions);
status = posix_spawn_file_actions_addopen(&child_fd_actions, STDOUT_FILENO, [stdoutFile cStringUsingEncoding:NSUTF8StringEncoding], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (status) {
NSLog(@"posix_spawn_file_actions_addopen (,, }
}
NSLog(@"Running status = posix_spawnp(&pid, argv[0], &child_fd_actions, NULL, argv, NULL);
if (status) {
NSLog(@"posix_spawnp: } else {
waitpid(pid, &status, 0);
NSLog(@" }
}
CFStringRef IOHIDUnitTestCreateStringFromTimeval(CFAllocatorRef allocator, struct timeval *tv)
{
struct tm tmd;
struct tm *local_time;
char time_str[32] = { 0, };
local_time = localtime_r(&tv->tv_sec, &tmd);
if (local_time == NULL) {
local_time = gmtime_r(&tv->tv_sec, &tmd);
}
if (local_time) {
strftime(time_str, sizeof(time_str), " }
CFStringRef time = CFStringCreateWithFormat(allocator, NULL, CFSTR(" return time;
}
void IOHIDUnitTestCollectLogs (uint32_t logTypes, char * fileName, int line)
{
struct timeval t;
gettimeofday(&t, NULL);
NSString * path = [NSString stringWithFormat:@"
NSString * base = [NSString stringWithFormat:@" path,
line,
CFBridgingRelease(IOHIDUnitTestCreateStringFromTimeval(kCFAllocatorDefault, &t))];
NSFileManager * manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath: path] == NO) {
[manager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}
if (logTypes & COLLECT_SPINDUMP) {
NSString * outFile = [NSString stringWithFormat:@" spindump[5] = [outFile cStringUsingEncoding:NSUTF8StringEncoding];
IOHIDUnitTestRunPosixCommand((char * const *)spindump, NULL);
}
if (logTypes & COLLECT_TAILSPIN) {
NSString * outFile = [NSString stringWithFormat:@" tailspin[6] = [outFile cStringUsingEncoding:NSUTF8StringEncoding];
IOHIDUnitTestRunPosixCommand((char * const *)tailspin, NULL);
}
if (logTypes & COLLECT_HIDUTIL) {
IOHIDUnitTestRunPosixCommand(hidutil, [NSString stringWithFormat:@" }
if (logTypes & COLLECT_IOREG) {
IOHIDUnitTestRunPosixCommand(ioreg, [NSString stringWithFormat:@" }
if (logTypes & COLLECT_LOGARCHIVE) {
IOHIDUnitTestRunPosixCommand(logcollect, [NSString stringWithFormat:@" }
}