libsystem: proc: add smaps parse api for a pid
authorWaLyong Cho <walyong.cho@samsung.com>
Thu, 10 Nov 2016 09:25:16 +0000 (18:25 +0900)
committerWaLyong Cho <walyong.cho@samsung.com>
Thu, 10 Nov 2016 10:08:32 +0000 (19:08 +0900)
Change-Id: Idae05dc3839b6cbc7051425bcc905a56b9370e17
Signed-off-by: WaLyong Cho <walyong.cho@samsung.com>
configure.ac
packaging/libsystem.spec
src/Makefile.am
src/libsystem/.gitignore
src/libsystem/proc-smaps-lookup.gperf [new file with mode: 0644]
src/libsystem/proc.c
src/libsystem/proc.h

index 49bf6cc2d14d6101b0c1c61736ddf4be5504da22..9e4ce3b73caa954b6d52071770e568998be84674 100644 (file)
@@ -46,6 +46,11 @@ AC_FUNC_MKTIME
 AC_FUNC_REALLOC
 AC_CHECK_FUNCS([getmntent gettimeofday localtime_r memset mkdir rmdir strchr strcspn strdup strndup strrchr strspn])
 
+AC_CHECK_TOOL(GPERF, gperf)
+if test -z "$GPERF" ; then
+        AC_MSG_ERROR([*** gperf not found])
+fi
+
 # ------------------------------------------------------------------------------
 PKG_CHECK_MODULES(DBUS, [dbus-1 >= 1.3.2])
 PKG_CHECK_MODULES(GLIB, [glib-2.0])
index 7fb9c257c679cde42a4768bbe2b70dcbf89854a0..9de8ad371ccbaadc5cd1faa5a715db3501f40c23 100644 (file)
@@ -15,6 +15,7 @@ BuildRequires:  libtool
 BuildRequires:  pkgconfig(dbus-1)
 BuildRequires:  pkgconfig(glib-2.0) >= 2.44
 BuildRequires:  pkgconfig(gio-2.0) >= 2.44
+BuildRequires:  gperf
 
 Requires:       /bin/cp
 
index f1092bcc4266bc42cc1b09b299e54bb00333e495..f1ca4cf901f98dfbcf754a287b66f073d8eafc1c 100644 (file)
@@ -66,8 +66,15 @@ libsystem_la_SOURCES = \
        libsystem/libsystem.c \
        libsystem/libsystem.h \
        libsystem/proc.c \
+       libsystem/proc-smaps-lookup.c \
        libsystem/time-util.c
 
+EXTRA_DIST += \
+       libsystem/proc-smaps-lookup.gperf
+
+CLEANFILES += \
+       libsystem/proc-smaps-lookup.c
+
 libsystem_la_CFLAGS = \
        $(AM_CFLAGS) \
        $(GLIB_CFLAGS)
@@ -150,4 +157,8 @@ SED_PROCESS = \
 %.pc: %.pc.in
        $(SED_PROCESS)
 
+%.c: %.gperf
+       $(AM_V_at)$(MKDIR_P) $(dir $@)
+       $(AM_V_GPERF)$(GPERF) < $< > $@
+
 install-exec-hook: $(INSTALL_EXEC_HOOKS)
