segmentation fault sometimes

K

Ken

Hi,

I have a little program running on Linux to draw a status bar with
framebuffer, but it has segment fault after it is run several times,
first time is ok usually, 2nd, 3rd times it produces segmentation
fault core dump, it is caused by the line below, could anyone help how
to fix the issue?

*(fbp + location) = 255; /* has segmentation fault sometimes here */

---------------------------------------------------------------
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <stdlib.h>

inline static unsigned short int make16color(unsigned char r, unsigned
char g, unsigned char b)
{
return (
(((r >> 3) & 31) << 11) |
(((g >> 2) & 63) << 5) |
((b >> 3) & 31) );
}

int main(void) {
int fbfd = 0;
struct fb_var_screeninfo vinfo =
{ .xres=0, .yres=0, .bits_per_pixel=0,
.xoffset=0, .yoffset=0 };
struct fb_fix_screeninfo finfo = { .line_length=0 };
long int screensize = 0;
char *fbp = 0;
unsigned x = 0, y = 0;
int guage_height = 20, step = 10;
unsigned int location = 0;

// Open the file for reading and writing
fbfd = open("/dev/fb0", O_RDWR);
if (!fbfd) {
printf("Error: cannot open framebuffer device.\n");
exit(1);
}
printf("The framebuffer device was opened successfully.\n");

// Get fixed screen information
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
printf("Error reading fixed information.\n");
exit(2);
}

// Get variable screen information
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
printf("Error reading variable information.\n");
exit(3);
}
// Figure out the size of the screen in bytes
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

// Map the device to memory
fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE,
MAP_SHARED,
fbfd, 0);

if ((int)fbp == -1) {
printf("Error: failed to map framebuffer device to memory.
\n");
exit(4);
}
printf("The framebuffer device was mapped to memory successfully.
\n");

//set to black color first
memset(fbp, 0, screensize);
//draw rectangle
y = (vinfo.yres - guage_height) / 2 - 2; // Where we are
going to put the pixel

for (x = step - 2; x < vinfo.xres - step + 2; x++) {
location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
(y+vinfo.yoffset) * finfo.line_length;
*(fbp + location) = 255; /* has segmentation fault sometimes
here */
}

y = (vinfo.yres + guage_height) / 2 + 2; // Where we are
going to put the pixel
for (x = step - 2; x < vinfo.xres - step + 2; x++) {
location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
(y+vinfo.yoffset) * finfo.line_length;
*(fbp + location) = 255;
}

x = step - 2;
for (y = (vinfo.yres - guage_height) / 2 - 2; y < (vinfo.yres +
guage_height) / 2 + 2; y++) {
location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
(y+vinfo.yoffset) * finfo.line_length;

*(fbp + location) = 255;
}

x = vinfo.xres - step + 2;
for (y = (vinfo.yres - guage_height) / 2 - 2; y < (vinfo.yres +
guage_height) / 2 + 2; y++) {
location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
(y+vinfo.yoffset) * finfo.line_length;

*(fbp + location) = 255;
}

// Figure out where in memory to put the pixel
for ( x = step; x < vinfo.xres - step; x++ ) {
for ( y = (vinfo.yres - guage_height) / 2; y < (vinfo.yres +
guage_height) / 2; y++ ) {
location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
(y+vinfo.yoffset) * finfo.line_length;

if ( vinfo.bits_per_pixel == 32 ) {
*(fbp + location) = 100; // Some blue
*(fbp + location + 1) = 15+(x-100)/2; // A little
green
*(fbp + location + 2) = 200-(y-100)/5; // A lot of
red
*(fbp + location + 3) = 0; // No transparency
} else { //assume 16bpp
unsigned char b = 255 * x / (vinfo.xres - step);
unsigned char g = 255; // (x - 100)/6 A little
green
unsigned char r = 255; // A lot of red
unsigned short int t = make16color(r, g, b);
*((unsigned short int*)(fbp + location)) = t;
}
}
usleep(200);
}
//clean framebuffer
munmap(fbp, screensize);
close(fbfd);

return 0;
}
---------------------------------------------------------------
 
K

Kenny McCormack

Hi,

I have a little program running on Linux to draw a status bar with
framebuffer, but it has segment fault after it is run several times,
first time is ok usually, 2nd, 3rd times it produces segmentation
fault core dump, it is caused by the line below, could anyone help how
to fix the issue?

The fact is, nobody here gives a shit about you or your problems.

You may find some or all of the following links helpful in understanding
why this is so:

http://en.wikipedia.org/wiki/Aspergers
http://en.wikipedia.org/wiki/Clique
http://en.wikipedia.org/wiki/C_programming_language
 
D

David Resnick

Hi,

I have a little program running on Linux to draw a status bar with
framebuffer, but it has segment fault after it is run several times,
first time is ok usually, 2nd, 3rd times it produces segmentation
fault core dump, it is caused by the line below, could anyone help how
to fix the issue?

A few suggestions:
1) Valgrind is often very helpful for debugging memory corruption
issues on linux. This can in general just tell you what is wrong.
I'm not sure however it is as good with mmap'd memory as it is with
*alloc memory.

