Merge "Prevent future instances of the last bug."
am: 113206fb65

* commit '113206fb6548cf95bdb91821c7b4dd384c926756':
  Prevent future instances of the last bug.
diff --git a/libpagemap/include/pagemap/pagemap.h b/libpagemap/include/pagemap/pagemap.h
index 9063b1e..4de2b4b 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 @@
     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 c6a1798..301a1cc 100644
--- a/libpagemap/pm_map.c
+++ b/libpagemap/pm_map.c
@@ -42,12 +42,13 @@
     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 @@
             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 @@
 
     error = 0;
 
-out:    
+out:
     free(pagemap);
 
     return error;
@@ -101,13 +103,13 @@
     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 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);
+    }
 }
diff --git a/libpagemap/pm_process.c b/libpagemap/pm_process.c
index b8e06c1..3c5c391 100644
--- a/libpagemap/pm_process.c
+++ b/libpagemap/pm_process.c
@@ -81,6 +81,10 @@
         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 @@
 
     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;
diff --git a/librank/librank.c b/librank/librank.c
index 28322b9..5c7c64e 100644
--- a/librank/librank.c
+++ b/librank/librank.c
@@ -316,6 +316,7 @@
 
     libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *));
     libraries_count = 0; libraries_size = INIT_LIBRARIES;
+    pm_memusage_zero(&map_usage);
 
     error = pm_kernel_create(&ker);
     if (error) {
@@ -376,7 +377,7 @@
         }
     }
 