index c97363e5a0f0f718f27e3ed4d08f2cec0b98ad8b..fe4ac252ed511dfee03e627a4d78b9261bb81702 100644 (file)
@@ -1 +1,2 @@
-/libsystem.pc
\ No newline at end of file
+/libsystem.pc
+/proc-smaps-lookup.c
\ No newline at end of file
diff --git a/src/libsystem/proc-smaps-lookup.gperf b/src/libsystem/proc-smaps-lookup.gperf
new file mode 100644 (file)
index 0000000..7cf82e1
--- /dev/null
@@ -0,0 +1,70 @@
+%{
+#include <assert.h>
+#include "proc.h"
+
+struct smap_mapping {
+        const char* name;
+        enum smap_id id;
+};
+typedef struct smap_mapping smap_mapping;
+
+%}
+smap_mapping;
+%language=ANSI-C
+%define slot-name name
+%define hash-function-name smap_mapping_hash
+%define lookup-function-name smap_mapping_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+AnonHugePages,  SMAPS_ID_ANON_HUGE_PAGES
+Anonymous,      SMAPS_ID_ANONYMOUS
+KernelPageSize, SMAPS_ID_KERNEL_PAGE_SIZE
+Locked,         SMAPS_ID_LOCKED
+MMUPageSize,    SMAPS_ID_MMU_PAGE_SIZE
+PSwap,          SMAPS_ID_PSWAP
+Private_Clean,  SMAPS_ID_PRIVATE_CLEAN
+Private_Dirty,  SMAPS_ID_PRIVATE_DIRTY
+Pss,            SMAPS_ID_PSS
+Referenced,     SMAPS_ID_REFERENCED
+Rss,            SMAPS_ID_RSS
+Shared_Clean,   SMAPS_ID_SHARED_CLEAN
+Shared_Dirty,   SMAPS_ID_SHARED_DIRTY
+Size,           SMAPS_ID_SIZE
+Swap,           SMAPS_ID_SWAP
+%%
+static const char* const smaps_string_lookup[SMAPS_ID_MAX] = {
+        [SMAPS_ID_ANON_HUGE_PAGES]      = "AnonHugePages",
+        [SMAPS_ID_ANONYMOUS]            = "Anonymous",
+        [SMAPS_ID_KERNEL_PAGE_SIZE]     = "KernelPageSize",
+        [SMAPS_ID_LOCKED]               = "Locked",
+        [SMAPS_ID_MMU_PAGE_SIZE]        = "MMUPageSize",
+        [SMAPS_ID_PSWAP]                = "PSwap",
+        [SMAPS_ID_PRIVATE_CLEAN]        = "Private_Clean",
+        [SMAPS_ID_PRIVATE_DIRTY]        = "Private_Dirty",
+        [SMAPS_ID_PSS]                  = "Pss",
+        [SMAPS_ID_REFERENCED]           = "Referenced",
+        [SMAPS_ID_RSS]                  = "Rss",
+        [SMAPS_ID_SHARED_CLEAN]         = "Shared_Clean",
+        [SMAPS_ID_SHARED_DIRTY]         = "Shared_Dirty",
+        [SMAPS_ID_SIZE]                 = "Size",
+        [SMAPS_ID_SWAP]                 = "Swap",
+};
+
+const char *smap_id_to_string(enum smap_id id) {
+
+        assert(id >= 0 && id < SMAPS_ID_MAX);
+
+        return smaps_string_lookup[id];
+}
+
+enum smap_id smap_string_to_id(const char *str) {
+        const struct smap_mapping *m;
+
+        assert(str);
+        m = smap_mapping_lookup(str,
+                                strlen(str));
+        return m ? m->id : SMAPS_ID_INVALID;
+}
index 0881ae8f27b16cb63962f8cd5b2c5a21cc875911..38d5b93ba5f20bb0a59d76b6052efddcfd004895 100644 (file)
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <limits.h>
 
 #include "libsystem.h"
