- IOTrackingQueue * queue;
- IOTracking * instance;
- IOTrackingCallSite * site;
- IOTrackingCallSiteInfo * siteInfos;
- IOTrackingCallSiteInfo * siteInfo;
- bool addresses;
- uint32_t num, idx;
- uintptr_t instFlags;
-
- if (!(kIOTracking & gIOKitDebug)) return (kIOReturnNotReady);
- ret = kIOReturnNotFound;
-
- lck_mtx_lock(gIOTrackingLock);
- queue_iterate(&gIOTrackingQ, queue, IOTrackingQueue *, link)
- {
- if (SkipName(options, queue->name, namesLen, names)) 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 (!queue->isAlloc) break;
-
- if (!data) data = OSData::withCapacity(1024 * sizeof(uintptr_t));
-
- IOTRecursiveLockLock(&queue->lock);
- queue_iterate(&queue->sites, 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:
- case kIOTrackingPrintTracking:
- {
- if (!data) data = OSData::withCapacity(128 * sizeof(IOTrackingCallSiteInfo));
-
- IOTRecursiveLockLock(&queue->lock);
- num = queue->siteCount;
- idx = 0;
- queue_iterate(&queue->sites, site, IOTrackingCallSite *, link)
- {
- assert(idx < num);
- idx++;
-
- if (size && ((site->info.size[0] + site->info.size[1]) < size)) continue;
-
- IOTrackingCallSiteInfo unslideInfo;
- unslideInfo.count = site->info.count;
- memcpy(&unslideInfo.size[0], &site->info.size[0], sizeof(unslideInfo.size));
-
- for (uint32_t j = 0; j < kIOTrackingCallSiteBTs; j++)
- {
- unslideInfo.bt[j] = VM_KERNEL_UNSLIDE(site->info.bt[j]);
- }
- data->appendBytes(&unslideInfo, sizeof(unslideInfo));
- }
- 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 (!queue->isAlloc) continue;
- IOTRecursiveLockUnlock(&queue->lock);
- }
- }
-
- lck_mtx_unlock(gIOTrackingLock);
-
- if (data)
- {
- siteInfos = (typeof(siteInfos)) data->getBytesNoCopy();
- num = (data->getLength() / sizeof(IOTrackingCallSiteInfo));
- qsort(siteInfos, num, sizeof(*siteInfos), &IOTrackingCallSiteInfoCompare);
-
- if (kIOTrackingPrintTracking == selector)
- {
- for (idx = 0; idx < num; idx++)
- {
- siteInfo = &siteInfos[idx];
- printf("\n0x%lx bytes (0x%lx + 0x%lx), %d call%s, [%d]\n",
- siteInfo->size[0] + siteInfo->size[1],
- siteInfo->size[0], siteInfo->size[1],
- siteInfo->count, (siteInfo->count != 1) ? "s" : "", idx);
- uintptr_t * bt = &siteInfo->bt[0];
- printf(" Backtrace 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
- bt[0], bt[1], bt[2], bt[3], bt[4], bt[5], bt[6], bt[7],
- bt[8], bt[9], bt[10], bt[11], bt[12], bt[13], bt[14], bt[15]);
- kmod_dump_log((vm_offset_t *) &bt[0], kIOTrackingCallSiteBTs, FALSE);
- }
- data->release();
- data = 0;
- }
- }
-
- *result = data;
+ kern_return_t kr;
+ IOTrackingQueue * queue;
+ IOTracking * instance;
+ IOTrackingCallSite * site;
+ IOTrackingCallSiteInfo siteInfo;
+ IOTrackingUser * user;
+ task_t mapTask;
+ mach_vm_address_t mapAddress;
+ mach_vm_size_t mapSize;
+ uint32_t num, idx, qIdx;
+ uintptr_t instFlags;
+ proc_t proc;
+ bool addresses;
+
+ ret = kIOReturnNotFound;
+ proc = NULL;
+ if (kIOTrackingGetMappings == selector) {
+ if (value != -1ULL) {
+ proc = proc_find(value);
+ if (!proc) {
+ return kIOReturnNotFound;
+ }
+ }
+ }
+
+ bzero(&siteInfo, sizeof(siteInfo));
+ lck_mtx_lock(gIOTrackingLock);
+ queue_iterate(&gIOTrackingQ, queue, IOTrackingQueue *, link)
+ {
+ 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;
+ }
+ }
+
+ *result = data;
+ if (proc) {
+ proc_rele(proc);
+ }