-    printf(" %6s   %6s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
+    printf(" %6s   %7s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
 
     if (has_swap) {
         printf(" %6s  ", "Swap");
@@ -390,7 +391,7 @@
     for (i = 0; i < libraries_count; i++) {
         li = libraries[i];
 
-        printf("%6zdK   %6s   %6s   %6s   %6s  ", li->total_usage.pss / 1024, "", "", "", "");
+        printf("%6zdK   %7s   %6s   %6s   %6s  ", li->total_usage.pss / 1024, "", "", "", "");
         if (has_swap) {
             printf(" %6s  ", "");
         }
@@ -402,7 +403,7 @@
         for (j = 0; j < li->mappings_count; j++) {
             mi = li->mappings[j];
             pi = mi->proc;
-            printf(   " %6s  %6zdK  %6zdK  %6zdK  %6zdK  ", "",
+            printf(   " %6s  %7zdK  %6zdK  %6zdK  %6zdK  ", "",
                 mi->usage.vss / 1024,
                 mi->usage.rss / 1024,
                 mi->usage.pss / 1024,
diff --git a/procrank/procrank.c b/procrank/procrank.c
index 1728467..881f110 100644
--- a/procrank/procrank.c
+++ b/procrank/procrank.c
@@ -48,9 +48,26 @@
 int (*compfn)(const void *a, const void *b);
 static int order;
 
-void print_mem_info() {
+enum {
+    MEMINFO_TOTAL,
+    MEMINFO_FREE,
+    MEMINFO_BUFFERS,
+    MEMINFO_CACHED,
+    MEMINFO_SHMEM,
+    MEMINFO_SLAB,
+    MEMINFO_SWAP_TOTAL,
+    MEMINFO_SWAP_FREE,
+    MEMINFO_ZRAM_TOTAL,
+    MEMINFO_MAPPED,
+    MEMINFO_VMALLOC_USED,
+    MEMINFO_PAGE_TABLES,
+    MEMINFO_KERNEL_STACK,
+    MEMINFO_COUNT
+};
+
+void get_mem_info(uint64_t mem[]) {
     char buffer[1024];
-    int numFound = 0;
+    unsigned int numFound = 0;
 
     int fd = open("/proc/meminfo", O_RDONLY);
 
@@ -75,6 +92,13 @@
             "Cached:",
             "Shmem:",
             "Slab:",
+            "SwapTotal:",
+            "SwapFree:",
+            "ZRam:",            /* not read from meminfo but from /sys/block/zram0 */
+            "Mapped:",
+            "VmallocUsed:",
+            "PageTables:",
+            "KernelStack:",
             NULL
     };
     static const int tagsLen[] = {
@@ -84,12 +108,18 @@
             7,
             6,
             5,
+            10,
+            9,
+            5,
+            7,
+            12,
+            11,
+            12,
             0
     };
-    uint64_t mem[] = { 0, 0, 0, 0, 0, 0 };
 
     char* p = buffer;
-    while (*p && numFound < 6) {
+    while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
         int i = 0;
         while (tags[i]) {
             if (strncmp(p, tags[i], tagsLen[i]) == 0) {
@@ -112,10 +142,6 @@
         }
         if (*p) p++;
     }
-
-    printf("RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64 "K buffers, "
-            "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64 "K slab\n",
-            mem[0], mem[1], mem[2], mem[3], mem[4], mem[5]);
 }
 
 int main(int argc, char *argv[]) {
@@ -127,9 +153,12 @@
     uint64_t total_pss;
     uint64_t total_uss;
     uint64_t total_swap;
+    uint64_t total_pswap;
+    uint64_t total_uswap;
+    uint64_t total_zswap;
     char cmdline[256]; // this must be within the range of int
     int error;
-    bool has_swap = false;
+    bool has_swap = false, has_zram = false;
     uint64_t required_flags = 0;
     uint64_t flags_mask = 0;
 
@@ -141,6 +170,12 @@
     int arg;
     size_t i, j;
 
+    uint64_t mem[MEMINFO_COUNT] = { };
+    pm_proportional_swap_t *p_swap;
+    int fd, len;
+    char buffer[1024];
+    float zram_cr = 0.0;
+
     signal(SIGPIPE, SIG_IGN);
     compfn = &sort_by_pss;
     order = -1;
@@ -164,6 +199,9 @@
         exit(EXIT_FAILURE);
     }
 
+    get_mem_info(mem);
+    p_swap = pm_memusage_pswap_create(mem[MEMINFO_SWAP_TOTAL] * 1024);
+
     error = pm_kernel_create(&ker);
     if (error) {
         fprintf(stderr, "Error creating kernel interface -- "
@@ -191,6 +229,7 @@
         }
         procs[i]->pid = pids[i];
         pm_memusage_zero(&procs[i]->usage);
+        pm_memusage_pswap_init_handle(&procs[i]->usage, p_swap);
         error = pm_process_create(ker, pids[i], &proc);
         if (error) {
             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
@@ -237,16 +276,37 @@
 
     qsort(procs, num_procs, sizeof(procs[0]), compfn);
 
+    if (has_swap) {
+        fd = open("/sys/block/zram0/mem_used_total", O_RDONLY);
+        if (fd >= 0) {
+            len = read(fd, buffer, sizeof(buffer)-1);
+            close(fd);
+            if (len > 0) {
+                buffer[len] = 0;
+                mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024;
+                zram_cr = (float) mem[MEMINFO_ZRAM_TOTAL] /
+                        (mem[MEMINFO_SWAP_TOTAL] - mem[MEMINFO_SWAP_FREE]);
+                has_zram = true;
+            }
+        }
+    }
+
     printf("%5s  ", "PID");
     if (ws) {
-        printf("%s  %7s  %7s  ", "WRss", "WPss", "WUss");
+        printf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss");
         if (has_swap) {
-            printf("%7s  ", "WSwap");
+            printf("%7s  %7s  %7s  ", "WSwap", "WPSwap", "WUSwap");
+            if (has_zram) {
+                printf("%7s  ", "WZSwap");
+            }
         }
     } else {
         printf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss");
         if (has_swap) {
-            printf("%7s  ", "Swap");
+            printf("%7s  %7s  %7s  ", "Swap", "PSwap", "USwap");
+            if (has_zram) {
+                printf("%7s  ", "ZSwap");
+            }
         }
     }
 
@@ -255,6 +315,9 @@
     total_pss = 0;
     total_uss = 0;
     total_swap = 0;
+    total_pswap = 0;
+    total_uswap = 0;
+    total_zswap = 0;
 
     for (i = 0; i < num_procs; i++) {
         if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) {
@@ -288,7 +351,20 @@
         }
 
         if (has_swap) {
+            pm_swapusage_t su;
+
+            pm_memusage_pswap_get_usage(&procs[i]->usage, &su);
             printf("%6zuK  ", procs[i]->usage.swap / 1024);
+            printf("%6zuK  ", su.proportional / 1024);
+            printf("%6zuK  ", su.unique / 1024);
+            total_pswap += su.proportional;
+            total_uswap += su.unique;
+            pm_memusage_pswap_free(&procs[i]->usage);
+            if (has_zram) {
+                size_t zpswap = su.proportional * zram_cr;
+                printf("%6zuK  ", zpswap / 1024);
+                total_zswap += zpswap;
+            }
         }
 
         printf("%s\n", cmdline);
@@ -297,6 +373,7 @@
     }
 
     free(procs);
+    pm_memusage_pswap_destroy(p_swap);
 
     /* Print the separator line */
     printf("%5s  ", "");
@@ -308,7 +385,10 @@
     }
 
     if (has_swap) {
-        printf("%7s  ", "------");
+        printf("%7s  %7s  %7s  ", "------", "------", "------");
+        if (has_zram) {
+            printf("%7s  ", "------");
+        }
     }
 
     printf("%s\n", "------");
@@ -316,7 +396,7 @@
     /* Print the total line */
     printf("%5s  ", "");
     if (ws) {
-        printf("%7s  %6" PRIu64 "K  %" PRIu64 "K  ",
+        printf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
             "", total_pss / 1024, total_uss / 1024);
     } else {
         printf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
@@ -325,12 +405,27 @@
 
     if (has_swap) {
         printf("%6" PRIu64 "K  ", total_swap / 1024);
+        printf("%6" PRIu64 "K  ", total_pswap / 1024);
+        printf("%6" PRIu64 "K  ", total_uswap / 1024);
+        if (has_zram) {
+            printf("%6" PRIu64 "K  ", total_zswap / 1024);
+        }
     }
 
     printf("TOTAL\n");
 
     printf("\n");
-    print_mem_info();
+
+    if (has_swap) {
+        printf("ZRAM: %" PRIu64 "K physical used for %" PRIu64 "K in swap "
+                "(%" PRIu64 "K total swap)\n",
+                mem[MEMINFO_ZRAM_TOTAL], (mem[MEMINFO_SWAP_TOTAL] - mem[MEMINFO_SWAP_FREE]),
+                mem[MEMINFO_SWAP_TOTAL]);
+    }
+    printf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64 "K buffers, "
+            "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64 "K slab\n",
+            mem[MEMINFO_TOTAL], mem[MEMINFO_FREE], mem[MEMINFO_BUFFERS],
+            mem[MEMINFO_CACHED], mem[MEMINFO_SHMEM], mem[MEMINFO_SLAB]);
 
     return 0;
 }