Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2013-01

Weird output from fpGetErrNo

9 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Ewald

Hello,

I was just writing a little mmapped (that's two `m`'s -- no typo ;-) ) file stream and thought to do it properly just in case I might fork(). So I thought to give some advise about memory:

    madvise(daBuffer, FileSize, MADV_DONTFORK);

Now if I check the result value and, if 0, print the detailed error code using:

    ErrorCode:= fpGetErrNo();
    WriteMsg(strError(ErrorCode));

I get the most ambiguous results (ENOTTY, ENOENT, which are not even documented as being possible error comditions after this call).

After some research I decided to try implement fpGetErrNo myself using (straight from <errno.h> -- using linux 3.7.4 running x86_64 hardware):

    Function __errno_location: pcint; cdecl; external 'c' name
    '__errno_location';
    Function fpGetErrNo: cint;
    Begin
        Result:= __errno_location()^;
    End;

And this gave me the expected results (Out of memory, Success, ...).

So, did I discover a bug or do I miss something?

Just for the record: `daBuffer` is just mmapped in the line preceding this one, there is no error there, it contains the right data, `Filesize` is obtained through stat(), so no issue there either.

Ewald

Reply by email

Marco van de Voort
to Ewald

In our previous episode, Ewald said:

I was just writing a little mmapped (that's two `m`'s -- no typo ;-) ) file stream and thought to do it properly just in case I might fork(). So I thought to give some advise about memory:

Why not simply use fpgetcerrnp in unit initc?

I get the most ambiguous results (ENOTTY, ENOENT, which are not even documented as being possible error comditions after this call).

What do you see if you strace the resulting binary?

Reply by email

Ewald
to Marco van de Voort

Once upon a time, on 01/30/2013 08:17 PM to be precise, Marco van de Voort said:

Show quoted text 5 lines
In our previous episode, Ewald said: > I was just writing a little mmapped (that's two `m`'s -- no typo ;-) ) > file stream and thought to do it properly just in case I might fork(). > So I thought to give some advise about memory: Why not simply use fpgetcerrnp in unit initc?

I didn't know it existed.

> I get the most ambiguous results (ENOTTY, ENOENT, which are not even > documented as being possible error comditions after this call). What do you see if you strace the resulting binary?

When explicitely triggering an error: (madvise(0, 4096)) madvise(0, 4096, 0xa /* MADV_??? */) = -1 ENOMEM (Cannot allocate memory)

And when using madvise() correctly:
madvise(0x7f5352248000, 31160801, 0xa /* MADV_??? */) = 0

Native fpGetErrNo gives ENOENT / ENOTTY respectively, while my little experiment gives me ENOMEM / 0 (success) respectively.

fpgetcerrno from initc gives me the correct results as well; and by looking at the code I see it implements it by using `__errno_location` under linux, so no surprise there.

So, what's supposed to be the difference between fpgetcerrno and fpgeterrno (multithreading? [*])?

[*] Maybe not important, but this is a rather heavily multi-threaded application.

Ewald

Reply by email

Marco van de Voort
to Ewald

In our previous episode, Ewald said:

fpgetcerrno from initc gives me the correct results as well; and by looking at the code I see it implements it by using `__errno_location` under linux, so no surprise there.

Well, the surprise is that initc worked, and yours not. From a quick glance I believe it to be correct too.

So, what's supposed to be the difference between fpgetcerrno and fpgeterrno (multithreading? [*])?

Platform dependent. They can be the same. FPC does its own kernel calls on some platforms, and the result is then stored in fperrno.

fpcerrno is always linking to libc's errno.

On platforms where FPC uses libc to acces the kernel, errno=cerrno.

[*] Maybe not important, but this is a rather heavily multi-threaded application.

Both are threadsafe, even if you use the pseudo variable variant.

Reply by email

Ewald
to Marco van de Voort
Marco van de Voort wrote 7 lines
In our previous episode, Ewald said: > fpgetcerrno from initc gives me the correct results as well; and by > looking at the code I see it implements it by using `__errno_location` > under linux, so no surprise there. Well, the surprise is that initc worked, and yours not. From a quick glance I believe it to be correct too.

