Memory leak when accessing GCController's gamepad - ios

I'm adding support for the iOS GameController Framework to a cross-platform input library, and running into some trouble with a memory leak. Here's a simplified version of the troublesome code:
void InputSystem::UpdateControllerState(size_t nControllerIndex)
{
Controller& c = mController[nControllerIndex];
GCController* gc = c.mGameController;
GCExtendedGamepad* gp = [gc extendedGamepad]; // ***
c.mState[kIOSThumbLX] = gp.leftThumbstick.xAxis.value;
c.mState[kIOSThumbLY] = gp.leftThumbstick.yAxis.value;
// ... etc
}
The memory leak seems to be caused by the line marked with ***. I can comment out the lines reading values from the controller and memory is still leaked (under certain circumstances; this could be the key). I have tried adding [gc release] at the end of the function but that doesn’t seem to have any impact. This doesn’t really surprise me since I am not retaining the pointer so I should not need to release it.
The interesting thing is that this code works fine in some conditions. As I was working on the implementation, I set up a basic test that worked just fine. The problem only showed up when I tried hooking up the existing cross-platform unit tests for this library. Here is a bit more detail on the two setups:
(No leak) I set up my InputSystem in applicationDidFinishLaunching,
along with a displayLink to call a function each frame. This
function would call InputSystem code that would call the above
UpdateControllerState method and then print out any new input (button
presses, etc) detected. This setup worked fine; I saw the input from
the controller that I expected, and memory use stayed nice and flat.
(Memory leak) In order to test more rigourously, I hooked in the
cross-platform InputSystem unit tests. These tests include an
interactive mode that continuously loops to poll and print out new
inputs. This doesn’t give iOS the chance to receive input and update
the GCController, so I created a new thread in
applicationDidFinishLaunching to call into the tests. This worked
for getting input, but memory use was steadily increasing. As I
mentioned above, commenting out parts of the new code helped me
narrow it down to the highlighted line above.
I don't understand why these two setups behave differently in this way. Any help understanding this problem would be appreciated. I don't know at this point if this is a problem with how I am using the GCController class, or something I am not understanding about iOS memory in general. Thanks!
EDIT: I just tried storing the pointer to the extendedGamepad in my Controller object, so I could read from it directly and remove the line marked with ***. In this case, however, memory is leaked when I include the lines that read the values from the buttons or thumbsticks.

Related

What can cause Exc_Bad_Access on first function call in first VC?

Have app in App Store running fine. New version adds a bunch of calculations and minimal new interface. During dev and testing, runs (seemingly) perfectly in Simulator on all devices. Started testing on physical devices, and getting Bad_Access 1 or 2. I read every link on those I could find, and realize it's a difficult error. Using Swift, so no specifically dereferenced pointers of the type mentioned in most links.
Refactored code in many ways over last week. Eventually got it as simple as possible, posted here. My AppDelegate.swift is as basic as possible with no extra stuff or app-specific processing/initialization. The storyboard entry point is a collection view controller of type MainCollectionViewController. In the viewDidLoad method of that controller, the very first statement is a function call to getDrawingInstructions() with parameters. Using default parameter values, and the first two parameters are structs full of CGFloat, Bool, and UIColor.
As you can see, it's crashing as it gets to that func before even entering the func and executing the func's code. The first func call from viewDidLoad of the initial vc.
I've cleaned the build folder and restarted XCode, I'm Swift 5 compliant, I've updated to XCode 11 GM, I've turned on zombies and have no console message. Looking for suggestions on what to try next, 'cause I'm stumped on how I could get screwed up addresses before (seemingly) executing any app-specific code. And remember, this part runs in Simulator and is unchanged from the prior version that ran fine on all devices.
(In different iterations of code, Zombies would yield a console message like objc[7801]: Class _NSZombie_FBSXPCMessage is implemented in both ?? (0x162585890) and ?? (0x162576dd0). One of the two will be used. Which one is undefined. That gives me no clue where to look, especially with the ??.)
Thank you for any insights.
Edit: Per suggestions, removed parameters from function (into struct), but the result was the same as before.
Edit: Removed first argument to the call, a large struct, and got same result. Also got console message: objc[8038]: Class _NSZombie__NSCallStackArray is implemented in both ?? (0x163481590) and ?? (0x163480750). One of the two will be used. Which one is undefined. Not sure how to use this message.
Edit: If the called func is a stub that returns immediately, it is safely processed. So...it's not even entered with a trace if there's code, but it processes if there's an immediate return. Confusing.
Edit: Reconfigured yet again to remove all arguments to the function and just initialize them with default values in the function. That crashes with the same error at the same place, the entry point to the function, without reaching any of the initialization statements in the function. So the first statement in viewDidLoad of the primary VC calls a func with no arguments and crashes with a dereferenced pointer or similar memory error without stepping into that func. I'm not sure how to make that even intentionally happen.

How iOS handles memory corruption from low level C/ Objective-C code?