+#include "proc.h"
 
 ssize_t proc_cmdline_get_str(char **buf, const char *op) {
         _cleanup_free_ char *cmdline = NULL;
@@ -93,3 +95,160 @@ int proc_pid_of(const char *pname) {
 
         return 0;
 }
+
+static void smap_free(struct smap *map) {
+        if (!map)
+                return;
+
+        if (map->mode)
+                free(map->mode);
+
+        if (map->name)
+                free(map->name);
+
+        free(map);
+}
+
+void smaps_free(struct smaps *maps) {
+        int i;
+
+        if (!maps)
+                return;
+
+        for (i = 0; i < maps->n_map; i++)
+                smap_free(maps->maps[i]);
+
+        free(maps->maps);
+        free(maps);
+}
+
+static int add_smap_to_smaps(struct smaps *maps, struct smap *map) {
+        int i;
+
+        assert(maps);
+        assert(map);
+
+        maps->n_map++;
+
+        maps->maps = (struct smap **)realloc(
+                maps->maps,
+                sizeof(struct smap *) * maps->n_map);
+        if (!maps->maps)
+                return -ENOMEM;
+
+        maps->maps[maps->n_map - 1] = map;
+
+        for (i = 0; i < SMAPS_ID_MAX; i++)
+                maps->sum[i] += map->value[i];
+
+        return 0;
+}
+
+int proc_pid_get_smaps(pid_t pid, struct smaps **maps, enum smap_mask mask) {
+        _cleanup_free_ char *path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        struct smaps *m = NULL;
+        char buf[LINE_MAX];
+        bool get_line = true;
+        int r;
+
+        assert(maps);
+
+        r = asprintf(&path, "/proc/%d/smaps", pid);
+        if (r < 0)
+                return -ENOMEM;
+
+        r = access(path, F_OK);
+        if (r < 0)
+                return -errno;
+
+        f = fopen(path, "re");
+        if (!f)
+                return -errno;
+
+        m = new0(struct smaps, 1);
+        if (!m)
+                return -ENOMEM;
+
+        for (;;) {
+                struct smap *map = NULL;
+                int n;
+
+                if (get_line && !fgets(buf, sizeof(buf), f)) {
+                        if (ferror(f)) {
+                                r = -errno;
+                                goto on_error;
+                        }
+                        break;
+                } else
+                        get_line = true;
+
+                map = new0(struct smap, 1);
+                if (!map) {
+                        r = -errno;
+                        goto on_error;
+                }
+
+                n = sscanf(buf, "%x-%x %ms %*s %*s %*s %ms",
+                           &map->start, &map->end, &map->mode, &map->name);
+
+                if (n == 3 && !map->name)
+                        map->name = strdup("[anon]");
+                else if (n != 4) {
+                        free(map);
+                        r = -EINVAL;
+                        goto on_error;
+                }
+
+                for (;;) {
+                        unsigned int v = 0;
+                        enum smap_id id;
+                        size_t l;
+
+                        if (!fgets(buf, sizeof(buf), f)) {
+                                if (ferror(f)) {
+                                        free(map);
+                                        r = -errno;
+                                        goto on_error;
+                                }
+                                break;
+                        }
+
+                        if ((*buf >= '0' && *buf <= '9') ||
+                            (*buf >= 'a' && *buf <= 'f')) {
+                                get_line = false;
+                                break;
+                        }
+
+                        l = strcspn(buf, ":");
+                        if (!l)
+                                break;
+
+                        buf[l] = 0;
+
+                        id = smap_string_to_id(buf);
+                        if (id < 0 || id >= SMAPS_ID_MAX)
+                                continue;
+
+                        if (!(mask & (1 << id)))
+                                continue;
+
+                        if (sscanf(buf + l + 1, "%d kB", &v) != 1)
+                                break;
+
+                        map->value[id] = v;
+                }
+
+                r = add_smap_to_smaps(m, map);
+                if (r < 0)
+                        goto on_error;
+        }
+
+        *maps = m;
+
+        return 0;
+
+on_error:
+        smaps_free(m);
+        return r;
+}
index 2938f6ad27d8d9c5833ba24e3e25ba4f4bb49769..132da3c43c5d799e2635da75961db41980cc2c0d 100644 (file)
@@ -3,7 +3,7 @@
 /*
  * libsystem
  *
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the License);
  * you may not use this file except in compliance with the License.
@@ -29,6 +29,9 @@
 
 #pragma once
 
+#include <sys/types.h>
+#include "libsystem.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -68,6 +71,162 @@ ssize_t proc_cmdline_get_str(char **buf, const char *op);
  * -errno is returned on failure.
  */
 int proc_pid_of(const char *pname);
+
+/**
+ * smaps id
+ */
+enum smap_id {
+        SMAPS_ID_INVALID = -1,
+        SMAPS_ID_ANON_HUGE_PAGES = 0,
+        SMAPS_ID_ANONYMOUS,
+        SMAPS_ID_KERNEL_PAGE_SIZE,
+        SMAPS_ID_LOCKED,
+        SMAPS_ID_MMU_PAGE_SIZE,
+        SMAPS_ID_PSWAP,
+        SMAPS_ID_PRIVATE_CLEAN,
+        SMAPS_ID_PRIVATE_DIRTY,
+        SMAPS_ID_PSS,
+        SMAPS_ID_REFERENCED,
+        SMAPS_ID_RSS,
+        SMAPS_ID_SHARED_CLEAN,
+        SMAPS_ID_SHARED_DIRTY,
+        SMAPS_ID_SIZE,
+        SMAPS_ID_SWAP,
+        SMAPS_ID_MAX,
+};
+
+/**
+ * smaps mask
+ */
+enum smap_mask {
+        SMAPS_MASK_ANON_HUGE_PAGES      = 1 << SMAPS_ID_ANON_HUGE_PAGES,
+        SMAPS_MASK_ANONYMOUS            = 1 << SMAPS_ID_ANONYMOUS,
+        SMAPS_MASK_KERNEL_PAGE_SIZE     = 1 << SMAPS_ID_KERNEL_PAGE_SIZE,
+        SMAPS_MASK_LOCKED               = 1 << SMAPS_ID_LOCKED,
+        SMAPS_MASK_MMU_PAGE_SIZE        = 1 << SMAPS_ID_MMU_PAGE_SIZE,
+        SMAPS_MASK_PSWAP                = 1 << SMAPS_ID_PSWAP,
+        SMAPS_MASK_PRIVATE_CLEAN        = 1 << SMAPS_ID_PRIVATE_CLEAN,
+        SMAPS_MASK_PRIVATE_DIRTY        = 1 << SMAPS_ID_PRIVATE_DIRTY,
+        SMAPS_MASK_PSS                  = 1 << SMAPS_ID_PSS,
+        SMAPS_MASK_REFERENCED           = 1 << SMAPS_ID_REFERENCED,
+        SMAPS_MASK_RSS                  = 1 << SMAPS_ID_RSS,
+        SMAPS_MASK_SHARED_CLEAN         = 1 << SMAPS_ID_SHARED_CLEAN,
+        SMAPS_MASK_SHARED_DIRTY         = 1 << SMAPS_ID_SHARED_DIRTY,
+        SMAPS_MASK_SIZE                 = 1 << SMAPS_ID_SIZE,
+        SMAPS_MASK_SWAP                 = 1 << SMAPS_ID_SWAP,
+        SMAPS_MASK_ALL                  = (1 << SMAPS_ID_MAX) - 1,
+        SMAPS_MASK_DEFAULT              = (SMAPS_MASK_SIZE |
+                                           SMAPS_MASK_RSS |
+                                           SMAPS_MASK_PSS |
+                                           SMAPS_MASK_SHARED_CLEAN |
+                                           SMAPS_MASK_SHARED_DIRTY |
+                                           SMAPS_MASK_PRIVATE_CLEAN |
+                                           SMAPS_MASK_PRIVATE_DIRTY |
+                                           SMAPS_MASK_SWAP |
+                                           SMAPS_MASK_PSWAP),
+};
+
+/**
+ * a smap info
+ */
+struct smap {
+        /**
+         * start address
+         */
+        unsigned int start;
+        /**
+         * end address
+         */
+        unsigned int end;
+        /**
+         * smaps mode
+         */
+        char *mode;
+        /**
+         * smaps name
+         */
+        char *name;
+        /**
+         * value of each
+         */
+        unsigned int value[SMAPS_ID_MAX];
+};
+
+/**
+ * a smaps info of pid
+ */
+struct smaps {
+        /**
+         * sum value of each
+         */
+        unsigned int sum[SMAPS_ID_MAX];
+        /**
+         * number of maps
+         */
+        int n_map;
+        /**
+         * maps
+         */
+        struct smap **maps;
+};
+
+/**
+ * @brief Destroy struct smaps
+ *
+ * @param maps a smaps
+ */
+void smaps_free(struct smaps *maps);
+
+static inline void smaps_freep(struct smaps **maps)
+{
+        if (*maps)
+                smaps_free(*maps);
+}
+
+/**
+ * Declare struct smaps with cleanup attribute. Allocated struct smaps
+ * is destroyed on going out the scope.
+ */
+#define _cleanup_smaps_free_ _cleanup_ (smaps_freep)
+
+/**
+ * @brief Convert smap id to string
+ *
+ * @param id smap id
+ *
+ * @return converted string
+ */
+const char *smap_id_to_string(enum smap_id id);
+
+/**
+ * @brief Convert smap string to id
+ *
+ * @param str smap string
+ *
+ * @return converted id
+ */
+enum smap_id smap_string_to_id(const char *str);
+
+/**
+ * @brief Get smaps info of pid
+ *
+ * @param pid a pid to get
+ * @param maps parsed smaps struct. This value has to be destoryed by
+ * caller. #_cleanup_smaps_free_ is useful to make allocated struct to
+ * autofree.
+ * @code{.c}
+ {
+         _cleanup_smaps_free_ struct smaps *maps;
+
+         proc_pid_get_smaps(pid, &maps, SMAPS_MASK_ALL);
+ }
+ * @endcode
+ * @param mask mask to parse smaps.
+ *
+ * @return 0 on success, -errno on failure.
+ */
+int proc_pid_get_smaps(pid_t pid, struct smaps **maps, enum smap_mask mask);
+
 /**
  * @}
  */