Prevent channel buffer allocation larger than memory
[lttng-tools.git] / src / common / utils.c
index 08139e5e2cb23d691c92c6800cef841e2362bc57..4c000e9b400864591a5bbd7715c2db7e3a1aef26 100644 (file)
@@ -33,6 +33,7 @@
 #include <unistd.h>
 
 #include <common/common.h>
+#include <common/readwrite.h>
 #include <common/runas.h>
 #include <common/compat/getenv.h>
 #include <common/compat/string.h>
 #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);
 }
This page took 0.026097 seconds and 5 git commands to generate.