X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;ds=sidebyside;f=src%2Fcommon%2Futils.c;h=4c000e9b400864591a5bbd7715c2db7e3a1aef26;hb=09b72f7aa737f46196db18bcdf3bc947a08c27a2;hp=08139e5e2cb23d691c92c6800cef841e2362bc57;hpb=2daf65025e0fe5c15179dc4bfb26f875e3d31a26;p=lttng-tools.git diff --git a/src/common/utils.c b/src/common/utils.c index 08139e5e2..4c000e9b4 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,19 @@ #include "defaults.h" #include "time.h" +#define PROC_MEMINFO_PATH "/proc/meminfo" +#define PROC_MEMINFO_MEMAVAILABLE_LINE "MemAvailable:" +#define PROC_MEMINFO_MEMTOTAL_LINE "MemTotal:" + +/* The length of the longest field of `/proc/meminfo`. */ +#define PROC_MEMINFO_FIELD_MAX_NAME_LEN 20 + +#if (PROC_MEMINFO_FIELD_MAX_NAME_LEN == 20) +#define MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "19" +#else +#error MAX_NAME_LEN_SCANF_IS_A_BROKEN_API must be updated to match (PROC_MEMINFO_FIELD_MAX_NAME_LEN - 1) +#endif + /* * Return a partial realpath(3) of the path even if the full path does not * exist. For instance, with /tmp/test1/test2/test3, if test2/ does not exist @@ -1104,12 +1118,19 @@ end: /** * Parse a string that represents a time in human readable format. It - * supports decimal integers suffixed by 's', 'u', 'm', 'us', and 'ms'. + * supports decimal integers suffixed by: + * "us" for microsecond, + * "ms" for millisecond, + * "s" for second, + * "m" for minute, + * "h" for hour * * The suffix multiply the integer by: - * 'u'/'us': 1 - * 'm'/'ms': 1000 - * 's': 1000000 + * "us" : 1 + * "ms" : 1000 + * "s" : 1000000 + * "m" : 60000000 + * "h" : 3600000000 * * Note that unit-less numbers are assumed to be microseconds. * @@ -1124,7 +1145,7 @@ int utils_parse_time_suffix(char const * const str, uint64_t * const time_us) { int ret; uint64_t base_time; - long multiplier = 1; + uint64_t multiplier = 1; const char *str_end; char *num_end; @@ -1161,17 +1182,37 @@ int utils_parse_time_suffix(char const * const str, uint64_t * const time_us) /* Check if a prefix is present. */ switch (*num_end) { case 'u': - multiplier = 1; - /* Skip another letter in the 'us' case. */ - num_end += (*(num_end + 1) == 's') ? 2 : 1; + /* + * Microsecond (us) + * + * Skip the "us" if the string matches the "us" suffix, + * otherwise let the check for the end of the string handle + * the error reporting. + */ + if (*(num_end + 1) == 's') { + num_end += 2; + } break; case 'm': - multiplier = 1000; - /* Skip another letter in the 'ms' case. */ - num_end += (*(num_end + 1) == 's') ? 2 : 1; + if (*(num_end + 1) == 's') { + /* Millisecond (ms) */ + multiplier = USEC_PER_MSEC; + /* Skip the 's' */ + num_end++; + } else { + /* Minute (m) */ + multiplier = USEC_PER_MINUTE; + } + num_end++; break; case 's': - multiplier = 1000000; + /* Second */ + multiplier = USEC_PER_SEC; + num_end++; + break; + case 'h': + /* Hour */ + multiplier = USEC_PER_HOURS; num_end++; break; case '\0': @@ -1647,37 +1688,76 @@ end: return ret; } -LTTNG_HIDDEN -int timespec_to_ms(struct timespec ts, unsigned long *ms) +static +int read_proc_meminfo_field(const char *field, size_t *value) { - unsigned long res, remain_ms; + int ret; + FILE *proc_meminfo; + char name[PROC_MEMINFO_FIELD_MAX_NAME_LEN] = {}; - if (ts.tv_sec > ULONG_MAX / MSEC_PER_SEC) { - errno = EOVERFLOW; - return -1; /* multiplication overflow */ - } - res = ts.tv_sec * MSEC_PER_SEC; - remain_ms = ULONG_MAX - res; - if (ts.tv_nsec / NSEC_PER_MSEC > remain_ms) { - errno = EOVERFLOW; - return -1; /* addition overflow */ + proc_meminfo = fopen(PROC_MEMINFO_PATH, "r"); + if (!proc_meminfo) { + PERROR("Failed to fopen() " PROC_MEMINFO_PATH); + ret = -1; + goto fopen_error; + } + + /* + * Read the contents of /proc/meminfo line by line to find the right + * field. + */ + while (!feof(proc_meminfo)) { + unsigned long value_kb; + + ret = fscanf(proc_meminfo, + "%" MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "s %lu kB\n", + name, &value_kb); + if (ret == EOF) { + /* + * fscanf() returning EOF can indicate EOF or an error. + */ + if (ferror(proc_meminfo)) { + PERROR("Failed to parse " PROC_MEMINFO_PATH); + } + break; + } + + if (ret == 2 && strcmp(name, field) == 0) { + /* + * This number is displayed in kilo-bytes. Return the + * number of bytes. + */ + *value = ((size_t) value_kb) * 1024; + ret = 0; + goto found; + } } - res += ts.tv_nsec / NSEC_PER_MSEC; - *ms = res; - return 0; + /* Reached the end of the file without finding the right field. */ + ret = -1; + +found: + fclose(proc_meminfo); +fopen_error: + return ret; +} + +/* + * Returns an estimate of the number of bytes of memory available based on the + * the information in `/proc/meminfo`. The number returned by this function is + * a best guess. + */ +LTTNG_HIDDEN +int utils_get_memory_available(size_t *value) +{ + return read_proc_meminfo_field(PROC_MEMINFO_MEMAVAILABLE_LINE, value); } +/* + * Returns the total size of the memory on the system in bytes based on the + * the information in `/proc/meminfo`. + */ LTTNG_HIDDEN -struct timespec timespec_abs_diff(struct timespec t1, struct timespec t2) +int utils_get_memory_total(size_t *value) { - uint64_t ts1 = (uint64_t) t1.tv_sec * (uint64_t) NSEC_PER_SEC + - (uint64_t) t1.tv_nsec; - uint64_t ts2 = (uint64_t) t2.tv_sec * (uint64_t) NSEC_PER_SEC + - (uint64_t) t2.tv_nsec; - uint64_t diff = max(ts1, ts2) - min(ts1, ts2); - struct timespec res; - - res.tv_sec = diff / (uint64_t) NSEC_PER_SEC; - res.tv_nsec = diff % (uint64_t) NSEC_PER_SEC; - return res; + return read_proc_meminfo_field(PROC_MEMINFO_MEMTOTAL_LINE, value); }