foss-for-synopsys-dwc-arc-processors/linux

ARCv2: Incorrect time is returned in user space

Closed this issue · 8 comments

Consider this example (both gettimeofday and clock_gettime are used to be sure):

#include <stdio.h>
#include <time.h>
#include <sys/time.h>

int main()
{
        struct timeval tv;
        struct timespec ts;
        gettimeofday(&tv, NULL);
        printf("Time: %lu.%lu\n", tv.tv_sec, tv.tv_usec);
        clock_gettime(CLOCK_REALTIME, &ts);
        printf("Time: %lu.%lu\n", ts.tv_sec, ts.tv_nsec);
        return 0;
}

In both cases only tv_sec part is returned and nano-part is always 0:

$ ./time.elf
Time: 4.0
Time: 4.0

The issue is observed on QEMU, nSIM and HSDK, both with RTC clock and without it. Both functions work correctly on ARCv3 targets.

The issue occurs on Linux systems with glibc only. uClibc is not affected. @pavelvkozlov Can you please look into it? Seems like it's not critical.

It's not an issue, glibc uses default TIMESIZE 64 definition and we don't redefine it for our 32-bit ACRv2 and ARCv3-32. time_t is 64-bit. Use correct printf format to get correct values with glibc.

@pavelvkozlov According to glibc manual:

Data Type: struct timespec
struct timespec represents a simple calendar time, or an elapsed time, with sub-second resolution. It is declared in time.h and has the following members:

time_t tv_sec
The number of whole seconds elapsed since the epoch (for a simple calendar time) or since some other starting point (for an elapsed time).

long int tv_nsec
The number of nanoseconds elapsed since the time given by the tv_sec member.

When struct timespec values are produced by GNU C Library functions, the value in this field will always be greater than or equal to zero, and less than 1,000,000,000. When struct timespec values are supplied to GNU C Library functions, the value in this field must be in the same range.

timespec is a structure with 2 value and tv_sec stands for seconds, tv_nsec stands for nanoseconds. And nano-seconds part is missing for some reasong.

Yes, it's true, tv_nsec is long int. But the first time_t tv_sec is 64-bit and you print it as 32-bit. It's incorrect and causes to incorrect offset for the second value inside printf.

Yes, it's true, tv_nsec is long int. But the first time_t tv_sec is 64-bit and you print it as 32-bit. It's incorrect and causes to incorrect offset for the second value inside printf.

Yes, I see. However, size of time_t is different in glibc (8-byte) and uClibc (4-byte).

P.S. By the way, time_t is marked as not portable and it's recommended not to use it directly or at least to cast it to another type large enough to keep time value. If this difference between glibc and uClibc is acceptable, then this issue may be closed.

However, size of time_t is different in glibc (8-byte) and uClibc (4-byte).
...
If this difference between glibc and uClibc is acceptable, then this issue may be closed.

It can confuse but this difference is acceptable.
It is better to have 64-bit time_t due to Y2038 issue.
The uclibc-ng libc only supports time_t 32-bit (long int) for 32-bit CPUs.

@pavelvkozlov @kolerov please add the last comment here which clearly explains what was wrong with the initial assumption or use of standard API and how that problem got resolved preferably with now correct example. That will help others if they ever bump into the same issue.

time_t represents time values in seconds in C. It is not a portable type, and it may have a different implementation in different standard libraries. In case of ARC it's a 8-byte value in glibc and it's a 4-byte value in uClibc. Though it's not recommended to use time_t values directly (for example, you can use functions like difftime that always return double), sometimes you can't avoid it.

If you really need to use time_t then consider casting it to double or uintmax_t. For example, you can print such a value this way:

#include <stdio.h>
#include <stdint.h>
#include <time.h>

int main()
{
        time_t seconds = time(NULL);
        printf("Time (integer): %ju\n", (uintmax_t) seconds);
        printf("Time (double): %f\n", (double) seconds);
        return 0;
}