I believe there is a bit of confusion: my little experiment and the fpgetcerrno both boil down to exactly the same code (both using __errno_location under linux). The reason I wrote it is that I didn't know it was already implemented this way in initc. Both functions obviously work.
The function that seemingly doesn't work is the native fpGetErrNo (with native I mean the function that is available in unit baseunix without any modification).

Show quoted text 6 lines
> So, what's supposed to be the difference between fpgetcerrno and > fpgeterrno (multithreading? [*])? Platform dependent. They can be the same. FPC does its own kernel calls on some platforms, and the result is then stored in fperrno.

That's handy, so it would be advisory to use fperrno as much as possible then.

fpcerrno is always linking to libc's errno. On platforms where FPC uses libc to acces the kernel, errno=cerrno.

Thus on linux fpc does its own kernel access then, since errno cerrno?

So, on this linux, after calling a syscall which I defined as `external 'c'` (like the madvise in the original post) it makes no sense to call fpGetErrno, and instead I should call fpgetCerrno to get sensible results?

If the above is right, than errno (without the `C`) contains a leftover error from somewhere before in the program. What bothers me is: what (and why) created the contition? (I know it is not some garbage data in the variable, since it changes over time to another error: ENOTTY --> ENOENT)

Ewald

Reply by email

Marco van de Voort
to Ewald

In our previous episode, Ewald said:

> Well, the surprise is that initc worked, and yours not. From a quick glance > I believe it to be correct too. I believe there is a bit of confusion:

Ok, cler.

The function that seemingly doesn't work is the native fpGetErrNo (with native I mean the function that is available in unit baseunix without any modification).

That only works for routines that call the FPC syscall.do_syscall function directly or indirectly.

Just like cerrno works for functions that call libc functions directly or indirectly.

That's handy, so it would be advisory to use fperrno as much as possible then.

Use fperrno for FPC predeclared functions. That works.

Show quoted text 5 lines
> fpcerrno is always linking to libc's errno. > > On platforms where FPC uses libc to acces the kernel, errno=cerrno. Thus on linux fpc does its own kernel access then, since errno cerrno?

Yes. Free/Open/NetBSD too. The other *nixes (Haiku,Solaris, Darwin/OSX) fall in the other category.

So, on this linux, after calling a syscall which I defined as `external 'c'`

Not a syscall, but a libc call. Even though it might call a syscall internally.

On most *nixes, the kernel returns the error result in some register. The wrapper code is responsible for properly storing the error result in some variable (and nowdays, preferably in a threadsafe manner)

So libc syscll wrapper code stores in the "c" errno, and routines that call the FPC internal syscall wrapper store it in the RTL errno variable.

(like the madvise in the original post) it makes no sense to call fpGetErrno, and instead I should call fpgetCerrno to get sensible results?

Correct. Or write madvise out as a true syscall, calling syscall.do_syscall (if there is any). Not all libc calls are direct syscalls.

If the above is right, than errno (without the `C`) contains a leftover error from somewhere before in the program.

All errno's are not cleared in any way, but should be checked _only_ if a function returns a value that indicates so (usually -1 but sometimes null)

What bothers me is: what (and why) created the contition?

You can't know. Any library call might call several other functions that update errno internally. Schematically, it could be like this:

function somelibccall(aparam:integer):cint;
begin
  if aparam=0 then
    exit(somefunc(aparam))
  else
    begin
      if aparam>10 then
        begin
          errno:=einval;
          exit(-1);
        end;
      result:=somefunc2(aparam);
      if result=-1 then
       begin
         result:=somefunc3(aparam+1);
         if (result=-1) and (errno=einsomething) then
             seterrno(einsomethingelse);
    end;
end;

All three somefuncs might write errno, and somelibccall simply passes the result of those calls on (and leaves their error unmodified, except in the last case). Moreover, it can write errno from nowwhere.

In such cases it is impossible to tell what of these cases happened from the result of somelibccall and errno.

Reply by email

Tomas Hajny
to Ewald: I believe there is a bit of confusion: my little experiment and the fpgetcerrno both boil…
Ewald wrote
On 30 Jan 2013, at 21:52, Marco van de Voort wrote: > In our previous episode, Ewald said:

.
.

