1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdlib.h>
18 #include <unistd.h>
19 
20 #include <pagemap/pagemap.h>
21 
22 #define SIMPLEQ_INSERT_SIMPLEQ_TAIL(head_a, head_b)             \
23     do {                                                        \
24         if (!SIMPLEQ_EMPTY(head_b)) {                           \
25             if ((head_a)->sqh_first == NULL)                    \
26                 (head_a)->sqh_first = (head_b)->sqh_first;      \
27             *(head_a)->sqh_last = (head_b)->sqh_first;          \
28             (head_a)->sqh_last = (head_b)->sqh_last;            \
29         }                                                       \
30     } while (/*CONSTCOND*/0)
31 
32 /* We use an array of int to store the references to a given offset in the swap
33    1 GiB swap means 512KiB size array: offset are the index */
34 typedef unsigned short pm_pswap_refcount_t;
35 struct pm_proportional_swap {
36     unsigned int array_size;
37     pm_pswap_refcount_t *offset_array;
38 };
39 
pm_memusage_zero(pm_memusage_t * mu)40 void pm_memusage_zero(pm_memusage_t *mu) {
41     mu->vss = mu->rss = mu->pss = mu->uss = mu->swap = 0;
42     mu->p_swap = NULL;
43     SIMPLEQ_INIT(&mu->swap_offset_list);
44 }
45 
pm_memusage_pswap_init_handle(pm_memusage_t * mu,pm_proportional_swap_t * p_swap)46 void pm_memusage_pswap_init_handle(pm_memusage_t *mu, pm_proportional_swap_t *p_swap) {
47     mu->p_swap = p_swap;
48 }
49 
pm_memusage_add(pm_memusage_t * a,pm_memusage_t * b)50 void pm_memusage_add(pm_memusage_t *a, pm_memusage_t *b) {
51     a->vss += b->vss;
52     a->rss += b->rss;
53     a->pss += b->pss;
54     a->uss += b->uss;
55     a->swap += b->swap;
56     SIMPLEQ_INSERT_SIMPLEQ_TAIL(&a->swap_offset_list, &b->swap_offset_list);
57 }
58 
pm_memusage_pswap_create(int swap_size)59 pm_proportional_swap_t * pm_memusage_pswap_create(int swap_size)
60 {
61     pm_proportional_swap_t *p_swap = NULL;
62 
63     p_swap = malloc(sizeof(pm_proportional_swap_t));
64     if (p_swap == NULL) {
65         fprintf(stderr, "Error allocating proportional swap.\n");
66     } else {
67         p_swap->array_size = swap_size / getpagesize();
68         p_swap->offset_array = calloc(p_swap->array_size, sizeof(pm_pswap_refcount_t));
69         if (p_swap->offset_array == NULL) {
70             fprintf(stderr, "Error allocating proportional swap offset array.\n");
71             free(p_swap);
72             p_swap = NULL;
73         }
74     }
75 
76     return p_swap;
77 }
78 
pm_memusage_pswap_destroy(pm_proportional_swap_t * p_swap)79 void pm_memusage_pswap_destroy(pm_proportional_swap_t *p_swap) {
80     if (p_swap) {
81         free(p_swap->offset_array);
82         free(p_swap);
83     }
84 }
85 
pm_memusage_pswap_add_offset(pm_memusage_t * mu,unsigned int offset)86 void pm_memusage_pswap_add_offset(pm_memusage_t *mu, unsigned int offset) {
87     pm_swap_offset_t *soff;
88 
89     if (mu->p_swap == NULL)
90         return;
91 
92     if (offset >= mu->p_swap->array_size) {
93         fprintf(stderr, "SWAP offset %d is out of swap bounds.\n", offset);
94         return;
95     }
96 
97     if (mu->p_swap->offset_array[offset] == USHRT_MAX) {
98         fprintf(stderr, "SWAP offset %d ref. count if overflowing ushort type.\n", offset);
99     } else {
100         mu->p_swap->offset_array[offset]++;
101     }
102 
103     soff = malloc(sizeof(pm_swap_offset_t));
104     if (soff) {
105         soff->offset = offset;
106         SIMPLEQ_INSERT_TAIL(&mu->swap_offset_list, soff, simpleqe);
107     }
108 }
109 
pm_memusage_pswap_get_usage(pm_memusage_t * mu,pm_swapusage_t * su)110 void pm_memusage_pswap_get_usage(pm_memusage_t *mu, pm_swapusage_t *su) {
111 
112     int pagesize = getpagesize();
113     pm_swap_offset_t *elem;
114 
115     if (su == NULL)
116         return;
117 
118     su->proportional = su->unique = 0;
119     SIMPLEQ_FOREACH(elem, &mu->swap_offset_list, simpleqe) {
120         su->proportional += pagesize / mu->p_swap->offset_array[elem->offset];
121         su->unique += mu->p_swap->offset_array[elem->offset] == 1 ? pagesize : 0;
122     }
123 }
124 
pm_memusage_pswap_free(pm_memusage_t * mu)125 void pm_memusage_pswap_free(pm_memusage_t *mu) {
126     pm_swap_offset_t *elem = SIMPLEQ_FIRST(&mu->swap_offset_list);
127     while (elem) {
128         SIMPLEQ_REMOVE_HEAD(&mu->swap_offset_list, simpleqe);
129         free(elem);
130         elem = SIMPLEQ_FIRST(&mu->swap_offset_list);
131     }
132 }
133