2) You could add either printfs or run under the debugger depending on
your tastes for such. Displaying by some means the various fbp and
location values prior to de-referencing will probably help you. Maybe
location is unexpectedly calculated to a bigger size than screensize?

-David
 
K

Ken

In <[email protected]>,
I have a little program running on Linux to draw a status bar with
framebuffer, but it has segment fault after it is run several times,
first time is ok usually, 2nd, 3rd times it produces segmentation
fault core dump, it is caused by the line below, could anyone help
how to fix the issue?
*(fbp + location) = 255; /* has segmentation fault sometimes here */

Idiomatically, this can be written as fbp[location] = 255, and this

I changed it to fbp[location] = 255, thanks.
may give you a clue as to what is probably going wrong. The fbp
pointer represents an address in memory. It is of type char *, so any
addition to it (say, + n) results in another address, n bytes further
on in memory. If your program "owns" all the addresses in between,
you're fine. You're not fine, and therefore we may conclude that you
don't own all those addresses. The obvious candidate is location,
which is calculated immediately prior to your segfault.

  location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
                 (y+vinfo.yoffset) * finfo.line_length;

In your position, I would start off my debugging efforts by writing
some trace code that dumps values like x, vinfo.xoffset,
vinfo.bits_per_pixel/8, etc etc, to a file, all neatly labelled so
that you know what they are - and make sure the file is fflushed
before you move to the sometimes-segfaulting line. I think you'll
discover on examining the file that the result calculated and
assigned to location is screwed (either completely way out and
obvious, or perhaps more subtle, maybe just an off-by-one error in
one or more of the inputs to that calculation).

These values are fixed to a specific video card.
screensize=307200, vinfo.bits_per_pixel/8= 2, finfo.line_length=960,
vinfo.xres = 480,
when it is normal and there is no segment fault error, the location
is : min=142096 max=143022,
when segment fault happens, the following values are,

(x,y), (vinfo.xoffset,vinfo.yoffset), location
(8, 148), (0, 320), 449296

I still don't figure out the root cause till now why it access memory
out of the program.
 
A

Alan Curry

These values are fixed to a specific video card.
screensize=307200, vinfo.bits_per_pixel/8= 2, finfo.line_length=960,
vinfo.xres = 480,
when it is normal and there is no segment fault error, the location
is : min=142096 max=143022,
when segment fault happens, the following values are,

(x,y), (vinfo.xoffset,vinfo.yoffset), location
(8, 148), (0, 320), 449296

I still don't figure out the root cause till now why it access memory
out of the program.

It looks like the yoffset is causing your trouble. Before going into detail,
a preliminary question about the code.

Given an (x,y) coordinate pair, you have used this formula to calculate an
offset into the fb memory:

location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
(y+vinfo.yoffset) * finfo.line_length;

Did you write that code yourself or copy it from somewhere? Can you explain
what all the terms mean? I suspect you copied it and haven't actually found
out what the xoffset and yoffset are for.

I didn't know what they were for either, a few minutes ago. Documentation on
the fb ioctls is too hard to find. But I think I've got it figured out:
xoffset and yoffset are the offset from beginning of fb memory to the first
visible pixel.

You've got a lot more video RAM than you'd need to store a screenful of
pixels at this resolution (by the way, 480 pixels horizontal? what a weird
number). The fb driver is providing you a virtual screen that is bigger than
the physical screen. By changing xoffset and yoffset, you can make the screen
scroll smoothly, revealing previously hidden pieces of the virtual screen.

Your location = ... calculation adds xoffset and yoffset to translate the
(x,y) coordinates (taken as relative to the physical screen origin) into a
byte offset (relative to the beginning of fb memory). So the byte offset can
be anywhere within the virtual screen, but the size of your mmap was based on
the physical resolution. You didn't mmap the entire virtual screen.

You should use xres_virtual and yres_virtual when calculating the amount of
memory to map. That would be

screensize =
vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;

But there's still a bug there. Video modes can have unused padding bytes at
the end of each row of pixels, for alignment purposes. The correct way to
convert "number of rows" to "number of bytes" is to multiply by
finfo.line_length (which you already did in the "location" formula, adding to
my suspicion that you copied that code without understanding it.).

