2

I had a working program to analyse data in C++ that has produced something like 35 successful data files to date. I was working on Scientific Linux in Code:Blocks when it was working and barring some small errors involving very large grid sizes (1000x1000+) it worked perfectly and produced exactly what I was looking for.

I recently switched to Ubuntu and expected it to work fine and it doesn't. It accepts initial input (the first particle switch) but then crashes immediately with a segmentation fault 139. I have tried to run it in Windows instead with my dual boot but it doesn't seem to recognise the local filing system so I'm forced to ask for help.

It's a long program so I'll reproduce the whole thing. I apologise in advance.

// This program converts the column output of a 1D PIC code into a workable solution

#include <iostream>
#include <fstream>
#include <math.h>
using namespace std;

double calculateXMaximum(double arraymax[], int size)
{
    double maximum = 0;
    for (int k = 1; k < size/2; k++)
    {
        if(arraymax[2*k] > maximum)
        {
            maximum = arraymax[2*k];
        }
    }
    return maximum;
}

double calculateXMinimum(double arraymin[], int size)
{
    double minimum = 0;
    for (int k = 1; k < size/2; k++)
    {
        if(arraymin[2*k] < minimum)
        {
            minimum = arraymin[2*k];
        }
    }
    return minimum;
}

double calculatePXMaximum(double arraymax[], int size)
{
    double maximum = 0;
    for (int k = 1; k < size/2; k++)
    {
        if(arraymax[2*k+1] > maximum)
        {
            maximum = arraymax[2*k+1];
        }
    }
    return maximum;
}

double calculatePXMinimum(double arraymin[], int size)
{
    double minimum = 0;
    for (int k = 1; k < size/2; k++)
    {
        if(arraymin[2*k+1] < minimum)
        {
            minimum = arraymin[2*k+1];
        }
    }
    return minimum;
}

int main()
{
// Variables settable before running program - will set up initialisation later.
double xmin = 0;
double xmax = 0;
double pmin = 0;
double pmax = 0;

int xni = 0;
double xntemp = 0;
double deltax = 0;
int xi; // X interpolates, defined from console for resolution of diagram

int pnj = 0;
double pntemp = 0;
double deltap = 0;
int pi;
int type;
double modifier;

// Determines momentum modifier!

cout << "For particle type, enter 1 (e-) or 2 (p+)" << endl;
cout << "Particle type:  ";
cin >> type;

if (type == 2)
{
    modifier = 1836;
}
else
{
    modifier = 1;
}

ifstream inputFile;
ofstream outputFile;

inputFile.open ("/home/Nick/fi020000.dat");
outputFile.open ("/home/Nick/fi20k.dat");

int dataformat[2];
for(int rd = 0; rd < 2; rd++)
{
    dataformat[rd] = 0;
    inputFile >> dataformat[rd];
}

int records = dataformat[1] + 2;
double data[records];
cout << "Number of particles: " << dataformat[1]/2 << endl;


// Introduction of data from input data file loop.  Produces records.
for (int count = 0; count < records; count++)
{
    inputFile >> data[count];
}

// Calling functions for xmin and xmax.  May streamline later

xmax = calculateXMaximum(data, records) * 1.1;
cout << "Maximum x value: " << xmax << endl;
xmin = calculateXMinimum(data, records) * 1.1;
cout << "Minimum x value: " << xmin << endl;
pmax = calculatePXMaximum(data, records) * 1.1 / modifier;
cout << "Maximum p value: " << pmax << endl;
pmin = calculatePXMinimum(data, records) * 1.1 / modifier;
cout << "Minimum p value: " << pmin << endl;

// Definition of bin size

cout << "Entire desired number of x bins: ";
cin >> xi;
const int xip = xi;
cout << "Enter desired number of p bins: ";
cin >> pi;
const int pip = pi;
cout << "Grid is " << xip << " x " << pip << endl;

// Calculate DELTA X and DELTA P
deltax = (xmax - xmin)/(xip);
deltap = (pmax - pmin)/(pip);

cout << "Resolution of x:  " << deltax << endl;
cout << "Resolution of p:  " << deltap << endl;

int phaseSpace [xip][pip];
for(int i=0; i<xip; i++)
{
    for(int j=0; j<pip; j++)
    {
        phaseSpace[i][j] = 0;
    }
}

for (int phasecount=1; phasecount < (records/2)-1; phasecount++)
{
    xntemp = (data[2*phasecount] - xmin)/deltax;
    xni = floor(xntemp);
    pntemp = ((data[(2*phasecount)+1] / modifier) - pmin)/deltap;
    pnj = floor(pntemp);
    phaseSpace[xni][pnj] = phaseSpace[xni][pnj] + 1;
}

for (int xoutcount = 0; xoutcount < xip; xoutcount++)
{
    for (int poutcount = 0; poutcount < pip; poutcount++)
    {
        outputFile << xmin+((xoutcount+0.5)*deltax) << " " << pmin+((poutcount+0.5)*deltap) << " "<< phaseSpace[xoutcount][poutcount] << endl;
    }
    outputFile << endl;
}


cout << "Program complete" << endl;

return 0;

}