I have an iOS app that uses some legacy low level memory manipulation code using pointers. I'm debugging an issue where multiple threads are causing multiple copies of these code to be executed on global variables simultaneously and cause memory corruption by writing invalid length or overwriting data.
The effect is that the length of the buffer below may change. I've seen iOS throw EXC_BAD_ACCESS or EXC_BREAKPOINT as a result of these calls.
My question is - would iOS always throw exceptions when I use memcpy incorrectly, or will it complain only when I write outside my allowed memory?
In other words, is my code free to corrupt my memory and create invisible issues, without causing exceptions, as long as it does not step outside allocated memory or access deallocated memory?
NSData* buffer = ...
Byte *array = (Byte*)malloc(buffer.length);
memcpy(array, buffer.bytes, buffer.length);
The last time I checked or had such an issue it would only complain when outside your own allowed memory. In my situation I was writing over whatever objects followed the address I was trying to write to. The result was what seemed a random crash on objects as unrelated as even NSString.
My mistake was something like the following:
MyStructure *myStructure = malloc(sizeof(myStructure)); // Incorrect
MyStructure *myStructure = malloc(sizeof(MyStructure)); // Fixed
A simple autocomplete error which led to days long hunting of this bug. MyStructure was a fairly big one so accessing some property (both read and write) would in this case simply overflow and read/write through whatever was after it. It eventually randomly crash; sometimes bad access, other times just some random exception on a random object.

Method mysteriously exits execution in the middle to resume the rest of the program?

