Exc_bad_access

Discussion in 'Mac Programming' started by Thethuthinang, Mar 31, 2011.

  1. Thethuthinang macrumors member

    Joined:
    Jan 3, 2011
    #1
    I am getting a "EXC_BAD_ACCESS" message when I try to allocate memory for a two dimensional array. While changing my code around to figure out what was going on, I was getting some inconsistent effects (code works fine, then doesn't next time it is run). I get the error for the following, which is contained in a method that is called from another class:

    Code:
    float **floatArray;
    //breakpoint 1
    floatArray = malloc(400*sizeof(float*));
    for(int i = 0 ; i < 400 ; i++){
            //breakpoint 2
    	floatArray[i] = malloc(300*sizeof(float));
    }
    //breakpoint 3
    
    This is the debugger output:

    Code:
    (gdb) continue
    Current language:  auto; currently objective-c
    (gdb) continue
    Program received signal:  “EXC_BAD_ACCESS”.
    (gdb) 
    
    Any ideas what is going on? As I mentioned, results were inconsistent. Another example: this same section of code worked fine at least once when I had variables in place of 300 and 400.
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    This code is unlikely to crash on its own. Can you post the smallest amount of code you can that still crashes? Include headers and implementation files. It is likely that the heap is already in a bad way when you make it here, and this grab for a few hundred KB brings that to the surface.

    -Lee
     
  3. Thethuthinang thread starter macrumors member

    Joined:
    Jan 3, 2011
    #3
    After commenting out a majority of my other code, I find that this crashes. I load an image, translate it into a bitmap, then allocate memory for other arrays:

    THGeometrize.h:
    Code:
    #import <Cocoa/Cocoa.h>
    
    @interface THGeometrize : NSObject {
    	NSImage* image;
    	unsigned char*** bitmap;
    	int width, height;  
    	
    	int dimensionWidth, dimensionHeight;
    	
    	NSPoint **pointArray;
    	float **hEdges, **dEdges, **vEdges;
    }
    
    -(id)initWithFile:(NSString*)file;
    -(void)makePointArray;
    -(void)makeEdgeArrays;
    @end
    
    
    THGeometrize.m:

    Code:
    #import "THGeometrize.h"
    
    @implementation THGeometrize
    -(id)initWithFile:(NSString*)file{
    	self = [super init];
    	if(self){
    		image = [[NSImage alloc] initWithContentsOfFile:file];
    		NSBitmapImageRep* rep = [[image representations] objectAtIndex:0];
    		
    		unsigned char* bitmapData = [rep bitmapData];
    		
                    height = [rep pixelsHigh];
    		width = [rep pixelsWide];
    
    		//Allocate bitmap
    		bitmap = malloc(width*sizeof(unsigned char**));
    		for(int i = 0 ; i < width ; i++){
    			bitmap[i] = malloc(height*sizeof(unsigned char*));
    			for(int j = 0 ; j < height ; j++){
    				bitmap[i][j] = malloc(3*sizeof(unsigned char));
    			}
    		}
    		
    		//Fill bitmap - coordinates are flipped :|
    		int bytesPerPixel = [rep bitsPerPixel]/8;
    		int bytesPerRow = [rep bytesPerRow];
    		for(int j = 0 ; j < height ; j++){
    			for(int i = 0 ; i < width ; i++){
    				bitmap[i][j][0] = bitmapData[(height - 1 - j)*bytesPerRow + bytesPerPixel*i];
    				bitmap[i][j][1] = bitmapData[(height - 1 - j)*bytesPerRow + bytesPerPixel*i + 1];
    				bitmap[i][j][2] = bitmapData[(height - 1 - j)*bytesPerRow + bytesPerPixel*i + 2];
    			}
    		}
    		
    		[self makePointArray];
    		
    		[self makeEdgeArrays];
    		
    	}
    	
    	return self;
    	
    }
    
    -(void)makePointArray{
    	
    	dimensionHeight = 300; 
    	dimensionWidth = 400;
    	
    	//Make new array
    	pointArray = malloc(dimensionWidth*sizeof(NSPoint*));
    	for(int i = 0 ; i < dimensionWidth ; i++){
    		pointArray[i] = malloc(dimensionHeight*sizeof(NSPoint));
    	}
    	
    	NSPoint p;
    	
    	for(int i = 0 ; i < dimensionWidth ; i++){
    		for(int j = 0 ; j < dimensionWidth ; j++){
    			p.x = width*i/(dimensionWidth - 1);
    			p.y = height*j/(dimensionHeight - 1);
    			pointArray[i][j] = p;
    		}
    	}
    }
    
    -(void)makeEdgeArrays{
    	
    	hEdges = malloc(dimensionWidth*sizeof(float*));
    	vEdges = malloc(dimensionWidth*sizeof(float*));
    	dEdges = malloc(dimensionWidth*sizeof(float*));
    	
    	for(int i = 0 ; i < dimensionWidth - 1 ; i++){
    		hEdges[i] = malloc(dimensionHeight*sizeof(float));
    	}
    	for(int i = 0 ; i < dimensionWidth ; i++){
    		vEdges[i] = malloc((dimensionHeight - 1)*sizeof(float));
    	}
    	for(int i = 0 ; i < dimensionWidth - 1 ; i++){
    		dEdges[i] = malloc((dimensionHeight - 1)*sizeof(float));
    	}
    	
    }
    
    @end
    
    THController.h:

    Code:
    #import <Cocoa/Cocoa.h>
    #import "THGeometrize.h"
    
    @interface THController : NSObject {
    	THGeometrize* geo;
    }
    
    @end
    
    THController.m

    Code:
    #import "THController.h"
    
    @implementation THController
    -(void)awakeFromNib{
    	geo = [[THGeometrize alloc] initWithFile:@"/images/tester.jpg"];
    }
    
    Now, I didn't remove other classes from the project, but these were not accessed (beyond "init" for a view class). Is the code posted enough to see the problem?
     
  4. Thethuthinang thread starter macrumors member

    Joined:
    Jan 3, 2011
    #4
    I had similar code in another project. I created a new project and added the classes to the project. The new project ran fine before I made changes to it.
     
  5. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #5
    When it crashes open the debugger, it should tell you exactly where it's crashing.
     
  6. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #6
    Are you implying that it doesn't run fine after making changes? Or that it still runs fine after making changes?

    And what is the significance of "similar code" if it's not actually identical code? "Similar code" could just mean that it's the same without the bug you accidentally introduced on line 183 when the cat jumped on your lap and you typed i instead of j (hypothetically).


    You should have a crash log with a stack trace from the crashing program. Look there for what the stacks are when the crash occurs. That may shed some light.

    Otherwise break the problem code down into smaller pieces, and set strategic breakpoints. If a breakpoint is reached before it crashes, check the heap state for corruption. If a breakpoint isn't reached before it crashes, you know the code after that breakpoint can't be a cause, so move the breakpoint earlier.

    There's no magic recipe for finding the problem. You'll have to apply basic decomposition (Break It Down) and debugging skills. Using test data is a basic debugging skill. Try that for the image-data. Also try different image dimensions, small and large, square and non-square, single row, single column, two row, two column, etc. Figure out which ones work and which ones fail.
     
  7. Thethuthinang thread starter macrumors member

    Joined:
    Jan 3, 2011
    #7
    I'm not sure what the stack trace is. Is this what is automatically output to the debugger console when the exception is received?

    Usually I get no information but "EXC_BAD_ACCESS". At times, it seems like I have the location for when the error is received pinpointed. But this sometimes changes (honestly). I reshuffled the breakpoints again, and ended up with just a single breakpoint just before "return self" in "-(id)initWithFile:(NSString*)file". Now I got:
    Code:
    Geometrization(2636) malloc: *** error for object 0xff7c00: incorrect checksum for freed object - object was probably modified after being freed.
    *** set a breakpoint in malloc_error_break to debug
    
    So I am trying to figure out what the object 0xff7c00 is, but the error location has changed again.
     
  8. SidBala macrumors 6502a

    Joined:
    Jun 27, 2010
    #8
    Are you checking if the malloc actually allocates space and doesn't just return a null?

    Make sure to free the allocated items after you are done.

    These are very common memory errors that show the symptoms you describe(working the first time, but not again). Go through the code and fix them.

    And also, never use multidimensional arrays like that unless if you really need the arrays to be jagged or very large. If they are just rectangular arrays, always use a 1D array and a lookup function to translate to the appropriate index.
     
  9. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #9
    Code:
    for(int i = 0 ; i < dimensionWidth ; i++){
    		for(int j = 0 ; j < dimensionWidth ; j++){
    These lines in makePointArray are causing a buffer overflow with your current values of dimensionWidth and dimensionHeight, and would anytime the height is smaller than the width, as i and j are used as references in a dynamically allocated array. You'll walk 100 items off the end of the array for every pixel of width. I didn't get this when i ran once, but if i init'd in a loop a few times i did get a crash.

    The fun thing about ruining memory is you never know how exactly it will affect your program, so it's notoriously difficult to debug.

    -Lee
     
  10. SidBala macrumors 6502a

    Joined:
    Jun 27, 2010
    #10
    aha! Lee has it.

    Looks like you copy pasted those for loops forgetting to change the limits.
     
  11. holmesf macrumors 6502a

    Joined:
    Sep 30, 2001
    #11
    The unpredictable behavior is likely not because malloc is returning null (which is a pretty rare thing unless your requested allocation size is GB in size or negative). I agree with your other points though, high dimension multidimensional arrays should almost always be avoided. I think most programmers consider them bad form.

    Anyway...

    EXEC_BAD_ACCESS does not simply happen on every out of bounds array access, but rather only when you access a page of memory that is not valid in the context of your application. Memory pages are at a relatively coarse granularity, usually something like 4KB in size. Therefore out of bounds accesses often still fall within a valid page of memory and so they do not throw an exception. It's the hardware that is throwing the exception, and the OS which catches it, passing it on to your program which will normally respond by terminating itself.

    The somewhat random nature of dynamic memory allocations alignment to memory pages can make things harder to debug, because as you've noticed they can cause non-deterministic runtime behavior. There are a number of debugging features to help you produce more predictable behavior:

    http://developer.apple.com/library/...ug.html#//apple_ref/doc/uid/20001884-CJBJFIDD
     
  12. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #12
    They aren't difficult if you write a single function for making them. A single function is also easier to test, easier to debug, easier to analyze the logic of, etc. The posted code is all copy-pasted in-line, which makes it much more prone to errors. The real root of the problem is copy-pasta, not the multidim mallocs.
     
  13. holmesf macrumors 6502a

    Joined:
    Sep 30, 2001
    #13
    I think there are good reasons not to use them. The main reason to use C multidimensional arrays is for the syntactic sugar. But at the same time it enforces a certain structure on your data, and these assumptions get sprinkled throughout your program, making the code harder to refactor. This is why I would use an abstract data structure with accessors instead. But anyway, I don't want this comment to get the whole thread side tracked into a "what color to paint the barn" type discussion. It's just my two cents ... a personal opinion.
     
  14. SidBala macrumors 6502a

    Joined:
    Jun 27, 2010
    #14
    I avoid multidim arrays like the plague. There is nothing wrong with them. But really using a 1D array is so much easier. For sequential iteration, like in the OP's code, the multiple pointer lookups make it much slower too. The only place where they really are needed is when you have jagged arrays. Even then, I prefer to use ADT containers like STL in C++.

    They are pretty much on the same level as using goto for me. Will avoid unless if absolutely required.
     
  15. Thethuthinang thread starter macrumors member

    Joined:
    Jan 3, 2011
    #15
    Wow. Great catch! Thanks Lee.

    Because the behavior was erratic, I ceased thinking there could be an error as simple as that.
     
  16. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #16
    This is why it's critical to post all of your code all the time, but especially when you're having inconsistent behavior that seems memory related. Rarely does the program crash right when you mess something up. Generally once you've ruined memory you've set a landmine that will likely blow your program to smithereens when it's idly walking through the garden.

    -Lee
     
  17. Thethuthinang thread starter macrumors member

    Joined:
    Jan 3, 2011
    #17
    So, more work is being done for a multidimensional array because you have pointer lookups as well as pointer arithmetic, whereas with a single dimensional array you just have arithmetic?
     
  18. holmesf, Apr 2, 2011
    Last edited: Apr 2, 2011

    holmesf macrumors 6502a

    Joined:
    Sep 30, 2001
    #18
    On multidimensional arrays you still just have arithmetic so long as the array is statically allocated.

    For example:
    Code:
    int foo[3][5]
    Will use arithmetic only when computing addresses.

    If the array is jagged, however, then yeah, you're going to have pointer chasing. Pointer chasing is usually slow because it's high latency, has serial dependencies, and might result in some cache unfriendliness if your data is spread about. In any case don't worry about it unless your code is performance critical.
     

Share This Page