Thread: subtle segmentation fault

  1. #1
    Kiss the monkey. CodeMonkey's Avatar
    Join Date
    Sep 2001
    Posts
    937

    subtle segmentation fault

    My haste in trying to implement a feature crudely "just to see if it works" has landed me in segmentation-fault-land without good debugging skills.

    Come along for the ride, if you like.

    It involves the dereferencing of pointers in a 2D array of doubles. This memory management is dealt with inside a class array2d. These array2d objects are stored in a vector. My program then uses them this way. The segmentation fault is happening when I try to iterate a function over one of these array2d. Function-like classes (inherit from virtual func2d) can be iterated over an array2d.

    Let me give you some background into what the suspect code does. It applies a process over a class grid : public array2d (and a back buffer, another grid). Then it stores that grid. Now it creates another grid and does the same process, but periodically subtracts the values in the previous grid from the new one. Then it creates another grid and applies the process, but this time subtracts both the old grids from the new one, etc..... until a predetermined number of grids have been processed. These grids are also called "modes" in the program.

    Ok -- so. I get a segmentation fault whenever mode_to_find is 2 or greater. The program handles the first two modes fine (0 and 1), but chokes up at 2.
    Here's the stack trace:
    Code:
    #0 0x80524b3	array2d<double, unsigned int>::operator() (this=0x8060470, x=1, y=0) (/home/david/Documents/c++/cavity/array2d.hpp:34)
    #1 0x80526f3	functions::inner_product::operator() (this=0x80604a8, x=1, y=0, g=@0x806c9a8, Kd=1) (/home/david/Documents/c++/cavity/grid.h:191)
    #2 0x805648f	grid::iterate_region(this=0x806c9a8, reg=@0xbfc43d98, f=@0x80604a8) (/home/david/Documents/c++/cavity/grid.cpp:14)
    #3 0x80537ca	functions::subtract_mode::update_inner_prod(this=0x8060498) (/home/david/Documents/c++/cavity/grid.h:221)
    #4 0x80511c2	do_relax(modes=@0xbfc43d8c, reg=@0xbfc43d98, lap=@0xbfc43d64, subtractors=@0xbfc43d80) (/home/david/Documents/c++/cavity/cavity.cpp:125)
    #5 0x8051d15	primary_logic() (/home/david/Documents/c++/cavity/cavity.cpp:72)
    #6 0x8052019	main(argc=1, argv=0x0) (/home/david/Documents/c++/cavity/cavity.cpp:23)
    I will bold the involved regions, keeping the rest for context:

    Code:
    //in cavity.cpp, the main source file:
           grid::region reg = bmp.region(paths[BOUND]);
    	grid lap(bmp.width(), bmp.height());     //laplacian field
    
    	std::vector<grid> modes;
    	std::vector<functions::subtract_mode*> subtractors;
    	//subtractors[n-1] will act on modes[n]
    	for(unsigned int i = 0; i <= mode_to_find; ++i)//problem when i > 1
    	{
    		modes.push_back( grid(bmp.width(), bmp.height()) );
    		std::vector<grid>::iterator newg = modes.end() - 1;
    		if(i) subtractors.push_back( new functions::subtract_mode(*newg, *(newg-1), reg) );
    		for(unsigned int j = 0; j < subtractors.size(); ++j)
    			subtractors[j]->set_current_mode(*newg);
    
    		functions::set_to setit(0.0);
    		for_each(*newg, setit);		//zeros out the whole grid (now that we know where the inside is)
    		functions::make_paraboloid mkpara;
    		newg->iterate_region(reg,mkpara);		//initial conditions (parabolic dome over grid)
    
    		do_relax(modes, reg, lap, subtractors); 		//crunch it all.
    		if(flags[check]) do_check(*newg);	//compare middle crossection with a sine wave
    		if(flags[freq]) do_freq(*newg,reg);	//output frequency distribution, and possibly multirun entry
    		if(flags[ffact]) do_ffactor(*newg,reg);   //output form factor distribution
    		if(flags[qfact]) do_qfactor(*newg,reg, lap);   //output quality factor distribution
    
    		//now we change the number at the end of all the output files.
    		for(unsigned int j = COMMAND+1; j <= NINJA; ++j)
    			paths[j].replace(paths[j].rfind('.')-1,1,1,'0'+i+1);
    	}
    Code:
    //further down in cavity.cpp:
    void do_relax(std::vector<grid> & modes, grid::region & reg, grid & lap, std::vector<functions::subtract_mode*> & subtractors)
    {
    	using namespace settings;
    	assert(modes.size()); //we don't want an empty container
    	grid & g = modes.back();
    	std::size_t n_cpu;
    	printdb( n_cpu = /*boost::thread::hardware_concurrency()*/1 );
    
        	std::cout << "Relaxing..." << std::endl;
    	stopwatch timer;
    	if(n_cpu == 1) //single thread
    	{
    		functions::calc_lap lfunc(lap);       //function objects
    		functions::relax rfunc(lap,dt);
    		if(modes.size() == 1) //if we're just doing this once (or if this is the first time)
    		{
    			timer.start();
    			for(uword i = 0; i < settings::time_steps; ++i)
    			{
    				g.iterate_region(reg, lfunc);
    				g.iterate_region(reg, rfunc);
    			}
    		}
    		else //if we want to substract the old modes.
    		{
    			for(uword i = 0; i < settings::time_steps; ++i)
    			{
    				g.iterate_region(reg, lfunc);
    				g.iterate_region(reg, rfunc);
    				if(i % 5 == 0)
    				{
    					for(unsigned int j = 0; j < subtractors.size(); ++j)
    					{
    						subtractors[j]->update_inner_prod();
    						g.iterate_region(reg,*subtractors[j]);
    					}
    				}				
    			}
    		}//if subtract older modes
    	}//if single thread
    //. . . . . and so on . . . .
    Code:
    class subtract_mode : public func2d
    {
    public:
    	subtract_mode(grid & curr_mode, grid & old_mode, grid::region & reg)
    		: curr(&curr_mode), old(old_mode), r(reg), ip(old_mode)
    	{
    		functions::sum< square<grid::data_type> > sumofsq;
    		old_mode.iterate_region(reg, sumofsq);
    		ssq = sumofsq.value();
    		update_inner_prod();
    	}
    	void operator()(grid::size_type x, grid::size_type y, grid::base_type & g, grid::data_type Kd = 1.0)
    	{
    		g(x,y) -= ip.value() / ssq * old(x,y);
    	}
    	void update_inner_prod()
    	{
    		ip.reset();
    		curr->iterate_region(r,ip);
    	}
    	grid * set_current_mode(grid & g)
    	{
    		grid * ret_ptr = curr;
    		printdb(curr = &g);
    		return ret_ptr;
    	}
    private:
    	grid * curr;
    	grid & old;
    	grid::region & r;
    	functions::inner_product ip;
    	grid::data_type ssq;
    };
    And finally.... the code essential to grid:
    Code:
    template<typename T, typename uint = std::size_t>
    class array2d
    {
      protected:
    	T** data;
    	const uint sz;
    	const uint w;
    	const uint h;
      public:
        	typedef T data_type;
        	typedef uint size_type;
        	typedef std::pair<uint, uint> point2d;
    
    	void alloc(const T & fill = T());
    	array2d(uint width_, uint height_, const T & fill = T()) : w(width_), h(height_), sz(width_*height_)
    		{ alloc(fill); }
    	array2d & operator = (array2d<T,uint> & other);
    	array2d & operator = (const array2d<T, uint> & other);
    	array2d(array2d<T,uint> & other) : w(other.width()), h(other.height()), sz(other.size())
    		{ alloc(); *this = other; }
    	array2d(const array2d<T,uint> & other) : w(other.width()), h(other.height()), sz(other.size())
    		{ alloc(); *this = other; }
    	~array2d();
    	const uint size() const { return sz; }
    	const uint width() const { return w; }
    	const uint height() const { return h; }
    	T & operator()(uint x, uint y) { return data[x][y]; }
    	const T & operator()(uint x, uint y) const { return data[x][y]; }
    	T & operator()(point2d p) { return (*this)(p.first, p.second); }
    };
    
    template<typename T, typename uint>
    void array2d<T,uint>::alloc(const T & fill)
    {
    	data = new T*[w];
    	for(uint i = 0; i < w; ++i)
    	{
    		data[i] = new T[h];
    		for(uint j = 0; j < h; ++j)
                		data[i][j] = fill;
    	}
    }
    
    template<typename T, typename uint>
    array2d<T,uint> & array2d<T,uint>::operator = (const array2d<T,uint> & other)
    {
    	#ifndef NDEBUG
    	std::cout << "!!!!!!!! WARNING array2d BEING COPIED !!!!!!!!" << std::endl;
    	#endif
    	assert(other.width() == w && other.height() == h);
    	for(uint i = 0; i < w; ++i)
    	{
    		for(uint j = 0; j < h; ++j)
    			data[i][j] = other(i,j);
    	}
    	return *this;
    }
    Code:
    //and the grid...
    class grid : public array2d<double, uword>
    {
    public:
            typedef array2d<double,uword> base_type;
    	typedef array2d<double,uword>* base_pointer_type;
    	grid(size_type w_in, size_type h_in, data_type fill = 0.0); //see declaration of array2d for 'data_type'
    
    	struct span;
    	typedef std::vector<span> region;
    	static size_type num_points(const region & r, size_type until = std::numeric_limits<size_type>::max());
    
    	bool iterate_region(const region & reg, func2d & f);
    	void write_file(const std::string & path);
    };
    
    bool grid::iterate_region(const region & reg, func2d & f)
    {
    	for(unsigned int i = 0; i < reg.size(); ++i)	//over all spans
    	{
    		for(unsigned int j = 0; j < reg[i].dy; ++j)	//for each span
    		{
    			f(reg[i].x, reg[i].y + j, *this, reg[i].Kd);
    		}
    	}
    	return true;
    }
    I understand if the code is too convoluted, or if nobody has the time. Thanks in advance for any help.

    When it comes down to it, we're trying to use data[1][0] in some array2d which, apparently, hasn't been initialized correctly. But how? All of the constructors initialize the memory. Is it the copy semantics?

    Is it the grid* curr in subtract_mode? It seems always to refer to a valid address.

    *edit*

    We also need class inner_product. Perhaps grid& old (in subtract_mode) becomes a bad reference when std::vector moves the grids around after a push_back()?
    Code:
    class inner_product : public func2d
    {
    public:
        inner_product(grid & other) : g2(other) {}
        void operator()(grid::size_type x, grid::size_type y, grid::base_type & g, grid::data_type Kd = 1.0)
        {
    	s += g(x,y) * g2(x,y);
        }
        void reset()
        { 
    	s = 0.0;
        }
        const grid::data_type value() const { return s; }
    private:
        grid::data_type s;
        grid & g2;
    };
    Last edited by CodeMonkey; 01-07-2009 at 11:43 AM. Reason: bolding
    "If you tell the truth, you don't have to remember anything"
    -Mark Twain

  2. #2
    Kiss the monkey. CodeMonkey's Avatar
    Join Date
    Sep 2001
    Posts
    937
    Yes, I now strongly believe that grid & subtract_mode::old is the culprit. How can I ensure that grid &old refers to a valid object as it moves in memory within the vector? I'd rather not let class subtract_mode know that a vector is involved.
    "If you tell the truth, you don't have to remember anything"
    -Mark Twain

  3. #3
    Kiss the monkey. CodeMonkey's Avatar
    Join Date
    Sep 2001
    Posts
    937
    Indeed, that was the issue.
    "If you tell the truth, you don't have to remember anything"
    -Mark Twain

  4. #4
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    Glad we could help

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. segmentation fault... first time with unix...
    By theMethod in forum C Programming
    Replies: 16
    Last Post: 09-30-2008, 02:01 AM
  2. Segmentation fault
    By bennyandthejets in forum C++ Programming
    Replies: 7
    Last Post: 09-07-2005, 05:04 PM
  3. Segmentation fault
    By NoUse in forum C Programming
    Replies: 4
    Last Post: 03-26-2005, 03:29 PM
  4. Locating A Segmentation Fault
    By Stack Overflow in forum C Programming
    Replies: 12
    Last Post: 12-14-2004, 01:33 PM
  5. Segmentation fault...
    By alvifarooq in forum C++ Programming
    Replies: 14
    Last Post: 09-26-2004, 12:53 PM