My intent was to finish a 30 page report by this weekend and now the program that I was using to do this has completely fallen apart. I am not a computer scientist - I'm a physicist and I learned C++ less than a month ago. As such, I have no clue what's going on. I know this topic has been seen a lot but I can't really understand the advice.

EDIT: Stack trace is:

#0 0x4010af     ??     ()     (??:??)
#1 0x7ffff7215ea5       __libc_start_main() (/lib/x86_64-linux-gnu/libc.so.6:??)
#2 0x4017f1 ??    () (??:??)

EDIT2: Valgrind results

==4089== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==4089== Command: ./Analysis
==4089== 
For particle type, enter 1 (e-) or 2 (p+)
Particle type:  2
==4089== Warning: client switching stacks?  SP change: 0x7fefff9c0 --> 0x7fe6d7118
==4089==          to suppress, use: --max-stackframe=9603240 or greater
==4089== Invalid write of size 8
==4089==    at 0x4010AF: ??? (in /home/paladin/Contour/Analysis/bin/Release/Analysis)
==4089==    by 0x5673EA4: (below main) (libc-start.c:260)
==4089==  Address 0x7fe6d7118 is on thread 1's stack
==4089== 
==4089== 
==4089== Process terminating with default action of signal 11 (SIGSEGV)
==4089==  Access not within mapped region at address 0x7FE6D7118
==4089==    at 0x4010AF: ??? (in /home/paladin/Contour/Analysis/bin/Release/Analysis)
==4089==  If you believe this happened as a result of a stack
==4089==  overflow in your program's main thread (unlikely but
==4089==  possible), you can try to increase the size of the
==4089==  main thread stack using the --main-stacksize= flag.
==4089==  The main thread stack size used in this run was 8388608.
==4089== 
==4089== Process terminating with default action of signal 11 (SIGSEGV)
==4089==  Access not within mapped region at address 0x7FE6D7111
==4089==    at 0x4A256A0: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-amd64-linux.so)
==4089==  If you believe this happened as a result of a stack
==4089==  overflow in your program's main thread (unlikely but
==4089==  possible), you can try to increase the size of the
==4089==  main thread stack using the --main-stacksize= flag.
==4089==  The main thread stack size used in this run was 8388608.
==4089== 
==4089== HEAP SUMMARY:
==4089==     in use at exit: 17,520 bytes in 4 blocks
==4089==   total heap usage: 4 allocs, 0 frees, 17,520 bytes allocated
==4089== 
==4089== LEAK SUMMARY:
==4089==    definitely lost: 0 bytes in 0 blocks
==4089==    indirectly lost: 0 bytes in 0 blocks
==4089==      possibly lost: 0 bytes in 0 blocks
==4089==    still reachable: 17,520 bytes in 4 blocks
==4089==         suppressed: 0 bytes in 0 blocks
==4089== Rerun with --leak-check=full to see details of leaked memory
==4089== 
==4089== For counts of detected and suppressed errors, rerun with: -v
==4089== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)
Segmentation fault (core dumped)

The fault occurs at the ifstream inputFile statement.

EDIT3: As requested, console session:

paladin@paladin:~/Programming/Contour Constructor 2/bin/Debug$ ./Contour\ Constructor\ 2 
For particle type, enter 1 (e-) or 2 (p+)
Particle type:  2
Segmentation fault (core dumped)

There are 1200402 lines in the input file, corresponding to 600200 particles in the PIC code plus 2 descriptive lines.

