+ if (SkipName(options, queue->name, namesLen, names)) {
+ continue;
+ }
+
+ if (!(kIOTracking & gIOKitDebug) && (kIOTrackingQueueTypeAlloc & queue->type)) {
+ continue;
+ }
+
+ switch (selector) {
+ case kIOTrackingResetTracking:
+ {
+ IOTrackingReset(queue);
+ ret = kIOReturnSuccess;
+ break;
+ }
+
+ case kIOTrackingStartCapture:
+ case kIOTrackingStopCapture:
+ {
+ queue->captureOn = (kIOTrackingStartCapture == selector);
+ ret = kIOReturnSuccess;
+ break;
+ }
+
+ case kIOTrackingSetMinCaptureSize:
+ {
+ queue->minCaptureSize = size;
+ ret = kIOReturnSuccess;
+ break;
+ }
+
+ case kIOTrackingLeaks:
+ {
+ if (!(kIOTrackingQueueTypeAlloc & queue->type)) {
+ break;
+ }
+
+ if (!data) {
+ data = OSData::withCapacity(1024 * sizeof(uintptr_t));
+ }
+
+ IOTRecursiveLockLock(&queue->lock);
+ for (idx = 0; idx < queue->numSiteQs; idx++) {
+ queue_iterate(&queue->sites[idx], site, IOTrackingCallSite *, link)
+ {
+ addresses = false;
+ queue_iterate(&site->instances, instance, IOTracking *, link)
+ {
+ if (instance == site->addresses) {
+ addresses = true;
+ }
+ instFlags = (typeof(instFlags))instance;
+ if (addresses) {
+ instFlags |= kInstanceFlagAddress;
+ }
+ data->appendBytes(&instFlags, sizeof(instFlags));
+ }
+ }
+ }
+ // queue is locked
+ ret = kIOReturnSuccess;
+ break;
+ }
+
+
+ case kIOTrackingGetTracking:
+ {
+ if (kIOTrackingQueueTypeMap & queue->type) {
+ break;
+ }
+
+ if (!data) {
+ data = OSData::withCapacity(128 * sizeof(IOTrackingCallSiteInfo));
+ }
+
+ IOTRecursiveLockLock(&queue->lock);
+ num = queue->siteCount;
+ idx = 0;
+ for (qIdx = 0; qIdx < queue->numSiteQs; qIdx++) {
+ queue_iterate(&queue->sites[qIdx], site, IOTrackingCallSite *, link)
+ {
+ assert(idx < num);
+ idx++;
+
+ size_t tsize[2];
+ uint32_t count = site->count;
+ tsize[0] = site->size[0];
+ tsize[1] = site->size[1];
+
+ if (intag || inzsize) {
+ uintptr_t addr;
+ vm_size_t size, zoneSize;
+ vm_tag_t tag;
+
+ if (kIOTrackingQueueTypeAlloc & queue->type) {
+ addresses = false;
+ count = 0;
+ tsize[0] = tsize[1] = 0;
+ queue_iterate(&site->instances, instance, IOTracking *, link)
+ {
+ if (instance == site->addresses) {
+ addresses = true;
+ }
+
+ if (addresses) {
+ addr = ~((IOTrackingAddress *)instance)->address;
+ } else {
+ addr = (uintptr_t) (instance + 1);
+ }
+
+ kr = vm_kern_allocation_info(addr, &size, &tag, &zoneSize);
+ if (KERN_SUCCESS != kr) {
+ continue;
+ }
+
+ if ((VM_KERN_MEMORY_NONE != intag) && (intag != tag)) {
+ continue;
+ }
+ if (inzsize && (inzsize != zoneSize)) {
+ continue;
+ }
+
+ count++;
+ tsize[0] += size;
+ }
+ } else {
+ if (!intag || inzsize || (intag != site->tag)) {
+ continue;
+ }
+ }
+ }
+
+ if (!count) {
+ continue;
+ }
+ if (size && ((tsize[0] + tsize[1]) < size)) {
+ continue;
+ }
+
+ siteInfo.count = count;
+ siteInfo.size[0] = tsize[0];
+ siteInfo.size[1] = tsize[1];
+
+ CopyOutKernelBacktrace(site, &siteInfo);
+ data->appendBytes(&siteInfo, sizeof(siteInfo));
+ }
+ }
+ assert(idx == num);
+ IOTRecursiveLockUnlock(&queue->lock);
+ ret = kIOReturnSuccess;
+ break;
+ }
+
+ case kIOTrackingGetMappings:
+ {
+ if (!(kIOTrackingQueueTypeMap & queue->type)) {
+ break;
+ }
+ if (!data) {
+ data = OSData::withCapacity(page_size);
+ }
+
+ IOTRecursiveLockLock(&queue->lock);
+ num = queue->siteCount;
+ idx = 0;
+ for (qIdx = 0; qIdx < queue->numSiteQs; qIdx++) {
+ queue_iterate(&queue->sites[qIdx], user, IOTrackingUser *, link)
+ {
+ assert(idx < num);
+ idx++;
+
+ kr = IOMemoryMapTracking(user, &mapTask, &mapAddress, &mapSize);
+ if (kIOReturnSuccess != kr) {
+ continue;
+ }
+ if (proc && (mapTask != proc_task(proc))) {
+ continue;
+ }
+ if (size && (mapSize < size)) {
+ continue;
+ }
+
+ siteInfo.count = 1;
+ siteInfo.size[0] = mapSize;
+ siteInfo.address = mapAddress;
+ siteInfo.addressPID = task_pid(mapTask);
+ siteInfo.btPID = user->btPID;
+
+ for (uint32_t j = 0; j < kIOTrackingCallSiteBTs; j++) {
+ siteInfo.bt[0][j] = VM_KERNEL_UNSLIDE(user->bt[j]);
+ }
+ uint32_t * bt32 = (typeof(bt32)) & user->btUser[0];
+ uint64_t * bt64 = (typeof(bt64))((void *) &user->btUser[0]);
+ for (uint32_t j = 0; j < kIOTrackingCallSiteBTs; j++) {
+ if (j >= user->userCount) {
+ siteInfo.bt[1][j] = 0;
+ } else if (user->user32) {
+ siteInfo.bt[1][j] = bt32[j];
+ } else {
+ siteInfo.bt[1][j] = bt64[j];
+ }
+ }
+ data->appendBytes(&siteInfo, sizeof(siteInfo));
+ }
+ }
+ assert(idx == num);
+ IOTRecursiveLockUnlock(&queue->lock);
+ ret = kIOReturnSuccess;
+ break;
+ }
+
+ default:
+ ret = kIOReturnUnsupported;
+ break;
+ }
+ }
+
+ if ((kIOTrackingLeaks == selector) && data) {
+ data = IOTrackingLeaks(data);
+ queue_iterate(&gIOTrackingQ, queue, IOTrackingQueue *, link)
+ {
+ if (SkipName(options, queue->name, namesLen, names)) {
+ continue;
+ }
+ if (!(kIOTrackingQueueTypeAlloc & queue->type)) {
+ continue;
+ }
+ IOTRecursiveLockUnlock(&queue->lock);
+ }
+ }
+
+ lck_mtx_unlock(gIOTrackingLock);
+
+ if ((kIOTrackingLeaks == selector) && namesLen && names) {
+ const char * scan;
+ const char * next;
+ size_t sLen;
+
+ if (!data) {
+ data = OSData::withCapacity(4096 * sizeof(uintptr_t));
+ }
+
+ // <len><name>...<len><name><0>
+ scan = names;
+ do{
+ sLen = scan[0];
+ scan++;
+ next = scan + sLen;
+ if (next >= (names + namesLen)) {
+ break;
+ }
+ kr = zone_leaks(scan, sLen, &ZoneSiteProc, data);
+ if (KERN_SUCCESS == kr) {
+ ret = kIOReturnSuccess;
+ } else if (KERN_INVALID_NAME != kr) {
+ ret = kIOReturnVMError;
+ }
+ scan = next;
+ }while (scan < (names + namesLen));
+ }
+
+ if (data) {
+ switch (selector) {
+ case kIOTrackingLeaks:
+ case kIOTrackingGetTracking:
+ case kIOTrackingGetMappings:
+ {
+ IOTrackingCallSiteInfo * siteInfos;
+ siteInfos = (typeof(siteInfos))data->getBytesNoCopy();
+ num = (data->getLength() / sizeof(*siteInfos));
+ qsort(siteInfos, num, sizeof(*siteInfos), &IOTrackingCallSiteInfoCompare);
+ break;
+ }
+ default: assert(false); break;
+ }