Show quoted text 9 lines
> fpcerrno is always linking to libc's errno. > > On platforms where FPC uses libc to acces the kernel, errno=cerrno. Thus on linux fpc does its own kernel access then, since errno cerrno? So, on this linux, after calling a syscall which I defined as `external 'c'` (like the madvise in the original post) it makes no sense to call fpGetErrno, and instead I should call fpgetCerrno to get sensible results?

Correct.

If the above is right, than errno (without the `C`) contains a leftover error from somewhere before in the program. What bothers me is: what (and why) created the contition? (I know it is not some garbage data in the variable, since it changes over time to another error: ENOTTY --> ENOENT)

I don't think you need to worry too much. It's perfectly normal that certain functions used within RTL, etc., may end up in a failure code. Sometimes there's no other way for testing certain things than simply trying them and checking the result afterwards (and this may happen within the RTL and RTL itself needs to evaluate what should be cinsidered an error and what not).

This is one of disadvantages of this way (one common function for checking the last error) of returning error codes, BTW - if API functions return detailed error code directly, no such leftovers may happen. Obviously, there are some advantages too. ;-)

Tomas

Reply by email

Ewald
to Marco van de Voort: In our previous episode, Ewald said: Ok, cler. That only works for routines that call the…

Once upon a time, on 01/30/2013 11:34 PM to be precise, Marco van de Voort said:

Show quoted text 9 lines
> The function that seemingly doesn't work is the native > fpGetErrNo (with native I mean the function that is available in unit > baseunix without any modification). That only works for routines that call the FPC syscall.do_syscall function directly or indirectly. Just like cerrno works for functions that call libc functions directly or indirectly.

Right, I understand.

> So, on this linux, after calling a syscall which I defined as `external > 'c'` Not a syscall, but a libc call. Even though it might call a syscall internally.

Yes, of course.

> (like the madvise in the original post) it makes no sense to call > fpGetErrno, and instead I should call fpgetCerrno to get sensible results? Correct. Or write madvise out as a true syscall, calling syscall.do_syscall (if there is any). Not all libc calls are direct syscalls.

That won't be necessary I think, the `external 'c'` approach (which thus wraps the syscall up in a c function) is good enough for me. The only problem I had had to do with the errno, but that was due to me using the wrong getErrNo function.

> If the above is right, than errno (without the `C`) contains a leftover > error from somewhere before in the program. All errno's are not cleared in any way, but should be checked _only_ if a function returns a value that indicates so (usually -1 but sometimes null)

That indeed sounds obvious.

Show quoted text 12 lines
> What bothers me is: what (and why) created the contition? You can't know. Any library call might call several other functions that update errno internally. Schematically, it could be like this: [snip] All three somefuncs might write errno, and somelibccall simply passes the result of those calls on (and leaves their error unmodified, except in the last case). Moreover, it can write errno from nowwhere. In such cases it is impossible to tell what of these cases happened from the result of somelibccall and errno.

I understand, so there probably is no issue in preceding code then, which means that I can hereby cross out this issue on my todo-list :-)

Thanks for the help!

Ewald

Reply by email

Ewald
to Tomas Hajny: . . Correct. I don't think you need to worry too much. It's perfectly normal that certain…

Once upon a time, on 01/30/2013 11:38 PM to be precise, Tomas Hajny said:

Show quoted text 10 lines
> If the above is right, than errno (without the `C`) contains a leftover > error from somewhere before in the program. What bothers me is: what (and > why) created the contition? (I know it is not some garbage data in the > variable, since it changes over time to another error: ENOTTY --> ENOENT) I don't think you need to worry too much. It's perfectly normal that certain functions used within RTL, etc., may end up in a failure code. Sometimes there's no other way for testing certain things than simply trying them and checking the result afterwards (and this may happen within the RTL and RTL itself needs to evaluate what should be cinsidered an error and what not).

This had actually occured to me, it wouldn't surprise me for example that after calling `FileExists(<a nonexistant filename>)` you get ENOENT (No Such File Or Directory) in errno as a leftover from FileExists, which has interpreted this code and so decided to return false. In this case ENOENT no longer has any meaning (or use for that matter). But then there is ENOTTY. "Where could it come from" was what I was wondering about, but as you and Marco say, I shouldn't worry too much about it.

And, since the program works flawlessly, it would be useless to go out on a witchhunt chasing errors which most probably aren't , thanks for the explanation!

Ewald

Reply by email