So i've got this code that tries to find an unused upload name, using the user's email and a number at its end. It does this with a list of uploaded objects we've already collected, the user's email.(upload_name), and the
current number that might be open (it is incremented when a match is found).
The list is not sorted, and it's pretty tricky to sort for a few reasons, so I'm having the method read through the list again if it reaches the end and the upload_number has changed.
- (NSString*)findUnusedUploadNameWithPreviousUploads:(NSMutableArray*)objects withBaseUploadName:(NSString*)upload_name {
previous_upload_number = upload_number;
for (NSString *key in objects) {
// the component of the object name before the first / is the upload name.
NSLog([key componentsSeparatedByString:#"/"][1]);
if ([[key componentsSeparatedByString:#"/"][1]
isEqualToString:([NSString stringWithFormat:#"%#_%ld", S3KeyUploadName1, upload_number])]) {
upload_number++;
NSLog([NSString stringWithFormat:#"upload name: %#_%ld", S3KeyUploadName1, upload_number]);
}
NSLog(#"pang");
}
NSLog(#"ping");
if (previous_upload_number == upload_number) {
return [NSString stringWithFormat:#"%#%ld", upload_name, upload_number];
}
return [self findUnusedUploadNameWithPreviousUploads:objects withBaseUploadName:upload_name];
}
The problem is, the program never reads the "ping". it just leaves the method after the first for loop is done.
Edit: No the NSlogs are fine, you can do simple string OR StringWithFormat.
Edit: Don't mind the unnecessary use of recursion, I did this because the simple way was having the same problem and i wanted to see if a different (albeit unnecessarily recursive) way would share that problem. It does.
Edit: I set a breakpoint in the for loop, and I set a break point at the "ping". It does not reach the ping. It completes the for loop and the ditches the whole thing.
Edit: Please try to help me figure out why it's exiting the the method immediately after the for loop. I'm aware this is stylistically meh, and I promise I'll make it shiny and spotless when it works. =]
Edit: to be clear, the method DOES exit. it does so early I know this because the rest of the program following this method (which is not threaded such that it wouldn't have to wait for it) runs AFTER this for loop, consistently.
There are a couple of possible explanations for the described behavior:
The method never exits. For some reason it blocks or performs infinitely somewhere in the loop. Make sure this is not the case by setting a breakpoint after the place where the message is called (i.e. the place to where it should return).
The method, or some method it calls, throws an exception. While seldom and unsupported in productive Cocoa code it could be some misbehaving 3rd party library or just a simple programmer error, where Cocoa actually does throw. Make sure this does not happen by setting an exception breakpoint in Xcode.
Undefined behavior. This is, sadly, part of official C and heavily exploited by modern compilers. It basically means: Anything can happen, if there's something in your code, where the standard says that the behavior is not defined. One example would be accessing a deallocated object. Another fine reason of undefined behavior can be threads accessing common data in an unsynchronized way.
Other than exceptions there's no explanation for a method to "exit early". If you still can't find the reason I suggest you invest some time to learn the debugger. Single stepping, like Aris suggested, might be a way to find out what's going on.

message sent to deallocated instance error

Im constantly being given an error that reads *** -[NSKeyValueObservance retain]: message sent to deallocated instance 0x86c75f10. I have tried running the Zombies template and here is the screenshot of what it provides.
It points to a managedObject, and I'm having trouble figuring out where the object is being deallocated. Here is the block of code that the compiler takes me to after each crash.
- (void)setIsFavourite:(BOOL)isFavourite shouldPostToAnalytics:(BOOL)shouldPostToAnalytics;
{
// check whether we need to generate preferences objects just in time
if(!self.preferences && !self.series.preferences /*&& isFavourite*/)
{
if(self.series)
{
[self.series addPreferencesObject];
}
else
{
[self addPreferencesObject];
}
}
//Crash In here
self.preferences.isFavourite = #(isFavourite);
self.series.preferences.isFavourite = #(isFavourite);
EDIT: If you need to see a larger size of the image here is a larger resolution link.
OK, I hit something similar and found a way to debug this kind of issue with NSKeyValueObservance. To debug, do the following:
In Xcode, open the "Breakpoint Navigator".
Add a new symbolic breakpoint with:
-[NSKeyValueObservance _initWithObserver:property:options:context:originalObservable:]
To that breakpoint, add an action and set it to "Debugger Command".
Set the following command: expr (void)NSLog(#"observer <0x%p>: %# <%p>, property: %#", $arg1, (id)NSStringFromClass((id)[(id)$arg3 class]), $arg3, (id)$arg4)
Click the "Automatically continue after evaluating expression".
Now you can run your application and take the steps necessary to reproduce your crash. And yes, you'll want NSZombies enabled. Note: it's going to run slow and you're going to get a ton of debug output, but just be patient. It'll get there eventually.
When you hit the crash when trying to message a deallocated NSKeyValueObservance, you'll be presented with the address of the original object. Highlight the address and hit cmd-e to enter the text in the search buffer. Then hit cmd-g find the next occurrence of the string in the debugger output. You're going to potentially find the address a couple of times, so look for the address that follows the observer <0x?????> output. The output on that line should tell you what object is being observed and for which property.
In my case, when I figured this all out, it turned out that I was observing a synthesized property that depended on an object in array and during a certain operation, the order of the objects in the array changed without doing the correct KVO notifications, and that caused my crash.
Are you using manual reference counting? If so, why? Convert your app to ARC. Manual reference counting is painful at best, and ARC is much better.
I am an experienced iOS and Mac OS developer and can do either, but I far prefer ARC. It's much less fussy and error-prone.
There is a feature built into Xcode that will convert your project to ARC for you. You might have to do some cleanup afterwords, but it's worth it.
If you do that your problem will likely go away.
As to the specifics, your screenshot is far too small to be able to read it. You will need to post a full-sized image if you want somebody to try to figure out what's going on.
However, in broad terms it sounds to me like you have an autorelease bug.
in manual reference counted code, lots of system method return objects that are "autoreleased." That means that when you receive them their retain count is positive (usually 1) so they stick around. However, they have been added to the "autorelease pool," which means that they are going to be released on the next pass through the event loop if nobody retains them first.
When you receive an autoreleased object you should either accept that it will be released once your current method returns, or retain it.
If you are trying to write Core Data code using manual reference counting and don't understand this then you are setting yourself up for failure.
Core Data is pretty complex, and you should have a solid understanding of Cocoa memory management before attempting to write a program that uses it, especially if you're using manual reference counting.

Something from GPUImageFilters remains in memory even after deallocation

I'm using GPUImage to do a bunch of image processing both in real time and on static images, I noticed that after churning through ~100 thumbnail images each of which has slightly different image processing done to each that there are still objects in memory after they're done processing and they're all related to GPUImageFilters:
(Allocation lifespan is 'created & still living')
You can see the memory spike from some processing I'm doing and after its done, on the other side of the mountain I have some stuff left in memory, I chose some 24KB blocks to examine (there are others). You can see on the right, the first item comes from GPUImageSoftLightBlendFilter, if I click on all 12 items each one comes from a GPUImageFilter (GPUImageHardLightBlendFilter, GPUImageMultiplyBlendFilter, etc). Now if I do the same processing a second time, and expand the memory graph selection you'll see no NEW instances of these objects were made, its as if they took up space in memory once and just hang around:
Sure enough, if I change the memory graph selection to only show the second mountain you see that those line don't show up again because they weren't 'created & still living' again:
Why is this, I don't want the memory from these GPUImageFilter objects hanging around for the lifetime of my app running? I put some logging in the GPUImageFilters to confirm they're being deallocated and dealloc is being called.
From code inspection of the GPUImage source, it looks like the GLProgram object from the last filter used remains set (and retained) by the shared GPUImageContext, which means that its corresponding OpenGL program objects also can't go away. However, the dealloc for the existing GLProgram implementation only marks the current program for deletion. Unlike glDeleteTextures, glDeleteProgram does not unbind the currently-bound program if it's the one being deleted, which means it can't be completely destroyed yet. Therefore, you probably have to make two changes:
Call [GPUImageContext setActiveShaderProgram:nil] to clear the current GLProgram binding, which releases the last reference to GLProgram and marks the current program for deletion.
Ensure that the program is unbound by calling glUseProgram(0). You can either do this any number of ways:
Call it explicitly immediately after the call above, since the correct EAGLContext will already be bound.
Add the call to the body of +[GPUImageContext setActiveShaderProgram:], either by adding it unconditionally above [shaderProgram use], or by calling either [shaderProgram use] or glUseProgram(0) depending on whether shaderProgram is nil.
Have -[GLProgram dealloc] check whether or not it is the currently-bound program with glGetIntegerv(GL_CURRENT_PROGRAM, &program), and unbinding itself if it is.

Resources