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
diff --git a/libpagemap/pm_memusage.c b/libpagemap/pm_memusage.c
index ea2a003..70cfede 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 @@
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);
+ }
}