line_length, also called the "stride" of the video mode, will be equal to
vinfo.xres_virtual * vinfo.bits_per_pixel/8 in the normal no-padding case.

The correct mmap size for all cases should be:

screensize = vinfo.yres_virtual * finfo.line_length;

If I've got all that right, there's still an unanswered question - what's
causing yoffset to change between (or during) runs of your program? But maybe
you won't need to answer that.
 
K

Ken

It looks like the yoffset is causing your trouble. Before going into detail,
a preliminary question about the code.

Given an (x,y) coordinate pair, you have used this formula to calculate an
offset into the fb memory:

location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
(y+vinfo.yoffset) * finfo.line_length;

Did you write that code yourself or copy it from somewhere? Can you explain
what all the terms mean? I suspect you copied it and haven't actually found
out what the xoffset and yoffset are for.

I didn't know what they were for either, a few minutes ago. Documentation on
the fb ioctls is too hard to find. But I think I've got it figured out:
xoffset and yoffset are the offset from beginning of fb memory to the first
visible pixel.

You've got a lot more video RAM than you'd need to store a screenful of
pixels at this resolution (by the way, 480 pixels horizontal? what a weird
number). The fb driver is providing you a virtual screen that is bigger than
the physical screen. By changing xoffset and yoffset, you can make the screen
scroll smoothly, revealing previously hidden pieces of the virtual screen.

Your location = ... calculation adds xoffset and yoffset to translate the
(x,y) coordinates (taken as relative to the physical screen origin) into a
byte offset (relative to the beginning of fb memory). So the byte offset can
be anywhere within the virtual screen, but the size of your mmap was based on
the physical resolution. You didn't mmap the entire virtual screen.

You should use xres_virtual and yres_virtual when calculating the amount of
memory to map. That would be

screensize =
vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;

But there's still a bug there. Video modes can have unused padding bytes at
the end of each row of pixels, for alignment purposes. The correct way to
convert "number of rows" to "number of bytes" is to multiply by
finfo.line_length (which you already did in the "location" formula, adding to
my suspicion that you copied that code without understanding it.).

Yes are right, I copied the code from some elsewhere post on Internet
and added minor change to correct bugs, I'd just play with this
program and learn from it, but the segment fault bug needs the expert
help in this group.
line_length, also called the "stride" of the video mode, will be equal to
vinfo.xres_virtual * vinfo.bits_per_pixel/8 in the normal no-padding case.

The correct mmap size for all cases should be:

screensize = vinfo.yres_virtual * finfo.line_length;

This fixed the segment fault bug here eventually, I learn from the
struct fb_fix_screeninfo actually the screen size is finfo.smem_len,
so instead of calculating it, just using smem_len is ok, I also
verified, your fixed screensize equals to the smem_len.

Another on-topic question is,
We can write *(fbp + location) = 255; as fbp[location] = 255; per the
suggestion in the thread.
how to rewrite below statement?
((unsigned short int*)(fbp+location)) = t;
If I rewrite it to fbp[location] = t, then I'll get wrong result.
If I've got all that right, there's still an unanswered question - what's
causing yoffset to change between (or during) runs of your program? But maybe
you won't need to answer that.

This is what I can not answer as you may see it, I also don't know why
it is changed between several runs, it is set to zero during
initialization in fact.
struct fb_var_screeninfo vinfo =
{ .xres=0, .yres=0, .bits_per_pixel=0,
.xoffset=0, .yoffset=0 };
 
A

Alan Curry

Another on-topic question is,
We can write *(fbp + location) = 255; as fbp[location] = 255; per the
suggestion in the thread.
how to rewrite below statement?
((unsigned short int*)(fbp+location)) = t;
If I rewrite it to fbp[location] = t, then I'll get wrong result.

That won't easily translate to array syntax because of the mixed pointer
types. You have a pointer that is mismatched to the actual underlying
hardware data format, but you have a great excuse: the actual underlying
hardware data format is not known until runtime. There's gotta be a
conversion somewhere. The cast is justified. You could hide the addition by
translating fbp+location into &fbp[location] but I wouldn't consider that an
improvement.
This is what I can not answer as you may see it, I also don't know why
it is changed between several runs, it is set to zero during
initialization in fact.
struct fb_var_screeninfo vinfo =
{ .xres=0, .yres=0, .bits_per_pixel=0,
.xoffset=0, .yoffset=0 };

Those initial values have no effect because you didn't send them to the
kernel (with a PUT ioctl). Your first GET ioctl overwrote them. All those
pretty 0's were never used.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top