summaryrefslogtreecommitdiff
path: root/libpagemap
diff options
context:
space:
mode:
authorThierry Strudel <tstrudel@google.com>2015-11-20 18:07:15 -0800
committerThierry Strudel <tstrudel@google.com>2015-11-30 10:10:04 -0800
commit08ea6e722b1f2d6359f54fe39c6bed82f524291c (patch)
tree9636c6575d266a0a61e344935986e839d5b3d172 /libpagemap
parent5e20c25f630fcaace1728090a43998f076b2851f (diff)
downloadextras-08ea6e722b1f2d6359f54fe39c6bed82f524291c.tar.gz
procrank: add proportional swap accounting
Shared pages are reported in each of the sharing process swapped pages. Compute a proportional swap usage to get a meaningful value of what each process has in swap. Report also process unique pages swapped out. In case ZRAM is used compute the compression ration and report the actual RAM use of the swapped pages. Bug: 25392275 Change-Id: I3a28c7812a09a02e1a604593615f5c6ad0340f9f
Diffstat (limited to 'libpagemap')
-rw-r--r--libpagemap/include/pagemap/pagemap.h30
-rw-r--r--libpagemap/pm_map.c10
-rw-r--r--libpagemap/pm_memusage.c103
-rw-r--r--libpagemap/pm_process.c9
4 files changed, 148 insertions, 4 deletions
diff --git a/libpagemap/include/pagemap/pagemap.h b/libpagemap/include/pagemap/pagemap.h
index 9063b1e1..4de2b4b6 100644
--- a/libpagemap/include/pagemap/pagemap.h
+++ b/libpagemap/include/pagemap/pagemap.h
@@ -21,9 +21,19 @@
#include <stdio.h>
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <sys/queue.h>
__BEGIN_DECLS
+typedef struct pm_proportional_swap pm_proportional_swap_t;
+
+typedef struct pm_swap_offset pm_swap_offset_t;
+
+struct pm_swap_offset {
+ unsigned int offset;
+ SIMPLEQ_ENTRY(pm_swap_offset) simpleqe;
+};
+
typedef struct pm_memusage pm_memusage_t;
/* Holds the various metrics for memory usage of a process or a mapping. */
@@ -33,12 +43,32 @@ struct pm_memusage {
size_t pss;
size_t uss;
size_t swap;
+ /* if non NULL then use swap_offset_list to compute proportional swap */
+ pm_proportional_swap_t *p_swap;
+ SIMPLEQ_HEAD(simpleqhead, pm_swap_offset) swap_offset_list;
+};
+
+typedef struct pm_swapusage pm_swapusage_t;
+struct pm_swapusage {
+ size_t proportional;
+ size_t unique;
};
/* Clears a memusage. */
void pm_memusage_zero(pm_memusage_t *mu);
/* Adds one memusage (a) to another (b). */
void pm_memusage_add(pm_memusage_t *a, pm_memusage_t *b);
+/* Adds a swap offset */
+void pm_memusage_pswap_add_offset(pm_memusage_t *mu, unsigned int offset);
+/* Enable proportional swap computing. */
+void pm_memusage_pswap_init_handle(pm_memusage_t *mu, pm_proportional_swap_t *p_swap);
+/* Computes and return the proportional swap */
+void pm_memusage_pswap_get_usage(pm_memusage_t *mu, pm_swapusage_t *su);
+void pm_memusage_pswap_free(pm_memusage_t *mu);
+/* Initialize a proportional swap computing handle:
+ assumes only 1 swap device, total swap size of this device in bytes to be given as argument */
+pm_proportional_swap_t * pm_memusage_pswap_create(int swap_size);
+void pm_memusage_pswap_destroy(pm_proportional_swap_t *p_swap);
typedef struct pm_kernel pm_kernel_t;
typedef struct pm_process pm_process_t;
diff --git a/libpagemap/pm_map.c b/libpagemap/pm_map.c
index c6a17981..301a1cc3 100644
--- a/libpagemap/pm_map.c
+++ b/libpagemap/pm_map.c
@@ -42,12 +42,13 @@ int pm_map_usage_flags(pm_map_t *map, pm_memusage_t *usage_out,
if (error) return error;
pm_memusage_zero(&usage);
+ pm_memusage_pswap_init_handle(&usage, usage_out->p_swap);
for (i = 0; i < len; i++) {
usage.vss += map->proc->ker->pagesize;
if (!PM_PAGEMAP_PRESENT(pagemap[i]) &&
- !PM_PAGEMAP_SWAPPED(pagemap[i]))
+ !PM_PAGEMAP_SWAPPED(pagemap[i]))
continue;
if (!PM_PAGEMAP_SWAPPED(pagemap[i])) {
@@ -70,6 +71,7 @@ int pm_map_usage_flags(pm_map_t *map, pm_memusage_t *usage_out,
usage.uss += (count == 1) ? (map->proc->ker->pagesize) : (0);
} else {
usage.swap += map->proc->ker->pagesize;
+ pm_memusage_pswap_add_offset(&usage, PM_PAGEMAP_SWAP_OFFSET(pagemap[i]));
}
}
@@ -77,7 +79,7 @@ int pm_map_usage_flags(pm_map_t *map, pm_memusage_t *usage_out,
error = 0;
-out:
+out:
free(pagemap);
return error;
@@ -101,13 +103,13 @@ int pm_map_workingset(pm_map_t *map, pm_memusage_t *ws_out) {
if (error) return error;
pm_memusage_zero(&ws);
-
+
for (i = 0; i < len; i++) {
error = pm_kernel_flags(map->proc->ker, PM_PAGEMAP_PFN(pagemap[i]),
&flags);
if (error) goto out;
- if (!(flags & PM_PAGE_REFERENCED))
+ if (!(flags & PM_PAGE_REFERENCED))
continue;
error = pm_kernel_count(map->proc->ker, PM_PAGEMAP_PFN(pagemap[i]),
diff --git a/libpagemap/pm_memusage.c b/libpagemap/pm_memusage.c
index ea2a003c..70cfedec 100644
--- a/libpagemap/pm_memusage.c
+++ b/libpagemap/pm_memusage.c
@@ -14,10 +14,37 @@
* limitations under the License.
*/
+#include <stdlib.h>
+#include <unistd.h>
+
#include <pagemap/pagemap.h>
+#define SIMPLEQ_INSERT_SIMPLEQ_TAIL(head_a, head_b) \
+ do { \
+ if (!SIMPLEQ_EMPTY(head_b)) { \
+ if ((head_a)->sqh_first == NULL) \
+ (head_a)->sqh_first = (head_b)->sqh_first; \
+ *(head_a)->sqh_last = (head_b)->sqh_first; \
+ (head_a)->sqh_last = (head_b)->sqh_last; \
+ } \
+ } while (/*CONSTCOND*/0)
+
+/* We use an array of int to store the references to a given offset in the swap
+ 1 GiB swap means 512KiB size array: offset are the index */
+typedef unsigned short pm_pswap_refcount_t;
+struct pm_proportional_swap {
+ unsigned int array_size;
+ pm_pswap_refcount_t *offset_array;
+};
+
void pm_memusage_zero(pm_memusage_t *mu) {
mu->vss = mu->rss = mu->pss = mu->uss = mu->swap = 0;
+ mu->p_swap = NULL;
+ SIMPLEQ_INIT(&mu->swap_offset_list);
+}
+
+void pm_memusage_pswap_init_handle(pm_memusage_t *mu, pm_proportional_swap_t *p_swap) {
+ mu->p_swap = p_swap;
}
void pm_memusage_add(pm_memusage_t *a, pm_memusage_t *b) {
@@ -26,4 +53,80 @@ void pm_memusage_add(pm_memusage_t *a, pm_memusage_t *b) {
a->pss += b->pss;
a->uss += b->uss;
a->swap += b->swap;
+ SIMPLEQ_INSERT_SIMPLEQ_TAIL(&a->swap_offset_list, &b->swap_offset_list);
+}
+
+pm_proportional_swap_t * pm_memusage_pswap_create(int swap_size)
+{
+ pm_proportional_swap_t *p_swap = NULL;
+
+ p_swap = malloc(sizeof(pm_proportional_swap_t));
+ if (p_swap == NULL) {
+ fprintf(stderr, "Error allocating proportional swap.\n");
+ } else {
+ p_swap->array_size = swap_size / getpagesize();
+ p_swap->offset_array = calloc(p_swap->array_size, sizeof(pm_pswap_refcount_t));
+ if (p_swap->offset_array == NULL) {
+ fprintf(stderr, "Error allocating proportional swap offset array.\n");
+ free(p_swap);
+ p_swap = NULL;
+ }
+ }
+
+ return p_swap;
+}
+
+void pm_memusage_pswap_destroy(pm_proportional_swap_t *p_swap) {
+ if (p_swap) {
+ free(p_swap->offset_array);
+ free(p_swap);
+ }
+}
+
+void pm_memusage_pswap_add_offset(pm_memusage_t *mu, unsigned int offset) {
+ pm_swap_offset_t *soff;
+
+ if (mu->p_swap == NULL)
+ return;
+
+ if (offset > mu->p_swap->array_size) {
+ fprintf(stderr, "SWAP offset %d is out of swap bounds.\n", offset);
+ return;
+ } else {
+ if (mu->p_swap->offset_array[offset] == USHRT_MAX) {
+ fprintf(stderr, "SWAP offset %d ref. count if overflowing ushort type.\n", offset);
+ } else {
+ mu->p_swap->offset_array[offset]++;
+ }
+ }
+
+ soff = malloc(sizeof(pm_swap_offset_t));
+ if (soff) {
+ soff->offset = offset;
+ SIMPLEQ_INSERT_TAIL(&mu->swap_offset_list, soff, simpleqe);
+ }
+}
+
+void pm_memusage_pswap_get_usage(pm_memusage_t *mu, pm_swapusage_t *su) {
+
+ int pagesize = getpagesize();
+ pm_swap_offset_t *elem;
+
+ if (su == NULL)
+ return;
+
+ su->proportional = su->unique = 0;
+ SIMPLEQ_FOREACH(elem, &mu->swap_offset_list, simpleqe) {
+ su->proportional += pagesize / mu->p_swap->offset_array[elem->offset];
+ su->unique += mu->p_swap->offset_array[elem->offset] == 1 ? pagesize : 0;
+ }
+}
+
+void pm_memusage_pswap_free(pm_memusage_t *mu) {
+ pm_swap_offset_t *elem = SIMPLEQ_FIRST(&mu->swap_offset_list);
+ while (elem) {
+ SIMPLEQ_REMOVE_HEAD(&mu->swap_offset_list, simpleqe);
+ free(elem);
+ elem = SIMPLEQ_FIRST(&mu->swap_offset_list);
+ }
}
diff --git a/libpagemap/pm_process.c b/libpagemap/pm_process.c
index 4d56428b..eee34648 100644
--- a/libpagemap/pm_process.c
+++ b/libpagemap/pm_process.c
@@ -81,6 +81,10 @@ int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out,
return -1;
pm_memusage_zero(&usage);
+ pm_memusage_pswap_init_handle(&usage, usage_out->p_swap);
+
+ pm_memusage_zero(&map_usage);
+ pm_memusage_pswap_init_handle(&map_usage, usage_out->p_swap);
for (i = 0; i < proc->num_maps; i++) {
error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask,
@@ -185,6 +189,11 @@ int pm_process_workingset(pm_process_t *proc,
if (ws_out) {
pm_memusage_zero(&ws);
+ pm_memusage_pswap_init_handle(&ws, ws_out->p_swap);
+
+ pm_memusage_zero(&map_ws);
+ pm_memusage_pswap_init_handle(&map_ws, ws_out->p_swap);
+
for (i = 0; i < proc->num_maps; i++) {
error = pm_map_workingset(proc->maps[i], &map_ws);
if (error) return error;