EDIT4: Complete shot in the dark here but I originally compiled this under GCC 4.4.7 on Scientific Linux. I am now using the latest Ubuntu version (4.8.1). Has something changed in the interim which would invalidate the size of the file I'm using?

  • `gdb --args prog arg arg` then `r` to run. When the fault occurs, `bt` for a stack trace. (Since core was dumped, you could also load p that image with `gdb` then issue `bt`.) – ikegami Sep 12 '13 at 16:12
  • 5
    ("segfault 139" is redundant. 139 = 11|128 = segfault and core dumped.) – ikegami Sep 12 '13 at 16:14
  • 2
    Did you recompile under Ubuntu? – trojanfoe Sep 12 '13 at 16:14
  • 1
    `valgrind prog arg arg` also useful. – ikegami Sep 12 '13 at 16:15
  • 1
    did you recompile? did you run it in a debugger? – Walter Sep 12 '13 at 16:16
  • I have no idea what I'm looking at here so I'll just reproduce what I've seen. I ran it to cursor at inputfile.open and it produced the segmentation fault there. I've tried to recompile it under Ubuntu as a completely new project with no attached files and just a main. It produced the same fault. The stack trace is as follows: #0 0x4010af ?? () (??:??) #1 0x7ffff7215ea5 __libc_start_main() (/lib/x86_64-linux-gnu/libc.so.6:??) #2 0x4017f1 ?? () (??:??) I don't understand a word of that. Any ideas? (EDIT: I'll put the stack trace in the main body of the text) – Rampaging Albatross Sep 12 '13 at 16:19
  • **(1)** You are not checking if files are successfully opened. `ifstream::opened` does not have a return value, but if the function fails to open a file, the `failbit` state flag is set for the stream (which may throw `ios_base::failure` if that state flag was registered using member exceptions). You should check that. See [ifstream::open](http://www.cplusplus.com/reference/fstream/ifstream/open/). For your check, see also [ios::good](http://www.cplusplus.com/reference/ios/ios/good/). – Daniel Daranas Sep 12 '13 at 16:23
  • 2
    **(2)** Please, always initialize your variables. I feel pain when I read things such as `int xi`; or `int pi;`. – Daniel Daranas Sep 12 '13 at 16:24
  • **(3)** Add more `cout` statements (even if they are useless in the normal code flow and you want get rid of them afterwards), just for the purpose of better locating the crash. For especially hard to locate crashes, I have sometimes added a `cout` statement after _every_ line. – Daniel Daranas Sep 12 '13 at 16:28
  • Your file system issue on Windows might be resolved by not hard coding the file paths. Try "fi020000.dat" instead of "/home/Nick/fi020000.dat". This change will require the file to be in the current working directory when the program runs. – Adam Burry Sep 12 '13 at 16:31
  • It was a debug build, yeah. It was only for my own use and for interpreting one specific type of data file. I'll updated the initial post with the Valgrind stuff. I don't understand that either. EDIT: I tried not hard coding the file paths (as you said, working directory) and it completely ignored the files. – Rampaging Albatross Sep 12 '13 at 16:32
  • @NicholasMillington, in that case the current working directory was not what you thought it was. Were you running from command-line or IDE? – Adam Burry Sep 12 '13 at 16:40
  • **(4)** See also the question [C++ file operations cause “crash” on embedded Linux](http://stackoverflow.com/q/5416934/96780) and its [accepted answer](http://stackoverflow.com/a/5417514/96780). – Daniel Daranas Sep 12 '13 at 16:43
  • I ran it from IDE so the working directory could have been anything, you're right. I'll try from command line and see if it works then. – Rampaging Albatross Sep 12 '13 at 16:52
  • It crashed under Windows too. Could it be that I just lucked upon a compiler that would actually make it worK? I think SciLinux had most of them so I could have picked any one of about a dozen. By the way, I appreciate the help. I just struggle to understand some of it. – Rampaging Albatross Sep 12 '13 at 16:58
  • 1
    Could you possibly update your question with a copy of the text from a console session and also tell us how many samples are in your data file? – Adam Burry Sep 12 '13 at 17:12
  • Done. Also added previous and current compiler information – Rampaging Albatross Sep 12 '13 at 17:23
  • 1
    Do you have a small dataset that you could test with? – Adam Burry Sep 12 '13 at 17:26
  • I can try and make one. Will take about half an hour though. I never really bothered making them since until yesterday it was working fine. – Rampaging Albatross Sep 12 '13 at 17:27
  • Should be easy to create a small dataset for functional testing purposes. No need to subsample or anything like that. Just `head fi020000.dat > test.dat` and fix up the first 2 lines should suffice. I am asking because I think your issue is the arrays you are creating on the stack. – Adam Burry Sep 12 '13 at 17:32
  • I stripped everything down and produced a 15,000 particle data set which ran perfectly, as normal. So the smaller file size worked straight away. How do I fix this for larger files? – Rampaging Albatross Sep 12 '13 at 17:35
  • @NicholasMillington, sth solved the issue in his answer below. Rather than allocating such large native arrays on the stack you need to use the heap. The easiest way is by using std::vector. – Adam Burry Sep 12 '13 at 17:40
  • Thank you very much, all of you. I'll read up on vectors and fix it tomorrow :) – Rampaging Albatross Sep 12 '13 at 17:42
  • 139 is 128 + 11. 128 is from the shell : `The return value of a simple command is its exit status, or 128+n if the command is terminated by signal n.` (from `man bash`). So 11 is the signal which is `SIGSEGV` (from `man 7 signal`) – Julien Palard Sep 03 '19 at 12:48

4 Answers4

6

The big hint here is that valgrind thinks you're switching threads -- and certainly from the code you've shown, you're not using multiple threads. Since every thread in a multithreaded program has its own stack, valgrind assumes that if the stack pointer changes by more than a certain threshold (see --max-stackframe as mentioned in the valgrind output) that you're switching to a different thread.

In reality, what's happening is you've created a HUGE stack frame, 9603240 bytes to be exact. This exceeds the 8MB or so that you probably are getting by default. You can see the current soft limit by looking in a shell:

$ ulimit -s
8192

In other words, the stack is limited to 8MB. If you exceed that limit, Linux will assume something bad(tm) happened and terminate your process.

One immediate fix you can use is to raise the limit, or just set it to unlimited:

$ ulimit -s 16384 # 16MB stack
$ ulimit -s unlimited # "unlimited" stack

This should stop your process from crashing, and explains why it worked fine on one box and not another (the one where it worked probably had a higher stack size limit set by default).

Now, from a design perspective, it's usually a bad idea to create stack frames this large. For large allocations, you should use heap memory, so something like:

double data[records];

could be replaced by

double *data = new double[records];

// use data ...

delete[] data;

which allocates and frees memory from the heap instead. This will allow you to avoid such problems in the first place. Or, you can always use one of the standard containers like std::vector<>` instead to avoid the issue.

FatalError
  • 52,695
  • 14
  • 99
  • 116
  • Thank you very much - I haven't been able to rewrite the program into vector form yet (will do so at a later date) so this immediate fix was very useful. – Rampaging Albatross Sep 18 '13 at 15:17
  • I implemented XDEBUG on HHVM and kept having this core dump error on my Linux Mint box when I engaged the debugger. Thank you!!! This setting change fixed the problem. I was not looking forward to hunting the bug down in the massive HHVM code base. – RyanNerd Jul 03 '15 at 15:02
2

If you see this problem only for large input sizes, chances are you get a stack overflow when creating phaseSpace, since that might be a quite big array. If you use a std::vector instead of the plain array, such a problem should be avoided:

#include <vector>

// ...

// A vector containing vectors containing integers.
// Initialized to contain the appropriate amount of space filled with zeros
std::vector< std::vector<int> > phaseSpace(pip, std::vector<int>(xip, 0));

The rest of your code probably works just the same with the vector.


Alternatively, if xni and pni go out of bounds, you will overwrite random memory. You could add output statements to show these values and see if something looks wrong.

sth
  • 222,467
  • 53
  • 283
  • 367
  • So that would explain why it sometimes used to return a -1 and crash for very large array sizes. It's worth including that anyway because the higher the array size the better the resolution of the contour diagram. Unfortunately I think this crash occurs before hand but I'll change this anyway since it'll be useful. Thanks :) – Rampaging Albatross Sep 12 '13 at 16:51
  • @NicholasMillington, you should also use a std::vector for your data array. std::vector allocates on the heap, whereas the arrays you are currently using allocate on the stack. There is more heap than stack. Also, you should specify the capacity of the vectors when you create them to avoid costly resize operations. – Adam Burry Sep 12 '13 at 17:24
  • Thanks very much. I'll sort this out tomorrow morning. – Rampaging Albatross Sep 12 '13 at 17:43
0

I am surprised this program even compiles: you are declaring arrays of size that cannot be determined during compile time, such as below. Could you tell which compiler you are using?

int records = dataformat[1] + 2;
double data[records];
Michael
  • 5,775
  • 2
  • 34
  • 53
0

This is what it might look like using std::vector instead of native arrays.

// This program converts the column output of a 1D PIC code into a
// workable solution

#include <cmath>
#include <fstream>
#include <iostream>
#include <limits>
#include <vector>

double calculateXMaximum(const std::vector<double>& arraymax) {
  double maximum = -std::numeric_limits<double>::max();
  for (unsigned k = 1; k < arraymax.size()/2; ++k) {
    maximum = std::max(arraymax[2*k], maximum);
  }
  return maximum;
}

double calculateXMinimum(const std::vector<double>& arraymin) {
  double minimum = std::numeric_limits<double>::max();
  for (unsigned k = 1; k < arraymin.size()/2; ++k) {
    minimum = std::min(arraymin[2*k], minimum);
  }
  return minimum;
}

double calculatePXMaximum(const std::vector<double>& arraymax) {
  double maximum = -std::numeric_limits<double>::max();
  for (unsigned k = 1; k < arraymax.size()/2; ++k) {
    maximum = std::max(arraymax[2*k+1], maximum);
  }
  return maximum;
}

double calculatePXMinimum(const std::vector<double>& arraymin) {
  double minimum = std::numeric_limits<double>::max();
  for (unsigned k = 1; k < arraymin.size()/2; ++k) {
    minimum = std::min(arraymin[2*k+1], minimum);
  }
  return minimum;
}

int main()
{
  // Variables settable before running program
  //  - will set up initialisation later.

  int xni = 0;
  double xntemp = 0;
  int xi; // X interpolates, defined from console for resolution of diagram

  int pnj = 0;
  double pntemp = 0;
  int pi;
  int type;

  // Determines momentum modifier!

  std::cout << "For particle type, enter 1 (e-) or 2 (p+)\n";
  std::cout << "Particle type:  ";
  std::cin >> type;

  const double modifier = (type == 2) ? 1836.0 : 1.0;

  std::ifstream inputFile("fi020000.dat");
  std::ofstream outputFile("fi20k.dat");

  int dataformat[2];
  inputFile >> dataformat[0];
  inputFile >> dataformat[1];

  int records = dataformat[1] + 2;
  std::vector<double> data(records, 0.0);
  std::cout << "Number of particles: " << dataformat[1]/2 << std::endl;


  // Introduction of data from input data file loop.  Produces records.
  for (int count = 0; count < records; ++count) {
    inputFile >> data[count];
  }

  // Calling functions for xmin and xmax.  May streamline later

  const double xmax = calculateXMaximum(data) * 1.1;
  std::cout << "Maximum x value: " << xmax << std::endl;
  const double xmin = calculateXMinimum(data) * 1.1;
  std::cout << "Minimum x value: " << xmin << std::endl;
  const double pmax = calculatePXMaximum(data) * 1.1 / modifier;
  std::cout << "Maximum p value: " << pmax << std::endl;
  const double pmin = calculatePXMinimum(data) * 1.1 / modifier;
  std::cout << "Minimum p value: " << pmin << std::endl;

  // Definition of bin size

  std::cout << "Entire desired number of x bins: ";
  std::cin >> xi;
  const int xip = xi;
  std::cout << "Enter desired number of p bins: ";
  std::cin >> pi;
  const int pip = pi;
  std::cout << "Grid is " << xip << " x " << pip << std::endl;

  // Calculate DELTA X and DELTA P
  const double deltax = (xmax - xmin)/(xip);
  const double deltap = (pmax - pmin)/(pip);

  std::cout << "Resolution of x:  " << deltax << std::endl;
  std::cout << "Resolution of p:  " << deltap << std::endl;

  std::vector< std::vector<int> > phaseSpace(xip, std::vector<int>(pip, 0));

  for (int phasecount=1; phasecount < (records/2)-1; ++phasecount) {
    xntemp = (data[2*phasecount] - xmin)/deltax;
    xni = std::floor(xntemp);
    pntemp = ((data[(2*phasecount)+1] / modifier) - pmin)/deltap;
    pnj = std::floor(pntemp);
    phaseSpace[xni][pnj] = phaseSpace[xni][pnj] + 1;
  }

  for (int xoutcount = 0; xoutcount < xip; ++xoutcount) {
    for (int poutcount = 0; poutcount < pip; ++poutcount) {
      outputFile << xmin+((xoutcount+0.5)*deltax) << " " << pmin+((poutcount+0.5)*deltap) << " " << phaseSpace[xoutcount][poutcount] << "\n";
    }
    outputFile << "\n";
  }

  std::cout << "Program complete" << std::endl;

  return 0;
}

I removed some of the std::endl calls and replaced them with "\n" to avoid unnecessary I/O buffer flushing.

I made a slight change to the way your min's and max's are calculated.

I moved a few variable declarations and made them const where possible.

Important: You should be checking the state of your ifstream as you are reading input. As it stands, you could read past the end of the file and never know it. There also appears to be a misunderstanding of the contents of your data array. It seems as though you think it contains your two leading descriptive lines. However you have already pulled those two numbers from the stream into dataformat, so you do not need to account for them.

A very small set of hand verified test input and output data would be useful.

Adam Burry
  • 1,904
  • 13
  • 20