From e6f674b36844cf6609116f9ee10269f3a582d65e Mon Sep 17 00:00:00 2001 From: Prajwal A N Date: Thu, 5 Mar 2015 16:24:37 +0900 Subject: [PATCH 02/14] Initial code commit * memps is a tool which provides information about the memory usage of processes (RSS, PSS, memory map of process, clean and dirty pages), tmps and graphics memory usage * Added source files and CmakeLists file * Added packaging and license files Change-Id: I5dc2357ff229ad876ec17919a2e9df51488b89e7 Signed-off-by: Prajwal A N --- CMakeLists.txt | 15 + LICENSE | 205 +++++++++ memps.c | 1124 ++++++++++++++++++++++++++++++++++++++++++++++++++ memps.manifest | 6 + packaging/memps.spec | 35 ++ 5 files changed, 1385 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 memps.c create mode 100644 memps.manifest create mode 100644 packaging/memps.spec diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..298e0e8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,15 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +PROJECT("memps") + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wall -g") + +SET (SOURCES + ${CMAKE_SOURCE_DIR}/memps.c +) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + +ADD_EXECUTABLE (${PROJECT_NAME} ${SOURCES}) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ff4bf31 --- /dev/null +++ b/LICENSE @@ -0,0 +1,205 @@ +Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + diff --git a/memps.c b/memps.c new file mode 100644 index 0000000..082d6d2 --- /dev/null +++ b/memps.c @@ -0,0 +1,1124 @@ +/* + Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define STR_SGX_PATH "/dev/pvrsrvkm" +#define STR_3D_PATH1 "/dev/mali" +#define STR_3D_PATH2 "/dev/kgsl-3d0" +#define STR_DRM_PATH1 "/drm mm object (deleted)" +#define STR_DRM_PATH2 "/dev/dri/card0" + +#define BUF_MAX (BUFSIZ) /* most optimal for libc::stdio */ +#define BUF_INC_SIZE (512 * 1024) /* maximal SMAPS I saw 2 MB */ +#define KB(bytes) ((bytes)/1024) + +typedef struct geminfo geminfo; +typedef struct mapinfo mapinfo; +typedef struct trib_mapinfo trib_mapinfo; + +enum { + OUTPUT_UART, + OUTPUT_FILE, + NUM_OUTPUT_TYPE +}; + +struct mapinfo { + mapinfo *next; + unsigned start; + unsigned end; + unsigned size; + unsigned rss; + unsigned pss; + unsigned shared_clean; + unsigned shared_dirty; + unsigned private_clean; + unsigned private_dirty; + char perm[4]; + char name[1]; +}; + +/* classify normal, graphic and other devices memory */ +struct trib_mapinfo { + unsigned shared_clean; + unsigned shared_dirty; + unsigned private_clean; + unsigned private_dirty; + unsigned shared_clean_pss; + unsigned shared_dirty_pss; + unsigned rss; + unsigned pss; + unsigned size; + unsigned graphic_3d; + unsigned gem_rss; + unsigned gem_pss; + unsigned peak_rss; + unsigned other_devices; + unsigned gem_mmap; +}; + +struct geminfo { + geminfo *next; + unsigned int tgid; + unsigned rss_size; + unsigned pss_size; + unsigned hcount; +}; + +static int ignore_smaps_field; +static int sum; +static int verbos; + +/* reads file contents into memory */ +static char* cread(const char* path) +{ + /* once allocated area for reads */ + static char* text = NULL; + static size_t size = 0; + + ssize_t ret; + char* ptr = text; + size_t cap = size; + int fd = open(path, O_RDONLY); + + if (fd < 0) { + return NULL; + } + + do { + /* ensure we have enough space */ + if (cap == 0) { + ptr = (char*)realloc(text, size + BUF_INC_SIZE); + if (ptr == NULL) { + ret = -1; + break; + } + + text = ptr; + ptr = text + size; + cap = BUF_INC_SIZE; + size += BUF_INC_SIZE; + } + ret = read(fd, ptr, cap); + if (ret == 0) { + *ptr = 0; + } else if (ret > 0) { + cap -= ret; + ptr += ret; + } + } while (ret > 0); + close(fd); + + return (ret < 0 ? NULL : text); +} /* cread */ + +/* like fgets/gets but adjusting contents pointer */ +static inline char* cgets(char** contents) +{ + if (contents && *contents && **contents) { + char* bos = *contents; /* begin of string */ + char* eos = strchr(bos, '\n'); /* end of string */ + + if (eos) { + *contents = eos + 1; + *eos = 0; + } else { + *contents = NULL; + } + return bos; + } + + return NULL; +} /* cgets */ + + +static unsigned get_peak_rss(unsigned int pid) +{ + static const char field[] = "VmHWM:"; + char tmp[128]; + char* line; + char* value; + + sprintf(tmp, "/proc/%d/status", pid); + line = cread(tmp); + if (line == NULL) { + fprintf(stderr, "cannot open %s\n", tmp); + return 0; + } + + value = strstr(line, field); + if (value) { + value += sizeof(field); + return strtoul(value, NULL, 10); + } + + return 0; +} +#define NUM_GEM_FIELD 6 + +static geminfo *read_geminfo(FILE *fp) +{ + geminfo *tgeminfo; + char line[BUF_MAX]; + unsigned int pid, tgid, handle, refcount, hcount; + unsigned gem_size; + + if (fgets(line, BUF_MAX, fp) != NULL) { + if (sscanf(line, "%d %d %d %d %d 0x%x", + &pid, &tgid, &handle, &refcount, + &hcount, &gem_size) != NUM_GEM_FIELD) + return NULL; + + tgeminfo = malloc(sizeof(geminfo)); + if (tgeminfo == NULL) + return NULL; + tgeminfo->tgid = tgid; + tgeminfo->hcount = hcount; + tgeminfo->rss_size = KB(gem_size); + tgeminfo->pss_size = KB(gem_size/tgeminfo->hcount); + } else + return NULL; + + return tgeminfo; +} + + +static geminfo *load_geminfo(void) +{ + geminfo *ginfo; + geminfo *gilist = NULL; + FILE *drm_fp; + char line[BUF_MAX]; + + drm_fp = fopen("/sys/kernel/debug/dri/0/gem_info", "r"); + + if (drm_fp == NULL) { + fprintf(stderr, + "cannot open /sys/kernel/debug/dri/0/gem_info\n"); + return NULL; + } + + if (fgets(line, BUF_MAX, drm_fp) == NULL) { + fclose(drm_fp); + return NULL; + } else { + /* we should count a number of whitespace separated fields */ + int in_field = (line[0] && !isblank(line[0])); + unsigned int size = (unsigned)in_field; + const char* ptr = &line[1]; + + /* sscanf() was used in original code, so number of fields */ + /* in string is expected to be at least NUM_GEM_FIELD */ + while (*ptr && size < NUM_GEM_FIELD) { + if (isblank(*ptr++)) { + if (in_field) { + /* end of field */ + in_field = 0; + } + } else { + if (!in_field) { + /* next field started */ + in_field = 1; + size++; + } + } + } /* while */ + + if (size != NUM_GEM_FIELD) { + fclose(drm_fp); + return NULL; + } + } + + while ((ginfo = read_geminfo(drm_fp)) != NULL) { + if (gilist && ginfo->tgid == gilist->tgid) { + gilist->pss_size += ginfo->pss_size; + gilist->rss_size += ginfo->rss_size; + free(ginfo); + continue; + } + ginfo->next = gilist; + gilist = ginfo; + } + + fclose(drm_fp); + + return gilist; +} + + +/* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so + * 012345678901234567890123456789012345678901234567890123456789 + * 0 1 2 3 4 5 + */ + +mapinfo *read_mapinfo(char** smaps, int rest_line) +{ + char* line; + mapinfo *mi; + int len; + int tmp; + + if ((line = cgets(smaps)) == 0) + return 0; + + len = strlen(line); + if (len < 1) + return 0; + + mi = malloc(sizeof(mapinfo) + len + 16); + if (mi == 0) + return 0; + + mi->start = strtoul(line, 0, 16); + mi->end = strtoul(line + 9, 0, 16); + + mi->perm[0] = line[18]; /* read */ + mi->perm[1] = line[19]; /* write */ + mi->perm[2] = line[20]; /* execute */ + mi->perm[3] = line[21]; /* may share or private */ + + if (len < 50) + strcpy(mi->name, "[anon]"); + else + strcpy(mi->name, line + 49); + + if ((line = cgets(smaps)) == 0) + goto oops; + if (sscanf(line, "Size: %d kB", &mi->size) != 1) + goto oops; + if ((line = cgets(smaps)) == 0) + goto oops; + if (sscanf(line, "Rss: %d kB", &mi->rss) != 1) + goto oops; + if ((line = cgets(smaps)) == 0) + goto oops; + if (sscanf(line, "Pss: %d kB", &mi->pss) == 1) + if ((line = cgets(smaps)) == 0) + goto oops; + if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1) + goto oops; + if ((line = cgets(smaps)) == 0) + goto oops; + if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1) + goto oops; + if ((line = cgets(smaps)) == 0) + goto oops; + if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1) + goto oops; + if ((line = cgets(smaps)) == 0) + goto oops; + if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1) + goto oops; + + while (rest_line-- && (line = cgets(smaps))) { + if (sscanf(line, "PSwap: %d kB", &tmp) == 1) + rest_line++; + } + + return mi; + oops: + printf("mi get error\n"); + free(mi); + return 0; +} + +static unsigned total_gem_memory(void) +{ + FILE *gem_fp; + unsigned total_gem_mem = 0; + unsigned name, size, handles, refcount; + char line[BUF_MAX]; + + gem_fp = fopen("/proc/dri/0/gem_names", "r"); + if(gem_fp == NULL) { + fprintf(stderr, + "cannot open /proc/dir/0/gem_names\n"); + return 0; + } + + if (fgets(line, BUF_MAX, gem_fp) == NULL) { + fclose(gem_fp); + return 0; + } + + while (fgets(line, BUF_MAX, gem_fp) != NULL) + if (sscanf(line, "%d %d %d %d\n", + &name, &size, &handles, &refcount) == 4) + total_gem_mem += size; + fclose(gem_fp); + + return total_gem_mem; +} + +static void get_mem_info(FILE *output_fp) +{ + char buf[PATH_MAX]; + FILE *fp; + char *idx; + unsigned int free = 0, cached = 0; + unsigned int total_mem = 0, available = 0, used; + unsigned int swap_total = 0, swap_free = 0, swap_used; + unsigned int used_ratio; + + if (output_fp == NULL) + return; + + fp = fopen("/proc/meminfo", "r"); + + if (!fp) { + fprintf(stderr, "%s open failed, %p", buf, fp); + return; + } + + while (fgets(buf, PATH_MAX, fp) != NULL) { + if ((idx = strstr(buf, "MemTotal:"))) { + idx += strlen("Memtotal:"); + while (*idx < '0' || *idx > '9') + idx++; + total_mem = atoi(idx); + } else if ((idx = strstr(buf, "MemFree:"))) { + idx += strlen("MemFree:"); + while (*idx < '0' || *idx > '9') + idx++; + free = atoi(idx); + } else if ((idx = strstr(buf, "MemAvailable:"))) { + idx += strlen("MemAvailable:"); + while (*idx < '0' || *idx > '9') + idx++; + available = atoi(idx); + } else if((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) { + idx += strlen("Cached:"); + while (*idx < '0' || *idx > '9') + idx++; + cached = atoi(idx); + } else if((idx = strstr(buf, "SwapTotal:"))) { + idx += strlen("SwapTotal:"); + while (*idx < '0' || *idx > '9') + idx++; + swap_total = atoi(idx); + } else if((idx = strstr(buf, "SwapFree:"))) { + idx += strlen("SwapFree"); + while (*idx < '0' || *idx > '9') + idx++; + swap_free = atoi(idx); + break; + } + } + + if (available == 0) + available = free + cached; + used = total_mem - available; + used_ratio = used * 100 / total_mem; + swap_used = swap_total - swap_free; + + fprintf(output_fp, + "====================================================================\n"); + + + fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n", + total_mem >> 10, total_mem); + + fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n", + (total_mem - free) >> 10, total_mem - free); + + fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n", + used >> 10, used); + + fprintf(output_fp, "Used (Mem): \t\t%15d MB( %6d kB)\n", + used >> 10, used); + + fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n", + swap_used >> 10, swap_used); + + fprintf(output_fp, "Used Ratio: \t\t%15d %%\n", used_ratio); + + fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n", + free >> 10, free); + + fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n", + available >> 10, + available); + fclose(fp); +} + +static int get_tmpfs_info(FILE *output_fp) +{ + FILE *fp; + char line[BUF_MAX]; + char tmpfs_mp[NAME_MAX]; /* tmpfs mount point */ + struct statfs tmpfs_info; + + if (output_fp == NULL) + return -1; + + fp = fopen("/etc/mtab", "r"); + if (fp == NULL) + return -1; + + fprintf(output_fp, + "====================================================================\n"); + fprintf(output_fp, "TMPFS INFO\n"); + + while (fgets(line, BUF_MAX, fp) != NULL) { + if (sscanf(line, "tmpfs %s tmpfs", tmpfs_mp) == 1) { + statfs(tmpfs_mp, &tmpfs_info); + fprintf(output_fp, + "tmpfs %16s Total %8ld KB, Used %8ld, Avail %8ld\n", + tmpfs_mp, + /* 1 block is 4 KB */ + tmpfs_info.f_blocks * 4, + (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4, + tmpfs_info.f_bfree * 4); + } + } + fclose(fp); + return 0; +} + +mapinfo *load_maps(int pid) +{ + char* smaps; + char tmp[128]; + mapinfo *milist = 0; + mapinfo *mi; + + sprintf(tmp, "/proc/%d/smaps", pid); + smaps = cread(tmp); + if (smaps == NULL) + return 0; + + while ((mi = read_mapinfo(&smaps, ignore_smaps_field)) != 0) { + if (milist) { + if ((!strcmp(mi->name, milist->name) + && (mi->name[0] != '['))) { + milist->size += mi->size; + milist->rss += mi->rss; + milist->pss += mi->pss; + milist->shared_clean += mi->shared_clean; + milist->shared_dirty += mi->shared_dirty; + milist->private_clean += mi->private_clean; + milist->private_dirty += mi->private_dirty; + + milist->perm[0] = mi->perm[0]; + milist->perm[1] = mi->perm[1]; + milist->perm[2] = mi->perm[2]; + milist->perm[3] = mi->perm[3]; + milist->end = mi->end; + strncpy(milist->perm, mi->perm, 4); + free(mi); + continue; + } + } + mi->next = milist; + milist = mi; + } + + return milist; +} + +static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist) +{ + geminfo *gi; + for (gi = gilist; gi; ) { + if (gi->tgid == tgid) + return gi; + + gi = gi->next; + } + return NULL; +} + +static void init_trib_mapinfo(trib_mapinfo *tmi) +{ + if (!tmi) + return; + tmi->shared_clean = 0; + tmi->shared_dirty = 0; + tmi->private_clean = 0; + tmi->private_dirty = 0; + tmi->shared_clean_pss = 0; + tmi->shared_dirty_pss = 0; + tmi->rss = 0; + tmi->pss = 0; + tmi->size = 0; + tmi->graphic_3d = 0; + tmi->gem_rss = 0; + tmi->gem_pss = 0; + tmi->peak_rss = 0; + tmi->other_devices = 0; + tmi->gem_mmap = 0; +} + +static int +get_trib_mapinfo(unsigned int tgid, mapinfo *milist, + geminfo *gilist, trib_mapinfo *result) + +{ + mapinfo *mi; + mapinfo *temp = NULL; + geminfo *gi; + + if (!result) + return -EINVAL; + + init_trib_mapinfo(result); + for (mi = milist; mi;) { + if (strstr(mi->name, STR_SGX_PATH)) { + result->graphic_3d += mi->pss; + } else if (strstr(mi->name, STR_3D_PATH1) || + strstr(mi->name, STR_3D_PATH2)) { + result->graphic_3d += mi->size; + } else if (mi->rss != 0 && mi->pss == 0 + && mi->shared_clean == 0 + && mi->shared_dirty == 0 + && mi->private_clean == 0 + && mi->private_dirty == 0) { + result->other_devices += mi->size; + } else if (!strncmp(mi->name, STR_DRM_PATH1, + sizeof(STR_DRM_PATH1)) || + !strncmp(mi->name, STR_DRM_PATH2, + sizeof(STR_DRM_PATH2))) { + result->gem_mmap += mi->rss; + } else { + result->shared_clean += mi->shared_clean; + result->shared_dirty += mi->shared_dirty; + result->private_clean += mi->private_clean; + result->private_dirty += mi->private_dirty; + result->rss += mi->rss; + result->pss += mi->pss; + result->size += mi->size; + + if(mi->shared_clean != 0) + result->shared_clean_pss += mi->pss; + else if (mi->shared_dirty != 0) + result->shared_dirty_pss += mi->pss; + } + + temp = mi; + mi = mi->next; + free(temp); + temp = NULL; + } + + result->peak_rss = get_peak_rss(tgid); + if (result->peak_rss < result->rss) + result->peak_rss = result->rss; + if (result->gem_mmap > 0) + result->peak_rss -= result->gem_mmap; + + gi = find_geminfo(tgid, gilist); + if (gi != NULL) { + result->gem_rss = gi->rss_size; + result->gem_pss = gi->pss_size; + } + + return 0; +} + +static int get_cmdline(unsigned int pid, char *cmdline) +{ + FILE *fp; + char buf[NAME_MAX] = {0, }; + int ret = -1; + + sprintf(buf, "/proc/%d/cmdline", pid); + fp = fopen(buf, "r"); + if (fp == 0) { + fprintf(stderr, "cannot file open %s\n", buf); + return ret; + } + if ((ret = fscanf(fp, "%s", cmdline)) < 1) { + fclose(fp); + return ret; + } + fclose(fp); + + return ret; +} + +static int get_oomscoreadj(unsigned int pid) +{ + FILE *fp; + char tmp[256]; + int oomadj_val; + + sprintf(tmp, "/proc/%d/oom_score_adj", pid); + fp = fopen(tmp, "r"); + + if (fp == NULL) { + oomadj_val = -50; + return oomadj_val; + } + if (fgets(tmp, sizeof(tmp), fp) == NULL) { + oomadj_val = -100; + fclose(fp); + return oomadj_val; + } + + oomadj_val = atoi(tmp); + + fclose(fp); + return oomadj_val; +} + +static void get_rss(pid_t pid, unsigned int *result) +{ + FILE *fp; + char proc_path[PATH_MAX]; + int rss = 0; + + *result = 0; + + sprintf(proc_path, "/proc/%d/statm", pid); + fp = fopen(proc_path, "r"); + if (fp == NULL) + return; + + if (fscanf(fp, "%*s %d", &rss) < 1) { + fclose(fp); + return; + } + + fclose(fp); + + /* convert page to Kb */ + *result = rss * 4; + return; +} + +static void show_rss(int output_type, char *output_path) +{ + DIR *pDir = NULL; + struct dirent *curdir; + pid_t pid; + char cmdline[PATH_MAX]; + FILE *output_file = NULL; + int oom_score_adj; + unsigned int rss; + + pDir = opendir("/proc"); + if (pDir == NULL) { + fprintf(stderr, "cannot read directory /proc.\n"); + return; + } + + if (output_type == OUTPUT_FILE && output_path) { + output_file = fopen(output_path, "w+"); + if (!output_file) { + fprintf(stderr, "cannot open output file(%s)\n", + output_path); + closedir(pDir); + exit(1); + } + } else + output_file = stdout; + + + fprintf(output_file, + " PID RSS OOM_SCORE COMMAND\n"); + + while ((curdir = readdir(pDir)) != NULL) { + pid = atoi(curdir->d_name); + if (pid < 1 || pid > 32768 || pid == getpid()) + continue; + + if (get_cmdline(pid, cmdline) < 0) + continue; + get_rss(pid, &rss); + oom_score_adj = get_oomscoreadj(pid); + + fprintf(output_file, + "%8d %8u %8d %s\n", + pid, + rss, + oom_score_adj, + cmdline); + + + } /* end of while */ + + get_tmpfs_info(output_file); + get_mem_info(output_file); + + fclose(output_file); + closedir(pDir); + + return; +} + +static int show_map_all_new(int output_type, char *output_path) +{ + DIR *pDir = NULL; + struct dirent *curdir; + unsigned int pid; + mapinfo *milist; + geminfo *glist; + unsigned total_pss = 0; + unsigned total_private = 0; + unsigned total_private_code = 0; + unsigned total_private_data = 0; + unsigned total_shared_code = 0; + unsigned total_shared_data = 0; + unsigned total_shared_code_pss = 0; + unsigned total_shared_data_pss = 0; + unsigned total_rss = 0; + unsigned total_graphic_3d = 0; + unsigned total_gem_rss = 0; + unsigned total_gem_pss = 0; + unsigned total_peak_rss = 0; + unsigned total_allocated_gem = 0; + trib_mapinfo tmi; + char cmdline[PATH_MAX]; + FILE *output_file = NULL; + int oom_score_adj; + + pDir = opendir("/proc"); + if (pDir == NULL) { + fprintf(stderr, "cannot read directory /proc.\n"); + return 0; + } + + if (output_type == OUTPUT_FILE && output_path) { + output_file = fopen(output_path, "w+"); + if (!output_file) { + fprintf(stderr, "cannot open output file(%s)\n", + output_path); + closedir(pDir); + exit(1); + } + } else + output_file = stdout; + + glist = load_geminfo(); + + if (!sum) { + if (verbos) + fprintf(output_file, + " PID S(CODE) S(DATA) P(CODE) P(DATA)" + " PEAK PSS 3D" + " GEM(PSS) GEM(RSS)" + " OOM_SCORE_ADJ COMMAND\n"); + else + fprintf(output_file, + " PID CODE DATA PEAK PSS" + " 3D GEM(PSS) COMMAND\n"); + } + + while ((curdir = readdir(pDir)) != NULL) { + pid = atoi(curdir->d_name); + if (pid < 1 || pid > 32768 || pid == getpid()) + continue; + + if (get_cmdline(pid, cmdline) < 0) + continue; + + milist = load_maps(pid); + if (milist == 0) + continue; + + /* get classified map info */ + get_trib_mapinfo(pid, milist, glist, &tmi); + oom_score_adj = get_oomscoreadj(pid); + + if (!sum) { + if (verbos) + fprintf(output_file, + "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d" + " %8d \t\t%s\n", + pid, + tmi.shared_clean, tmi.shared_dirty, + tmi.private_clean, tmi.private_dirty, + tmi.peak_rss, tmi.pss, tmi.graphic_3d, + tmi.gem_pss, tmi.gem_rss, oom_score_adj, cmdline); + else + fprintf(output_file, + "%8d %8d %8d %8d %8d %8d %8d %s\n", + pid, + tmi.shared_clean + + tmi.private_clean, + tmi.shared_dirty + tmi.private_dirty, + tmi.peak_rss, + tmi.pss, + tmi.graphic_3d, + tmi.gem_pss, cmdline); + + if (tmi.other_devices != 0) + fprintf(output_file, + "%s(%d) %d KB may mapped by device(s).\n", + cmdline, pid, tmi.other_devices); + } + + total_private += (tmi.private_clean + tmi.private_dirty); + total_pss += tmi.pss; + total_rss += tmi.rss; + total_graphic_3d += tmi.graphic_3d; + total_gem_rss += tmi.gem_rss; + total_gem_pss += tmi.gem_pss; + total_private_code += tmi.private_clean; + total_private_data += tmi.private_dirty; + total_shared_code += tmi.shared_clean; + total_shared_data += tmi.shared_dirty; + total_peak_rss += tmi.peak_rss; + + total_shared_code_pss += tmi.shared_clean_pss; + total_shared_data_pss += tmi.shared_dirty_pss; + } /* end of while */ + + total_allocated_gem = KB(total_gem_memory()); + fprintf(output_file, + "===============================================" + "===============================================\n"); + if (verbos) { + fprintf(output_file, + "TOTAL: S(CODE) S(DATA) P(CODE) P(DATA)" + " PEAK PSS 3D " + "GEM(PSS) GEM(RSS) GEM(ALLOC) TOTAL(KB)\n"); + fprintf(output_file, + " %8d %8d %8d %8d %8d %8d %8d" + " %8d %8d %8d %8d\n", + total_shared_code, total_shared_data, + total_private_code, total_private_data, + total_peak_rss, total_pss, total_graphic_3d, + total_gem_pss, total_gem_rss, + total_allocated_gem, + total_pss + total_graphic_3d + + total_allocated_gem); + } else { + fprintf(output_file, + "TOTAL: CODE DATA PEAK PSS " + "3D GEM(PSS) GEM(ALLOC) TOTAL(KB)\n"); + fprintf(output_file, " %8d %8d %8d %8d %8d %8d %7d %8d\n", + total_shared_code + total_private_code, + total_shared_data + total_private_data, + total_peak_rss, total_pss, + total_graphic_3d, total_gem_pss, + total_allocated_gem, + total_pss + total_graphic_3d + + total_allocated_gem); + + } + + if (verbos) + fprintf(output_file, + "* S(CODE): shared clean memory, it includes" + " duplicated memory\n" + "* S(DATA): shared dirty memory, it includes" + " duplicated memory\n" + "* P(CODE): private clean memory\n" + "* P(DATA): private dirty memory\n" + "* PEAK: peak memory usage of S(CODE) + S(DATA)" + " + P(CODE) + P(DATA)\n" + "* PSS: Proportional Set Size\n" + "* 3D: memory allocated by GPU driver\n" + "* GEM(PSS): GEM memory devided by # of sharers\n" + "* GEM(RSS): GEM memory including duplicated memory\n" + "* GEM(ALLOC): sum of unique gem memory in the system\n" + "* TOTAL: PSS + 3D + GEM(ALLOC) \n"); + else + fprintf(output_file, + "* CODE: shared and private clean memory\n" + "* DATA: shared and private dirty memory\n" + "* PEAK: peak memory usage of CODE + DATA\n" + "* PSS: Proportional Set Size\n" + "* 3D: memory allocated by GPU driver\n" + "* GEM(PSS): GEM memory deviced by # of sharers\n" + "* GEM(ALLOC): sum of unique GEM memory in the system\n" + "* TOTAL: PSS + 3D + GEM(ALLOC)\n"); + + get_tmpfs_info(output_file); + get_mem_info(output_file); + + fclose(output_file); + free(glist); + closedir(pDir); + return 1; +} + +static int show_map_new(int pid) +{ + mapinfo *milist; + mapinfo *mi; + unsigned shared_dirty = 0; + unsigned shared_clean = 0; + unsigned private_dirty = 0; + unsigned private_clean = 0; + unsigned pss = 0; + unsigned start = 0; + unsigned end = 0; + unsigned private_clean_total = 0; + unsigned private_dirty_total = 0; + unsigned shared_clean_total = 0; + unsigned shared_dirty_total = 0; + int duplication = 0; + + milist = load_maps(pid); + + if (milist == 0) { + fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid); + return 1; + } + + if (!sum) { + printf(" S(CODE) S(DATA) P(CODE) P(DATA) ADDR(start-end)" + "OBJECT NAME\n"); + printf("-------- -------- -------- -------- -----------------" + "------------------------------\n"); + } else { + printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS\n"); + printf("-------- -------- -------------------" + "------------------\n"); + } + for (mi = milist; mi; mi = mi->next) { + shared_clean += mi->shared_clean; + shared_dirty += mi->shared_dirty; + private_clean += mi->private_clean; + private_dirty += mi->private_dirty; + pss += mi->pss; + + shared_clean_total += mi->shared_clean; + shared_dirty_total += mi->shared_dirty; + private_clean_total += mi->private_clean; + private_dirty_total += mi->private_dirty; + + if (!duplication) + start = mi->start; + + if ((mi->next && !strcmp(mi->next->name, mi->name)) && + (mi->next->start == mi->end)) { + duplication = 1; + continue; + } + end = mi->end; + duplication = 0; + + if (!sum) { + printf("%8d %8d %8d %8d %08x-%08x %s\n", + shared_clean, shared_dirty, private_clean, private_dirty, + start, end, mi->name); + } + shared_clean = 0; + shared_dirty = 0; + private_clean = 0; + private_dirty = 0; + } + if (sum) { + printf("%8d %8d %8d %8d %18d\n", + shared_clean_total, + shared_dirty_total, + private_clean_total, + private_dirty_total, + pss); + } + + return 1; +} + +void check_kernel_version(void) +{ + struct utsname buf; + int ret; + + ret = uname(&buf); + + if (!ret) { + if (buf.release[0] == '3') { + char *pch; + char str[3]; + int sub_version; + pch = strstr(buf.release, "."); + strncpy(str, pch+1, 2); + sub_version = atoi(str); + + if (sub_version >= 10) + ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages, + Swap, KernelPageSize, MMUPageSize, + Locked, VmFlags */ + + else + ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages, + Swap, KernelPageSize, MMUPageSize, + Locked */ + } else { + ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize, + MMUPageSize */ + } + } +} + +int main(int argc, char *argv[]) +{ + int usage = 1; + sum = 0; + + if (argc > 1) { + check_kernel_version(); + + if (!strcmp(argv[1], "-r")) { + if (argc >= 3) + show_rss(OUTPUT_FILE, argv[2]); + else + show_rss(OUTPUT_UART, NULL); + usage = 0; + } else if (!strcmp(argv[1], "-s")) { + sum = 1; + if (argc == 3 && atoi(argv[2]) > 0) { + show_map_new(atoi(argv[2])); + usage = 0; + } + } else if (!strcmp(argv[1], "-a")) { + verbos = 0; + show_map_all_new(OUTPUT_UART, NULL); + usage = 0; + } else if (!strcmp(argv[1], "-v")) { + verbos = 1; + show_map_all_new(OUTPUT_UART, NULL); + usage = 0; + } else if (!strcmp(argv[1], "-f")) { + if (argc >= 3) { + verbos = 1; + show_map_all_new(OUTPUT_FILE, argv[2]); + usage = 0; + } + } else if (argc == 2 && atoi(argv[1]) > 0) { + show_map_new(atoi(argv[1])); + usage = 0; + } + } + if (usage) { + fprintf(stderr, + "memps [-a] | [-v] | [-s] | [-f] \n" + " -s = sum (show only sum of each)\n" + " -f = all (show all processes via output file)\n" + " -a = all (show all processes)\n" + " -v = verbos (show all processes in detail)\n"); + } + + return 0; +} diff --git a/memps.manifest b/memps.manifest new file mode 100644 index 0000000..79f9a2f --- /dev/null +++ b/memps.manifest @@ -0,0 +1,6 @@ + + + + + + diff --git a/packaging/memps.spec b/packaging/memps.spec new file mode 100644 index 0000000..d7142a8 --- /dev/null +++ b/packaging/memps.spec @@ -0,0 +1,35 @@ +Name: memps +Summary: Tool to summarize memory usage of processes, tmpfs and graphics memory usage +Version: 0.1.8 +Release: 0 +Group: System/Utilities +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz + +BuildRequires: cmake + + +%description +memps displays information about current memory usage of processes (metrics like RSS, PSS, memory map of the process, clean and dirty pages), tmpfs and graphics memory usage information + + +%prep +%setup -q + + +%build +%cmake . + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} + +%make_install + + +%files +%manifest memps.manifest +%defattr(-,root,root,-) +%{_bindir}/memps +%license LICENSE -- 2.7.4 From 59df0a09ae75f2fdf16beb070efa8276f174f5fe Mon Sep 17 00:00:00 2001 From: Hyeongsik Min Date: Tue, 29 Dec 2015 19:30:27 +0900 Subject: [PATCH 03/14] Fix Svace issues 15723 - DIVISION_BY_ZERO 25769 - DIVISION_BY_ZERO 25770 - DIVISION_BY_ZERO Change-Id: Icfed0528b6891af56f51c4c7864652ae01801ef8 Signed-off-by: Hyeongsik Min --- memps.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/memps.c b/memps.c index 082d6d2..33ec5b4 100644 --- a/memps.c +++ b/memps.c @@ -195,6 +195,8 @@ static geminfo *read_geminfo(FILE *fp) &hcount, &gem_size) != NUM_GEM_FIELD) return NULL; + if (hcount == 0) + return NULL; tgeminfo = malloc(sizeof(geminfo)); if (tgeminfo == NULL) return NULL; @@ -432,6 +434,12 @@ static void get_mem_info(FILE *output_fp) } } + if (total_mem == 0) { + fprintf(stderr, "cannot get total memory size\n"); + fclose(fp); + return; + } + if (available == 0) available = free + cached; used = total_mem - available; -- 2.7.4 From 0ea9bc0f2035bed73c2cfc886bd0d9d45769690c Mon Sep 17 00:00:00 2001 From: "Minyoung, Song" Date: Tue, 8 Mar 2016 17:17:17 +0900 Subject: [PATCH 04/14] Merged latest memps code providing swap and cgroup usage Change-Id: I44624fa22a9bc9f9703c55d919cf1fdf5b9cca42 Signed-off-by: Minyoung, Song --- memps.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 175 insertions(+), 15 deletions(-) diff --git a/memps.c b/memps.c index 33ec5b4..b9c36c4 100644 --- a/memps.c +++ b/memps.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -34,11 +35,25 @@ #define STR_3D_PATH2 "/dev/kgsl-3d0" #define STR_DRM_PATH1 "/drm mm object (deleted)" #define STR_DRM_PATH2 "/dev/dri/card0" +#define MEMCG_PATH "/sys/fs/cgroup/memory" +#define ZRAM_USED_PATH "/sys/block/zram0/mem_used_total" #define BUF_MAX (BUFSIZ) /* most optimal for libc::stdio */ #define BUF_INC_SIZE (512 * 1024) /* maximal SMAPS I saw 2 MB */ #define KB(bytes) ((bytes)/1024) +#define BYTE_TO_KBYTE(b) ((b) >> 10) +#define BYTE_TO_MBYTE(b) ((b) >> 20) +#define BYTE_TO_GBYTE(b) ((b) >> 30) + +#define KBYTE_TO_BYTE(k) ((k) << 10) +#define KBYTE_TO_MBYTE(k) ((k) >> 10) +#define KBYTE_TO_GBYTE(k) ((k) >> 20) + +#define GBYTE_TO_BYTE(g) ((g) << 30) +#define GBYTE_TO_KBYTE(g) ((g) << 20) +#define GBYTE_TO_MBYTE(g) ((g) << 10) + typedef struct geminfo geminfo; typedef struct mapinfo mapinfo; typedef struct trib_mapinfo trib_mapinfo; @@ -54,6 +69,7 @@ struct mapinfo { unsigned start; unsigned end; unsigned size; + unsigned swap; unsigned rss; unsigned pss; unsigned shared_clean; @@ -72,6 +88,7 @@ struct trib_mapinfo { unsigned private_dirty; unsigned shared_clean_pss; unsigned shared_dirty_pss; + unsigned swap; unsigned rss; unsigned pss; unsigned size; @@ -340,6 +357,10 @@ mapinfo *read_mapinfo(char** smaps, int rest_line) goto oops; while (rest_line-- && (line = cgets(smaps))) { + if (sscanf(line, "Swap: %d kB", &tmp) == 1) { + mi->swap = tmp; + //rest_line++; + } if (sscanf(line, "PSwap: %d kB", &tmp) == 1) rest_line++; } @@ -379,6 +400,129 @@ static unsigned total_gem_memory(void) return total_gem_mem; } + +int fread_uint(const char *path, u_int32_t *number) +{ + FILE *f = NULL; + int ret; + + f = fopen(path, "r"); + + if(!f) { + fprintf(stderr,"Fail to open %s file.\n", path); + return -1; + } + + ret = fscanf(f, "%u", number); + if(ret == EOF) { + fprintf(stderr,"Fail to read file\n"); + fclose(f); + return -1; + } + + fclose(f); + return 0; +} + +#define MAX_PATH_LENGTH 512 +static int cgroup_read_node(const char *cgroup_name, + const char *file_name, unsigned int *value) +{ + char buf[MAX_PATH_LENGTH]; + int ret; + snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name); + ret = fread_uint(buf, value); + return ret; +} + +/** + * @desc Provides usage in bytes for provided memory cgroup. Works + * with/without swap accounting. + * + * @param memcg_path[in] Full path to memory cgroup + * @param swap[in] Boolean value for deciding if account usage with swap + * @return current cgroup usage in bytes or 0 on error + */ +static unsigned int get_memcg_usage(const char *memcg_path, bool swap) +{ + int ret; + unsigned int usage; + + if (swap) { + ret = cgroup_read_node(memcg_path, + "/memory.memsw.usage_in_bytes", &usage); + } else { + ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes", + &usage); + } + + if (ret != 0) + usage = 0; + + return usage; +} + +static void get_memcg_info(FILE *output_fp) +{ + char buf[PATH_MAX]; + DIR *pdir = NULL; + struct dirent entry; + struct dirent *result; + struct stat path_stat; + long usage_swap; + unsigned long usage, usage_with_swap; + int ret; + + fprintf(output_fp,"====================================================================\n"); + fprintf(output_fp,"MEMORY CGROUPS USAGE INFO\n"); + + pdir = opendir(MEMCG_PATH); + if (pdir == NULL) { + fprintf(stderr,"cannot read directory %s", MEMCG_PATH); + return; + } + + while (!(ret = readdir_r(pdir, &entry, &result)) && result != NULL) { + snprintf(buf, sizeof(buf), "%s/%s", MEMCG_PATH, entry.d_name); + /* If can't stat then ignore */ + if (stat(buf, &path_stat) != 0) + continue; + + /* If it's not directory or it's parent path then ignore */ + if (!(S_ISDIR(path_stat.st_mode) && + strncmp(entry.d_name, "..", 3))) + continue; + + usage = get_memcg_usage(buf, false); + usage_with_swap = get_memcg_usage(buf, true); + /* It is posible by rounding errors to get negative value */ + usage_swap = usage_with_swap - usage; + if (usage_swap < 0) + usage_swap = 0; + + /* Case of root cgroup in hierarchy */ + if (!strncmp(entry.d_name, ".", 2)) + fprintf(output_fp, "%13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB) \n\n", + MEMCG_PATH, BYTE_TO_MBYTE(usage), + BYTE_TO_KBYTE(usage), + BYTE_TO_MBYTE(usage_with_swap), + BYTE_TO_KBYTE(usage_with_swap), + BYTE_TO_MBYTE(usage_swap), + BYTE_TO_KBYTE(usage_swap)); + else + fprintf(output_fp, "memcg: %13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB)\n", + entry.d_name, BYTE_TO_MBYTE(usage), + BYTE_TO_KBYTE(usage), + BYTE_TO_MBYTE(usage_with_swap), + BYTE_TO_KBYTE(usage_with_swap), + BYTE_TO_MBYTE(usage_swap), + BYTE_TO_KBYTE(usage_swap)); + + } + + closedir(pdir); +} + static void get_mem_info(FILE *output_fp) { char buf[PATH_MAX]; @@ -386,7 +530,7 @@ static void get_mem_info(FILE *output_fp) char *idx; unsigned int free = 0, cached = 0; unsigned int total_mem = 0, available = 0, used; - unsigned int swap_total = 0, swap_free = 0, swap_used; + unsigned int swap_total = 0, swap_free = 0, zram_used, swap_used; unsigned int used_ratio; if (output_fp == NULL) @@ -446,6 +590,9 @@ static void get_mem_info(FILE *output_fp) used_ratio = used * 100 / total_mem; swap_used = swap_total - swap_free; + if (fread_uint(ZRAM_USED_PATH, &zram_used) != 0) + zram_used = 0; + fprintf(output_fp, "====================================================================\n"); @@ -465,6 +612,9 @@ static void get_mem_info(FILE *output_fp) fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n", swap_used >> 10, swap_used); + fprintf(output_fp, "Used (Zram block device): %13d MB( %6d kB)\n", + BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used)); + fprintf(output_fp, "Used Ratio: \t\t%15d %%\n", used_ratio); fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n", @@ -527,6 +677,7 @@ mapinfo *load_maps(int pid) if ((!strcmp(mi->name, milist->name) && (mi->name[0] != '['))) { milist->size += mi->size; + milist->swap += mi->swap; milist->rss += mi->rss; milist->pss += mi->pss; milist->shared_clean += mi->shared_clean; @@ -571,6 +722,7 @@ static void init_trib_mapinfo(trib_mapinfo *tmi) tmi->shared_dirty = 0; tmi->private_clean = 0; tmi->private_dirty = 0; + tmi->swap = 0; tmi->shared_clean_pss = 0; tmi->shared_dirty_pss = 0; tmi->rss = 0; @@ -607,7 +759,8 @@ get_trib_mapinfo(unsigned int tgid, mapinfo *milist, && mi->shared_clean == 0 && mi->shared_dirty == 0 && mi->private_clean == 0 - && mi->private_dirty == 0) { + && mi->private_dirty == 0 + && mi->swap == 0) { result->other_devices += mi->size; } else if (!strncmp(mi->name, STR_DRM_PATH1, sizeof(STR_DRM_PATH1)) || @@ -619,6 +772,7 @@ get_trib_mapinfo(unsigned int tgid, mapinfo *milist, result->shared_dirty += mi->shared_dirty; result->private_clean += mi->private_clean; result->private_dirty += mi->private_dirty; + result->swap += mi->swap; result->rss += mi->rss; result->pss += mi->pss; result->size += mi->size; @@ -796,6 +950,7 @@ static int show_map_all_new(int output_type, char *output_path) unsigned total_shared_data = 0; unsigned total_shared_code_pss = 0; unsigned total_shared_data_pss = 0; + unsigned total_swap = 0; unsigned total_rss = 0; unsigned total_graphic_3d = 0; unsigned total_gem_rss = 0; @@ -831,12 +986,12 @@ static int show_map_all_new(int output_type, char *output_path) fprintf(output_file, " PID S(CODE) S(DATA) P(CODE) P(DATA)" " PEAK PSS 3D" - " GEM(PSS) GEM(RSS)" - " OOM_SCORE_ADJ COMMAND\n"); + " GEM(PSS) GEM(RSS) SWAP" + " OOM_SCORE_ADJ COMMAND\n"); else fprintf(output_file, " PID CODE DATA PEAK PSS" - " 3D GEM(PSS) COMMAND\n"); + " 3D GEM(PSS) SWAP COMMAND\n"); } while ((curdir = readdir(pDir)) != NULL) { @@ -858,16 +1013,17 @@ static int show_map_all_new(int output_type, char *output_path) if (!sum) { if (verbos) fprintf(output_file, - "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d" + "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d" " %8d \t\t%s\n", pid, tmi.shared_clean, tmi.shared_dirty, tmi.private_clean, tmi.private_dirty, tmi.peak_rss, tmi.pss, tmi.graphic_3d, - tmi.gem_pss, tmi.gem_rss, oom_score_adj, cmdline); + tmi.gem_pss, tmi.gem_rss, tmi.swap, + oom_score_adj, cmdline); else fprintf(output_file, - "%8d %8d %8d %8d %8d %8d %8d %s\n", + "%8d %8d %8d %8d %8d %8d %8d %8d %s\n", pid, tmi.shared_clean + tmi.private_clean, @@ -875,7 +1031,8 @@ static int show_map_all_new(int output_type, char *output_path) tmi.peak_rss, tmi.pss, tmi.graphic_3d, - tmi.gem_pss, cmdline); + tmi.gem_pss, + tmi.swap, cmdline); if (tmi.other_devices != 0) fprintf(output_file, @@ -891,12 +1048,14 @@ static int show_map_all_new(int output_type, char *output_path) total_gem_pss += tmi.gem_pss; total_private_code += tmi.private_clean; total_private_data += tmi.private_dirty; + total_swap += tmi.swap; total_shared_code += tmi.shared_clean; total_shared_data += tmi.shared_dirty; total_peak_rss += tmi.peak_rss; total_shared_code_pss += tmi.shared_clean_pss; total_shared_data_pss += tmi.shared_dirty_pss; + } /* end of while */ total_allocated_gem = KB(total_gem_memory()); @@ -907,27 +1066,27 @@ static int show_map_all_new(int output_type, char *output_path) fprintf(output_file, "TOTAL: S(CODE) S(DATA) P(CODE) P(DATA)" " PEAK PSS 3D " - "GEM(PSS) GEM(RSS) GEM(ALLOC) TOTAL(KB)\n"); + "GEM(PSS) GEM(RSS) GEM(ALLOC) SWAP TOTAL(KB)\n"); fprintf(output_file, " %8d %8d %8d %8d %8d %8d %8d" - " %8d %8d %8d %8d\n", + " %8d %8d %8d %8d %8d\n", total_shared_code, total_shared_data, total_private_code, total_private_data, total_peak_rss, total_pss, total_graphic_3d, total_gem_pss, total_gem_rss, - total_allocated_gem, + total_allocated_gem, total_swap, total_pss + total_graphic_3d + total_allocated_gem); } else { fprintf(output_file, "TOTAL: CODE DATA PEAK PSS " "3D GEM(PSS) GEM(ALLOC) TOTAL(KB)\n"); - fprintf(output_file, " %8d %8d %8d %8d %8d %8d %7d %8d\n", + fprintf(output_file, " %8d %8d %8d %8d %8d %8d %7d %8d %8d\n", total_shared_code + total_private_code, total_shared_data + total_private_data, total_peak_rss, total_pss, total_graphic_3d, total_gem_pss, - total_allocated_gem, + total_allocated_gem, total_swap, total_pss + total_graphic_3d + total_allocated_gem); @@ -948,7 +1107,7 @@ static int show_map_all_new(int output_type, char *output_path) "* GEM(PSS): GEM memory devided by # of sharers\n" "* GEM(RSS): GEM memory including duplicated memory\n" "* GEM(ALLOC): sum of unique gem memory in the system\n" - "* TOTAL: PSS + 3D + GEM(ALLOC) \n"); + "* TOTAL: PSS + 3D + GEM(ALLOC)\n"); else fprintf(output_file, "* CODE: shared and private clean memory\n" @@ -961,6 +1120,7 @@ static int show_map_all_new(int output_type, char *output_path) "* TOTAL: PSS + 3D + GEM(ALLOC)\n"); get_tmpfs_info(output_file); + get_memcg_info(output_file); get_mem_info(output_file); fclose(output_file); -- 2.7.4 From 257c19ab58a81e358a3d3ad07b96dee25a47f29e Mon Sep 17 00:00:00 2001 From: Hyeongsik Min Date: Wed, 9 Mar 2016 17:29:26 +0900 Subject: [PATCH 05/14] Fix svace issues Change-Id: I9182ba8d42006a5388dacad9144d9cb2b6a0b2b1 Signed-off-by: Hyeongsik Min --- memps.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/memps.c b/memps.c index b9c36c4..ed5a1e4 100644 --- a/memps.c +++ b/memps.c @@ -182,7 +182,7 @@ static unsigned get_peak_rss(unsigned int pid) char* line; char* value; - sprintf(tmp, "/proc/%d/status", pid); + snprintf(tmp, sizeof(tmp), "/proc/%d/status", pid); line = cread(tmp); if (line == NULL) { fprintf(stderr, "cannot open %s\n", tmp); @@ -324,9 +324,9 @@ mapinfo *read_mapinfo(char** smaps, int rest_line) mi->perm[3] = line[21]; /* may share or private */ if (len < 50) - strcpy(mi->name, "[anon]"); + strncpy(mi->name, "[anon]", strlen("[anon]")); else - strcpy(mi->name, line + 49); + strncpy(mi->name, line + 49, len); if ((line = cgets(smaps)) == 0) goto oops; @@ -667,7 +667,7 @@ mapinfo *load_maps(int pid) mapinfo *milist = 0; mapinfo *mi; - sprintf(tmp, "/proc/%d/smaps", pid); + snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid); smaps = cread(tmp); if (smaps == NULL) return 0; @@ -810,13 +810,13 @@ static int get_cmdline(unsigned int pid, char *cmdline) char buf[NAME_MAX] = {0, }; int ret = -1; - sprintf(buf, "/proc/%d/cmdline", pid); + snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); fp = fopen(buf, "r"); if (fp == 0) { fprintf(stderr, "cannot file open %s\n", buf); return ret; } - if ((ret = fscanf(fp, "%s", cmdline)) < 1) { + if ((ret = fscanf(fp, "%4095s", cmdline)) < 1) { fclose(fp); return ret; } @@ -831,7 +831,7 @@ static int get_oomscoreadj(unsigned int pid) char tmp[256]; int oomadj_val; - sprintf(tmp, "/proc/%d/oom_score_adj", pid); + snprintf(tmp, sizeof(tmp), "/proc/%d/oom_score_adj", pid); fp = fopen(tmp, "r"); if (fp == NULL) { @@ -858,7 +858,7 @@ static void get_rss(pid_t pid, unsigned int *result) *result = 0; - sprintf(proc_path, "/proc/%d/statm", pid); + snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid); fp = fopen(proc_path, "r"); if (fp == NULL) return; @@ -878,12 +878,14 @@ static void get_rss(pid_t pid, unsigned int *result) static void show_rss(int output_type, char *output_path) { DIR *pDir = NULL; - struct dirent *curdir; + struct dirent curdir; + struct dirent *result; pid_t pid; char cmdline[PATH_MAX]; FILE *output_file = NULL; int oom_score_adj; unsigned int rss; + int ret; pDir = opendir("/proc"); if (pDir == NULL) { @@ -906,8 +908,8 @@ static void show_rss(int output_type, char *output_path) fprintf(output_file, " PID RSS OOM_SCORE COMMAND\n"); - while ((curdir = readdir(pDir)) != NULL) { - pid = atoi(curdir->d_name); + while (!(ret = readdir_r(pDir, &curdir, &result)) && result != NULL) { + pid = atoi(curdir.d_name); if (pid < 1 || pid > 32768 || pid == getpid()) continue; @@ -938,7 +940,8 @@ static void show_rss(int output_type, char *output_path) static int show_map_all_new(int output_type, char *output_path) { DIR *pDir = NULL; - struct dirent *curdir; + struct dirent curdir; + struct dirent *result; unsigned int pid; mapinfo *milist; geminfo *glist; @@ -962,6 +965,8 @@ static int show_map_all_new(int output_type, char *output_path) FILE *output_file = NULL; int oom_score_adj; + int r; + pDir = opendir("/proc"); if (pDir == NULL) { fprintf(stderr, "cannot read directory /proc.\n"); @@ -994,8 +999,8 @@ static int show_map_all_new(int output_type, char *output_path) " 3D GEM(PSS) SWAP COMMAND\n"); } - while ((curdir = readdir(pDir)) != NULL) { - pid = atoi(curdir->d_name); + while (!(r = readdir_r(pDir, &curdir, &result)) && result != NULL) { + pid = atoi(curdir.d_name); if (pid < 1 || pid > 32768 || pid == getpid()) continue; @@ -1248,27 +1253,27 @@ int main(int argc, char *argv[]) if (argc > 1) { check_kernel_version(); - if (!strcmp(argv[1], "-r")) { + if (!strncmp(argv[1], "-r", strlen("-r")+1)) { if (argc >= 3) show_rss(OUTPUT_FILE, argv[2]); else show_rss(OUTPUT_UART, NULL); usage = 0; - } else if (!strcmp(argv[1], "-s")) { + } else if (!strncmp(argv[1], "-s", strlen("-s")+1)) { sum = 1; if (argc == 3 && atoi(argv[2]) > 0) { show_map_new(atoi(argv[2])); usage = 0; } - } else if (!strcmp(argv[1], "-a")) { + } else if (!strncmp(argv[1], "-a", strlen("-a")+1)) { verbos = 0; show_map_all_new(OUTPUT_UART, NULL); usage = 0; - } else if (!strcmp(argv[1], "-v")) { + } else if (!strncmp(argv[1], "-v", strlen("-v")+1)) { verbos = 1; show_map_all_new(OUTPUT_UART, NULL); usage = 0; - } else if (!strcmp(argv[1], "-f")) { + } else if (!strncmp(argv[1], "-f", strlen("-f")+1)) { if (argc >= 3) { verbos = 1; show_map_all_new(OUTPUT_FILE, argv[2]); -- 2.7.4 From d50e87699cad36fe5084c86d8c2f23a691efa783 Mon Sep 17 00:00:00 2001 From: "Minyoung, Song" Date: Fri, 11 Mar 2016 10:26:44 +0900 Subject: [PATCH 06/14] Fixed gem_info parsing logic to find and sum from all rows of gem_info Signed-off-by: Minyoung, Song Change-Id: Ia4af00cfa8542352d6bc2f9a8b096ccb8e99ce82 --- memps.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/memps.c b/memps.c index ed5a1e4..a91a1fa 100644 --- a/memps.c +++ b/memps.c @@ -228,10 +228,24 @@ static geminfo *read_geminfo(FILE *fp) } +static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist) +{ + geminfo *gi; + for (gi = gilist; gi; ) { + if (gi->tgid == tgid) + return gi; + + gi = gi->next; + } + return NULL; +} + static geminfo *load_geminfo(void) { geminfo *ginfo; geminfo *gilist = NULL; + geminfo *exist_ginfo = NULL; + FILE *drm_fp; char line[BUF_MAX]; @@ -281,6 +295,11 @@ static geminfo *load_geminfo(void) gilist->rss_size += ginfo->rss_size; free(ginfo); continue; + } else if(gilist && ((exist_ginfo = find_geminfo(ginfo->tgid, gilist)) != NULL)) { + exist_ginfo->pss_size += ginfo->pss_size; + exist_ginfo->rss_size += ginfo->rss_size; + free(ginfo); + continue; } ginfo->next = gilist; gilist = ginfo; @@ -702,18 +721,6 @@ mapinfo *load_maps(int pid) return milist; } -static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist) -{ - geminfo *gi; - for (gi = gilist; gi; ) { - if (gi->tgid == tgid) - return gi; - - gi = gi->next; - } - return NULL; -} - static void init_trib_mapinfo(trib_mapinfo *tmi) { if (!tmi) -- 2.7.4 From 7e75a143f988a643d75490453108ea020038c0b1 Mon Sep 17 00:00:00 2001 From: "Minyoung, Song" Date: Tue, 22 Mar 2016 12:22:43 +0900 Subject: [PATCH 07/14] Modified licensing header Signed-off-by: Minyoung, Song Change-Id: I0388c60d0e30aa2fb5a47101a52f268d5f56ccbd --- memps.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/memps.c b/memps.c index a91a1fa..489c418 100644 --- a/memps.c +++ b/memps.c @@ -1,15 +1,17 @@ -/* - Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License -*/ +/* Copyright 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include -- 2.7.4 From 99115c352f766b04639911693ee9b5fe97ee2531 Mon Sep 17 00:00:00 2001 From: "Minyoung, Song" Date: Wed, 30 Mar 2016 11:14:32 +0900 Subject: [PATCH 08/14] Bug fix for guaranteeing NULL-termination of strncpy Change-Id: I8ef500d9007f0b151fcc5558cc5faab84dbb9d7c Signed-off-by: Minyoung, Song --- memps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/memps.c b/memps.c index 489c418..c6903da 100644 --- a/memps.c +++ b/memps.c @@ -345,7 +345,7 @@ mapinfo *read_mapinfo(char** smaps, int rest_line) mi->perm[3] = line[21]; /* may share or private */ if (len < 50) - strncpy(mi->name, "[anon]", strlen("[anon]")); + strncpy(mi->name, "[anon]", strlen("[anon]")+1); else strncpy(mi->name, line + 49, len); -- 2.7.4 From 9c665915d9aecedf99a05fb1cf48622311bb4d9a Mon Sep 17 00:00:00 2001 From: sungguk Date: Wed, 6 Jul 2016 19:33:01 +0900 Subject: [PATCH 09/14] Modified kernel version check to support 4.x kernel smaps Change-Id: I0878424145df4920ef09d4f4b659808ddc26c874 Signed-off-by: sungguk --- memps.c | 47 ++++++++++++++++++++++++++++++----------------- packaging/memps.spec | 2 +- 2 files changed, 31 insertions(+), 18 deletions(-) mode change 100644 => 100755 memps.c diff --git a/memps.c b/memps.c old mode 100644 new mode 100755 index c6903da..2cd4d79 --- a/memps.c +++ b/memps.c @@ -1230,26 +1230,39 @@ void check_kernel_version(void) ret = uname(&buf); if (!ret) { - if (buf.release[0] == '3') { - char *pch; - char str[3]; - int sub_version; - pch = strstr(buf.release, "."); - strncpy(str, pch+1, 2); - sub_version = atoi(str); - + char *pch; + char str[3]; + int sub_version; + pch = strstr(buf.release, "."); + strncpy(str, pch+1, 2); + sub_version = atoi(str); + + if (buf.release[0] >= '4') { + if (sub_version >= 4) + ignore_smaps_field = 11; + /* Referenced, Anonymous, AnonHugePages, Shared_Hugetlb, + * Private_Hugetlb, Swap, SwapPss, KernelPageSize, + * MMUPageSize, Locked, VmFlags */ + else if (sub_version == 3) + ignore_smaps_field = 9; + /* Referenced, Anonymous, AnonHugePages, Swap, SwapPss, + * KernelPageSize, MMUPageSize, Locked, VmFlags */ + else + ignore_smaps_field = 8; + /* Referenced, Anonymous, AnonHugePages, Swap, + * KernelPageSize, MMUPageSize, Locked, VmFlags */ + } else if (buf.release[0] == '3') { if (sub_version >= 10) - ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages, - Swap, KernelPageSize, MMUPageSize, - Locked, VmFlags */ - + ignore_smaps_field = 8; + /* Referenced, Anonymous, AnonHugePages, Swap, + * KernelPageSize, MMUPageSize, Locked, VmFlags */ else - ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages, - Swap, KernelPageSize, MMUPageSize, - Locked */ + ignore_smaps_field = 7; + /* Referenced, Anonymous, AnonHugePages, Swap, + * KernelPageSize, MMUPageSize, Locked */ } else { - ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize, - MMUPageSize */ + ignore_smaps_field = 4; + /* Referenced, Swap, KernelPageSize, MMUPageSize */ } } } diff --git a/packaging/memps.spec b/packaging/memps.spec index d7142a8..d98f018 100644 --- a/packaging/memps.spec +++ b/packaging/memps.spec @@ -1,6 +1,6 @@ Name: memps Summary: Tool to summarize memory usage of processes, tmpfs and graphics memory usage -Version: 0.1.8 +Version: 0.1.9 Release: 0 Group: System/Utilities License: Apache-2.0 -- 2.7.4 From cef39eff476fb88918d365f9536b9953b2413ff5 Mon Sep 17 00:00:00 2001 From: Hyeongsik Min Date: Thu, 20 Oct 2016 22:20:11 +0900 Subject: [PATCH 10/14] Refactoring smaps parsing logic To be compatible with various kernel version Change-Id: Ie494cebc8581612271acb6eb584437563be1017a Signed-off-by: Hyeongsik Min --- memps.c | 147 +++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 86 insertions(+), 61 deletions(-) mode change 100755 => 100644 memps.c diff --git a/memps.c b/memps.c old mode 100755 new mode 100644 index 2cd4d79..bde83d6 --- a/memps.c +++ b/memps.c @@ -110,7 +110,7 @@ struct geminfo { unsigned hcount; }; -static int ignore_smaps_field; +static int smaps_field_lcnt; static int sum; static int verbos; @@ -313,19 +313,20 @@ static geminfo *load_geminfo(void) } -/* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so - * 012345678901234567890123456789012345678901234567890123456789 - * 0 1 2 3 4 5 +/* b6e82000-b6e83000 rw-p 00020000 b3:19 714 /usr/lib/ld-2.20-2014.11.so : TM1 + * 7fae2e4b2000-7fae2e4b3000 r--p 00021000 fe:01 603 /usr/lib64/ld-2.20-2014.11.so : x86-64 Emulator + * 7f9389d000-7f9389e000 rw-p 0001f000 b3:12 618 /usr/lib64/ld-2.20-2014.11.so : Note4 + * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + * 0 1 2 3 4 5 6 7 */ - -mapinfo *read_mapinfo(char** smaps, int rest_line) +mapinfo *read_mapinfo(char** smaps, int line_cnt) { char* line; mapinfo *mi; int len; int tmp; - if ((line = cgets(smaps)) == 0) + if ((--line_cnt <= 0) || (line = cgets(smaps)) == 0) return 0; len = strlen(line); @@ -349,41 +350,15 @@ mapinfo *read_mapinfo(char** smaps, int rest_line) else strncpy(mi->name, line + 49, len); - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Size: %d kB", &mi->size) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Rss: %d kB", &mi->rss) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Pss: %d kB", &mi->pss) == 1) - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1) - goto oops; - - while (rest_line-- && (line = cgets(smaps))) { - if (sscanf(line, "Swap: %d kB", &tmp) == 1) { - mi->swap = tmp; - //rest_line++; - } - if (sscanf(line, "PSwap: %d kB", &tmp) == 1) - rest_line++; + while (line_cnt-- && (line = cgets(smaps))) { + if (sscanf(line, "Size: %d kB", &mi->size) == 1) {} + else if (sscanf(line, "Rss: %d kB", &mi->rss) == 1) {} + else if (sscanf(line, "Pss: %d kB", &mi->pss) == 1) {} + else if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) == 1) {} + else if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) == 1) {} + else if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) == 1) {} + else if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) == 1) {} + else if (sscanf(line, "Swap: %d kB", &mi->swap) == 1) {} } return mi; @@ -693,7 +668,7 @@ mapinfo *load_maps(int pid) if (smaps == NULL) return 0; - while ((mi = read_mapinfo(&smaps, ignore_smaps_field)) != 0) { + while ((mi = read_mapinfo(&smaps, smaps_field_lcnt)) != 0) { if (milist) { if ((!strcmp(mi->name, milist->name) && (mi->name[0] != '['))) { @@ -1222,7 +1197,7 @@ static int show_map_new(int pid) return 1; } -void check_kernel_version(void) +int get_fixed_smaps_lcnt(void) { struct utsname buf; int ret; @@ -1239,32 +1214,82 @@ void check_kernel_version(void) if (buf.release[0] >= '4') { if (sub_version >= 4) - ignore_smaps_field = 11; - /* Referenced, Anonymous, AnonHugePages, Shared_Hugetlb, + ret = 18; + /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + * Private_Clean, Private_Drity, + * Referenced, Anonymous, AnonHugePages, Shared_Hugetlb, * Private_Hugetlb, Swap, SwapPss, KernelPageSize, * MMUPageSize, Locked, VmFlags */ else if (sub_version == 3) - ignore_smaps_field = 9; - /* Referenced, Anonymous, AnonHugePages, Swap, SwapPss, - * KernelPageSize, MMUPageSize, Locked, VmFlags */ + ret = 16; + /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + * Private_Clean, Private_Drity, + * Referenced, Anonymous, AnonHugePages, Swap, SwapPss, + * KernelPageSize, MMUPageSize, Locked, VmFlags */ else - ignore_smaps_field = 8; - /* Referenced, Anonymous, AnonHugePages, Swap, - * KernelPageSize, MMUPageSize, Locked, VmFlags */ + ret = 15; + /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + * Private_Clean, Private_Drity, + * Referenced, Anonymous, AnonHugePages, Swap, + * KernelPageSize, MMUPageSize, Locked, VmFlags */ } else if (buf.release[0] == '3') { if (sub_version >= 10) - ignore_smaps_field = 8; - /* Referenced, Anonymous, AnonHugePages, Swap, - * KernelPageSize, MMUPageSize, Locked, VmFlags */ + ret = 15; + /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + * Private_Clean, Private_Drity, + * Referenced, Anonymous, AnonHugePages, Swap, + * KernelPageSize, MMUPageSize, Locked, VmFlags */ else - ignore_smaps_field = 7; - /* Referenced, Anonymous, AnonHugePages, Swap, - * KernelPageSize, MMUPageSize, Locked */ + ret = 14; + /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + * Private_Clean, Private_Drity, + * Referenced, Anonymous, AnonHugePages, Swap, + * KernelPageSize, MMUPageSize, Locked */ } else { - ignore_smaps_field = 4; - /* Referenced, Swap, KernelPageSize, MMUPageSize */ + ret = 11; + /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + * Private_Clean, Private_Drity, + * Referenced, Swap, KernelPageSize, MMUPageSize */ } } + return ret; +} + +int get_smaps_lcnt(void) +{ + char *buf, *buf_start, *line; + char cmd[64] = "/proc/self/smaps"; + int line_count = 0; + long start_addr, end_addr; + unsigned long pg_off; + int major, minor; + char flags[4]; + + buf_start = cread(cmd); + if (buf_start == NULL) { + goto error; + } + + buf = buf_start; + if ((line = cgets(&buf)) == 0) + goto error; + + if (sscanf(line, "%lx-%lx %s %lx %x:%x", + &start_addr, &end_addr, flags, &pg_off, &major, &minor) != 6) { + goto error; + } + + while ((line = cgets(&buf)) != 0 ) { + line_count++; + if (sscanf(line, "%lx-%lx %s %lx %x:%x", + &start_addr, &end_addr, flags, &pg_off, &major, &minor) == 6) + break; + } + + return line_count; + +error: + return get_fixed_smaps_lcnt(); } int main(int argc, char *argv[]) @@ -1273,7 +1298,7 @@ int main(int argc, char *argv[]) sum = 0; if (argc > 1) { - check_kernel_version(); + smaps_field_lcnt = get_smaps_lcnt(); if (!strncmp(argv[1], "-r", strlen("-r")+1)) { if (argc >= 3) -- 2.7.4 From 1256cc0af4bc61bdf00ed0785aede553c92a6f96 Mon Sep 17 00:00:00 2001 From: "Minyoung, Song" Date: Mon, 31 Oct 2016 16:28:52 +0900 Subject: [PATCH 11/14] Fix smaps parsing issue on 64bit kernel Signed-off-by: Minyoung, Song Change-Id: Ib1b439805dab0475fce70f1fa41906691fd5790a --- memps.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/memps.c b/memps.c index bde83d6..db6a938 100644 --- a/memps.c +++ b/memps.c @@ -68,8 +68,8 @@ enum { struct mapinfo { mapinfo *next; - unsigned start; - unsigned end; + unsigned long start; + unsigned long end; unsigned size; unsigned swap; unsigned rss; @@ -314,8 +314,8 @@ static geminfo *load_geminfo(void) /* b6e82000-b6e83000 rw-p 00020000 b3:19 714 /usr/lib/ld-2.20-2014.11.so : TM1 + * 7f9389d000-7f9389e000 rw-p 0001f000 b3:12 618 /usr/lib64/ld-2.20-2014.11.so : TM2 * 7fae2e4b2000-7fae2e4b3000 r--p 00021000 fe:01 603 /usr/lib64/ld-2.20-2014.11.so : x86-64 Emulator - * 7f9389d000-7f9389e000 rw-p 0001f000 b3:12 618 /usr/lib64/ld-2.20-2014.11.so : Note4 * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 * 0 1 2 3 4 5 6 7 */ @@ -324,7 +324,7 @@ mapinfo *read_mapinfo(char** smaps, int line_cnt) char* line; mapinfo *mi; int len; - int tmp; + int n; if ((--line_cnt <= 0) || (line = cgets(smaps)) == 0) return 0; @@ -337,18 +337,11 @@ mapinfo *read_mapinfo(char** smaps, int line_cnt) if (mi == 0) return 0; - mi->start = strtoul(line, 0, 16); - mi->end = strtoul(line + 9, 0, 16); + n = sscanf(line, "%lx-%lx %s %*s %*s %*s %s", + &mi->start, &mi->end, &mi->perm, &mi->name); - mi->perm[0] = line[18]; /* read */ - mi->perm[1] = line[19]; /* write */ - mi->perm[2] = line[20]; /* execute */ - mi->perm[3] = line[21]; /* may share or private */ - - if (len < 50) + if (n == 3) strncpy(mi->name, "[anon]", strlen("[anon]")+1); - else - strncpy(mi->name, line + 49, len); while (line_cnt-- && (line = cgets(smaps))) { if (sscanf(line, "Size: %d kB", &mi->size) == 1) {} @@ -1127,8 +1120,8 @@ static int show_map_new(int pid) unsigned private_dirty = 0; unsigned private_clean = 0; unsigned pss = 0; - unsigned start = 0; - unsigned end = 0; + unsigned long start = 0; + unsigned long end = 0; unsigned private_clean_total = 0; unsigned private_dirty_total = 0; unsigned shared_clean_total = 0; @@ -1144,7 +1137,7 @@ static int show_map_new(int pid) if (!sum) { printf(" S(CODE) S(DATA) P(CODE) P(DATA) ADDR(start-end)" - "OBJECT NAME\n"); + " OBJECT NAME\n"); printf("-------- -------- -------- -------- -----------------" "------------------------------\n"); } else { @@ -1176,9 +1169,9 @@ static int show_map_new(int pid) duplication = 0; if (!sum) { - printf("%8d %8d %8d %8d %08x-%08x %s\n", - shared_clean, shared_dirty, private_clean, private_dirty, - start, end, mi->name); + printf("%8d %8d %8d %8d %08lx-%08lx %s\n", + shared_clean, shared_dirty, private_clean, + private_dirty, start, end, mi->name); } shared_clean = 0; shared_dirty = 0; -- 2.7.4 From 6c7b7d54f013217213dc6672dd7bfa020167d45c Mon Sep 17 00:00:00 2001 From: Hyeongsik Min Date: Fri, 4 Nov 2016 10:42:18 +0900 Subject: [PATCH 12/14] Fix smaps parsing on 64bit kernel In previous patch, there is a issue that memps truncates pathname when it includes blank space. Change-Id: I72770bc84c4f11b114c2dcdaad69314bacb113e4 Signed-off-by: Hyeongsik Min --- memps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/memps.c b/memps.c index db6a938..84f4f06 100644 --- a/memps.c +++ b/memps.c @@ -337,7 +337,7 @@ mapinfo *read_mapinfo(char** smaps, int line_cnt) if (mi == 0) return 0; - n = sscanf(line, "%lx-%lx %s %*s %*s %*s %s", + n = sscanf(line, "%lx-%lx %s %*s %*s %*s %[^\n]", &mi->start, &mi->end, &mi->perm, &mi->name); if (n == 3) -- 2.7.4 From 872b83ece7991deb0de0d6e85554a5880bde5540 Mon Sep 17 00:00:00 2001 From: Hyeongsik Min Date: Mon, 7 Nov 2016 21:33:41 +0900 Subject: [PATCH 13/14] Fix build warnings Change-Id: Icf8062b8f35b788a9d58ed2a7d207ef80a98bdaa Signed-off-by: Hyeongsik Min --- memps.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/memps.c b/memps.c index 84f4f06..deee6d3 100644 --- a/memps.c +++ b/memps.c @@ -78,8 +78,8 @@ struct mapinfo { unsigned shared_dirty; unsigned private_clean; unsigned private_dirty; - char perm[4]; - char name[1]; + char *perm; + char *name; }; /* classify normal, graphic and other devices memory */ @@ -333,15 +333,15 @@ mapinfo *read_mapinfo(char** smaps, int line_cnt) if (len < 1) return 0; - mi = malloc(sizeof(mapinfo) + len + 16); + mi = malloc(sizeof(mapinfo)); if (mi == 0) return 0; - n = sscanf(line, "%lx-%lx %s %*s %*s %*s %[^\n]", + n = sscanf(line, "%lx-%lx %ms %*s %*s %*s %m[^\n]", &mi->start, &mi->end, &mi->perm, &mi->name); - if (n == 3) - strncpy(mi->name, "[anon]", strlen("[anon]")+1); + if (n == 3 && !mi->name) + mi->name = strndup("[anon]", strlen("[anon]")); while (line_cnt-- && (line = cgets(smaps))) { if (sscanf(line, "Size: %d kB", &mi->size) == 1) {} @@ -355,10 +355,6 @@ mapinfo *read_mapinfo(char** smaps, int line_cnt) } return mi; - oops: - printf("mi get error\n"); - free(mi); - return 0; } static unsigned total_gem_memory(void) @@ -674,12 +670,10 @@ mapinfo *load_maps(int pid) milist->private_clean += mi->private_clean; milist->private_dirty += mi->private_dirty; - milist->perm[0] = mi->perm[0]; - milist->perm[1] = mi->perm[1]; - milist->perm[2] = mi->perm[2]; - milist->perm[3] = mi->perm[3]; milist->end = mi->end; strncpy(milist->perm, mi->perm, 4); + free(mi->perm); + free(mi->name); free(mi); continue; } @@ -762,6 +756,8 @@ get_trib_mapinfo(unsigned int tgid, mapinfo *milist, temp = mi; mi = mi->next; + free(temp->perm); + free(temp->name); free(temp); temp = NULL; } -- 2.7.4 From d235b390f1dfa731eff43a391bdee6ed5dd46585 Mon Sep 17 00:00:00 2001 From: Hyeongsik Min Date: Thu, 10 Nov 2016 16:47:29 +0900 Subject: [PATCH 14/14] Fix fixed value of smaps line count Vma line was missed, so we need to add 1 on each hardcoded value. Change-Id: I735bc0aaa312e034655534a78b456a35c5ce9fdb Signed-off-by: Hyeongsik Min --- memps.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/memps.c b/memps.c index deee6d3..12de206 100644 --- a/memps.c +++ b/memps.c @@ -110,7 +110,7 @@ struct geminfo { unsigned hcount; }; -static int smaps_field_lcnt; +static int smaps_lcnt; static int sum; static int verbos; @@ -657,7 +657,7 @@ mapinfo *load_maps(int pid) if (smaps == NULL) return 0; - while ((mi = read_mapinfo(&smaps, smaps_field_lcnt)) != 0) { + while ((mi = read_mapinfo(&smaps, smaps_lcnt)) != 0) { if (milist) { if ((!strcmp(mi->name, milist->name) && (mi->name[0] != '['))) { @@ -1203,40 +1203,40 @@ int get_fixed_smaps_lcnt(void) if (buf.release[0] >= '4') { if (sub_version >= 4) - ret = 18; - /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + ret = 19; + /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty, * Private_Clean, Private_Drity, * Referenced, Anonymous, AnonHugePages, Shared_Hugetlb, * Private_Hugetlb, Swap, SwapPss, KernelPageSize, * MMUPageSize, Locked, VmFlags */ else if (sub_version == 3) - ret = 16; - /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + ret = 17; + /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty, * Private_Clean, Private_Drity, * Referenced, Anonymous, AnonHugePages, Swap, SwapPss, * KernelPageSize, MMUPageSize, Locked, VmFlags */ else - ret = 15; - /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + ret = 16; + /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty, * Private_Clean, Private_Drity, * Referenced, Anonymous, AnonHugePages, Swap, * KernelPageSize, MMUPageSize, Locked, VmFlags */ } else if (buf.release[0] == '3') { if (sub_version >= 10) - ret = 15; - /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + ret = 16; + /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty, * Private_Clean, Private_Drity, * Referenced, Anonymous, AnonHugePages, Swap, * KernelPageSize, MMUPageSize, Locked, VmFlags */ else - ret = 14; - /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + ret = 15; + /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty, * Private_Clean, Private_Drity, * Referenced, Anonymous, AnonHugePages, Swap, * KernelPageSize, MMUPageSize, Locked */ } else { - ret = 11; - /* Size, Rss, Pss, Shared_Clean, Shard_Dirty, + ret = 12; + /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty, * Private_Clean, Private_Drity, * Referenced, Swap, KernelPageSize, MMUPageSize */ } @@ -1287,7 +1287,7 @@ int main(int argc, char *argv[]) sum = 0; if (argc > 1) { - smaps_field_lcnt = get_smaps_lcnt(); + smaps_lcnt = get_smaps_lcnt(); if (!strncmp(argv[1], "-r", strlen("-r")+1)) { if (argc >= 3) -- 2.7.4