Swift pointer problems with MACH_TASK_BASIC_INFO

Took me a bit to update Airspeed Velocity's answer to the latest swift syntax (Swift 3, beta 6), but here is what I got:

func report_memory() {
    var info = mach_task_basic_info()
    let MACH_TASK_BASIC_INFO_COUNT = MemoryLayout<mach_task_basic_info>.stride/MemoryLayout<natural_t>.stride
    var count = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)

    let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
        $0.withMemoryRebound(to: integer_t.self, capacity: MACH_TASK_BASIC_INFO_COUNT) {
            task_info(mach_task_self_,
                      task_flavor_t(MACH_TASK_BASIC_INFO),
                      $0,
                      &count)
        }
    }

    if kerr == KERN_SUCCESS {
        print("Memory in use (in bytes): \(info.resident_size)")
    }
    else {
        print("Error with task_info(): " +
            (String(cString: mach_error_string(kerr), encoding: String.Encoding.ascii) ?? "unknown error"))
    }
}

Hope that's helpful.


For a quick copy and paste solution in Swift 5, use

func reportMemory() {
    var taskInfo = task_vm_info_data_t()
    var count = mach_msg_type_number_t(MemoryLayout<task_vm_info>.size) / 4
    let result: kern_return_t = withUnsafeMutablePointer(to: &taskInfo) {
        $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
            task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), $0, &count)
        }
    }
    let usedMb = Float(taskInfo.phys_footprint) / 1048576.0
    let totalMb = Float(ProcessInfo.processInfo.physicalMemory) / 1048576.0
    result != KERN_SUCCESS ? print("Memory used: ? of \(totalMb)") : print("Memory used: \(usedMb) of \(totalMb)")
}

When interacting with C functions, you can't rely on the compiler's error messages - break it down parameter by parameter, command-clicking until you know what you're working with. To start with, the types you're running into are:

  • task_name_t: UInt32
  • task_flavor_t: UInt32
  • task_info_t: UnsafeMutablePointer<Int32>
  • UnsafeMutablePointer<mach_msg_type_number_t>: UnsafeMutablePointer<UInt32>
  • kern_return_t - Int32

There's one tricky Swift bit along with a bug in your code standing in your way here. First, the task_info_out parameter needs to be a UnsafeMutablePointer<UInt32>, but needs to actually point to an instance of mach_task_basic_info. We can get around this by creating a UnsafeMutablePointer<mach_task_basic_info> and wrapping it in another UnsafeMutablePointer at call time - the compiler will use type inference to know we want that wrapping pointer to be sub-typed as UInt32.

Second, you're calling sizeof(mach_task_basic_info_t) (the pointer to mach_task_basic_info) when you should be calling sizeinfo(mach_task_basic_info), so your byte count ends up too low to hold the data structure.

On further research, this got a little more complicated. The original code for this was incorrect, in that size should be initialized to the constant MACH_TASK_BASIC_INFO_COUNT. Unfortunately, that's a macro, not a simple constant:

#define MACH_TASK_BASIC_INFO_COUNT (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t)) 

Swift doesn't import those, so we'll need to redefine it ourselves. Here's working code for all this:

// constant
let MACH_TASK_BASIC_INFO_COUNT = (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t))

// prepare parameters
let name   = mach_task_self_
let flavor = task_flavor_t(MACH_TASK_BASIC_INFO)
var size   = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)

// allocate pointer to mach_task_basic_info
var infoPointer = UnsafeMutablePointer<mach_task_basic_info>.alloc(1)

// call task_info - note extra UnsafeMutablePointer(...) call
let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)

// get mach_task_basic_info struct out of pointer
let info = infoPointer.move()

// deallocate pointer
infoPointer.dealloc(1)

// check return value for success / failure
if kerr == KERN_SUCCESS {
    println("Memory in use (in bytes): \(info.resident_size)")
} else {
    let errorString = String(CString: mach_error_string(kerr), encoding: NSASCIIStringEncoding)
    println(errorString ?? "Error: couldn't parse error string")
}

Tags:

Swift

Mach