From 5404ee30387a5c19a9ff6269453fa701a51c8fe5 Mon Sep 17 00:00:00 2001 From: WaLyong Cho Date: Thu, 10 Nov 2016 18:25:16 +0900 Subject: [PATCH] libsystem: proc: add smaps parse api for a pid Change-Id: Idae05dc3839b6cbc7051425bcc905a56b9370e17 Signed-off-by: WaLyong Cho --- configure.ac | 5 + packaging/libsystem.spec | 1 + src/Makefile.am | 11 ++ src/libsystem/.gitignore | 3 +- src/libsystem/proc-smaps-lookup.gperf | 70 +++++++++++ src/libsystem/proc.c | 159 +++++++++++++++++++++++++ src/libsystem/proc.h | 161 +++++++++++++++++++++++++- 7 files changed, 408 insertions(+), 2 deletions(-) create mode 100644 src/libsystem/proc-smaps-lookup.gperf diff --git a/configure.ac b/configure.ac index 49bf6cc..9e4ce3b 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/packaging/libsystem.spec b/packaging/libsystem.spec index 7fb9c25..9de8ad3 100644 --- a/packaging/libsystem.spec +++ b/packaging/libsystem.spec @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index f1092bc..f1ca4cf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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) diff --git a/src/libsystem/.gitignore b/src/libsystem/.gitignore index c97363e..fe4ac25 100644 --- a/src/libsystem/.gitignore +++ b/src/libsystem/.gitignore @@ -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 index 0000000..7cf82e1 --- /dev/null +++ b/src/libsystem/proc-smaps-lookup.gperf @@ -0,0 +1,70 @@ +%{ +#include +#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; +} diff --git a/src/libsystem/proc.c b/src/libsystem/proc.c index 0881ae8..38d5b93 100644 --- a/src/libsystem/proc.c +++ b/src/libsystem/proc.c @@ -22,8 +22,10 @@ #include #include #include +#include #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; +} diff --git a/src/libsystem/proc.h b/src/libsystem/proc.h index 2938f6a..132da3c 100644 --- a/src/libsystem/proc.h +++ b/src/libsystem/proc.h @@ -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 +#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); + /** * @} */ -- 2.34.1