1 /*
2  * Parse output from tmem-list and reformat to human-readable
3  *
4  * NOTE: NEVER delete a parse call as this file documents backwards
5  * compatibility for older versions of tmem-list and we don't want to
6  * accidentally reuse an old tag
7  *
8  * Copyright (c) 2009, Dan Magenheimer, Oracle Corp.
9  */
10 
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <string.h>
14 
15 #define BUFSIZE 4096
16 #define PAGE_SIZE 4096
17 
parse(char * s,char * match)18 unsigned long long parse(char *s,char *match)
19 {
20     char *s1 = strstr(s,match);
21     unsigned long long ret;
22 
23     if ( s1 == NULL )
24         return 0LL;
25     s1 += 2;
26     if ( *s1++ != ':' )
27         return 0LL;
28     sscanf(s1,"%llu",&ret);
29     return ret;
30 }
31 
parse_hex(char * s,char * match)32 unsigned long long parse_hex(char *s,char *match)
33 {
34     char *s1 = strstr(s,match);
35     unsigned long long ret;
36 
37     if ( s1 == NULL )
38         return 0LL;
39     s1 += 2;
40     if ( *s1++ != ':' )
41         return 0LL;
42     sscanf(s1,"%llx",&ret);
43     return ret;
44 }
45 
parse2(char * s,char * match1,char * match2)46 unsigned long long parse2(char *s,char *match1, char *match2)
47 {
48     char match[3];
49     match[0] = *match1;
50     match[1] = *match2;
51     match[2] = '\0';
52     return parse(s,match);
53 }
54 
parse_string(char * s,char * match,char * buf,int len)55 void parse_string(char *s,char *match, char *buf, int len)
56 {
57     char *s1 = strstr(s,match);
58     int i;
59 
60     if ( s1 == NULL )
61         return;
62     s1 += 2;
63     if ( *s1++ != ':' )
64         return;
65     for ( i = 0; i < len; i++ )
66         *buf++ = *s1++;
67 }
68 
parse_sharers(char * s,char * match,char * buf,int len)69 void parse_sharers(char *s, char *match, char *buf, int len)
70 {
71     char *s1 = strstr(s,match);
72     char *b = buf;
73 
74     if ( s1 == NULL )
75         return;
76     while ( s1 )
77     {
78         s1 += 2;
79         if (*s1++ != ':')
80             return;
81         while (*s1 >= '0' && *s1 <= '9')
82             *b++ = *s1++;
83         *b++ = ',';
84         s1 = strstr(s1,match);
85     }
86     if ( b != buf )
87         *--b = '\0';
88 }
89 
parse_global(char * s)90 void parse_global(char *s)
91 {
92     unsigned long long total_ops = parse(s,"Tt");
93     unsigned long long errored_ops = parse(s,"Te");
94     unsigned long long failed_copies = parse(s,"Cf");
95     unsigned long long alloc_failed = parse(s,"Af");
96     unsigned long long alloc_page_failed = parse(s,"Pf");
97     unsigned long long avail_pages = parse(s,"Ta");
98     unsigned long long low_on_memory = parse(s,"Lm");
99     unsigned long long evicted_pgs = parse(s,"Et");
100     unsigned long long evict_attempts = parse(s,"Ea");
101     unsigned long long relinq_pgs = parse(s,"Rt");
102     unsigned long long relinq_attempts = parse(s,"Ra");
103     unsigned long long max_evicts_per_relinq = parse(s,"Rx");
104     unsigned long long total_flush_pool = parse(s,"Fp");
105     unsigned long long global_eph_count = parse(s,"Ec");
106     unsigned long long global_eph_max = parse(s,"Em");
107     unsigned long long obj_count = parse(s,"Oc");
108     unsigned long long obj_max = parse(s,"Om");
109     unsigned long long rtree_node_count = parse(s,"Nc");
110     unsigned long long rtree_node_max = parse(s,"Nm");
111     unsigned long long pgp_count = parse(s,"Pc");
112     unsigned long long pgp_max = parse(s,"Pm");
113     unsigned long long page_count = parse(s,"Fc");
114     unsigned long long max_page_count = parse(s,"Fm");
115     unsigned long long pcd_count = parse(s,"Sc");
116     unsigned long long max_pcd_count = parse(s,"Sm");
117     unsigned long long pcd_tot_tze_size = parse(s,"Zt");
118     unsigned long long pcd_tot_csize = parse(s,"Gz");
119     unsigned long long deduped_puts = parse(s,"Gd");
120     unsigned long long tot_good_eph_puts = parse(s,"Ep");
121 
122     printf("total tmem ops=%llu (errors=%llu) -- tmem pages avail=%llu\n",
123            total_ops, errored_ops, avail_pages);
124     printf("datastructs: objs=%llu (max=%llu) pgps=%llu (max=%llu) "
125            "nodes=%llu (max=%llu) pages=%llu (max=%llu) ",
126            obj_count, obj_max, pgp_count, pgp_max,
127            rtree_node_count, rtree_node_max,
128            page_count,max_page_count);
129     if (max_pcd_count != 0 && global_eph_count != 0 && tot_good_eph_puts != 0) {
130            printf("pcds=%llu (max=%llu) ",
131                pcd_count,max_pcd_count);
132            printf("deduped: avg=%4.2f%% (curr=%4.2f%%) ",
133                    ((deduped_puts*1.0)/tot_good_eph_puts)*100,
134                    (1.0-(pcd_count*1.0)/global_eph_count)*100);
135     }
136     if (pcd_count != 0)
137     {
138            if (pcd_tot_tze_size && (pcd_tot_tze_size < pcd_count*PAGE_SIZE))
139                printf("tze savings=%4.2f%% ",
140                    (1.0-(pcd_tot_tze_size*1.0)/(pcd_count*PAGE_SIZE))*100);
141            if (pcd_tot_csize && (pcd_tot_csize < pcd_count*PAGE_SIZE))
142                printf("compression savings=%4.2f%% ",
143                    (1.0-(pcd_tot_csize*1.0)/(pcd_count*PAGE_SIZE))*100);
144     }
145     printf("\n");
146     printf("misc: failed_copies=%llu alloc_failed=%llu alloc_page_failed=%llu "
147            "low_mem=%llu evicted=%llu/%llu relinq=%llu/%llu, "
148            "max_evicts_per_relinq=%llu, flush_pools=%llu, "
149            "eph_count=%llu, eph_max=%llu\n",
150            failed_copies, alloc_failed, alloc_page_failed, low_on_memory,
151            evicted_pgs, evict_attempts, relinq_pgs, relinq_attempts,
152            max_evicts_per_relinq, total_flush_pool,
153            global_eph_count, global_eph_max);
154 }
155 
156 #define PARSE_CYC_COUNTER(s,x,prefix) unsigned long long \
157    x##_count = parse2(s,prefix,"n"), \
158    x##_sum_cycles = parse2(s,prefix,"t"), \
159    x##_max_cycles = parse2(s,prefix,"x"), \
160    x##_min_cycles = parse2(s,prefix,"m")
161 #define PRINTF_CYC_COUNTER(x,text) \
162   if (x##_count) printf(text" avg=%llu, max=%llu, " \
163   "min=%llu, samples=%llu\n", \
164   x##_sum_cycles ? (x##_sum_cycles/x##_count) : 0, \
165   x##_max_cycles, x##_min_cycles, x##_count)
166 
parse_time_stats(char * s)167 void parse_time_stats(char *s)
168 {
169     PARSE_CYC_COUNTER(s,succ_get,"G");
170     PARSE_CYC_COUNTER(s,succ_put,"P");
171     PARSE_CYC_COUNTER(s,non_succ_get,"g");
172     PARSE_CYC_COUNTER(s,non_succ_put,"p");
173     PARSE_CYC_COUNTER(s,flush,"F");
174     PARSE_CYC_COUNTER(s,flush_obj,"O");
175     PARSE_CYC_COUNTER(s,pg_copy,"C");
176     PARSE_CYC_COUNTER(s,compress,"c");
177     PARSE_CYC_COUNTER(s,decompress,"d");
178 
179     PRINTF_CYC_COUNTER(succ_get,"succ get cycles:");
180     PRINTF_CYC_COUNTER(succ_put,"succ put cycles:");
181     PRINTF_CYC_COUNTER(non_succ_get,"failed get cycles:");
182     PRINTF_CYC_COUNTER(non_succ_put,"failed put cycles:");
183     PRINTF_CYC_COUNTER(flush,"flush cycles:");
184     PRINTF_CYC_COUNTER(flush_obj,"flush_obj cycles:");
185     PRINTF_CYC_COUNTER(pg_copy,"page copy cycles:");
186     PRINTF_CYC_COUNTER(compress,"compression cycles:");
187     PRINTF_CYC_COUNTER(decompress,"decompression cycles:");
188 }
189 
parse_client(char * s)190 void parse_client(char *s)
191 {
192     unsigned long cli_id = parse(s,"CI");
193     unsigned long weight = parse(s,"ww");
194     unsigned long cap = parse(s,"ca");
195     unsigned long compress = parse(s,"co");
196     unsigned long frozen = parse(s,"fr");
197     unsigned long long eph_count = parse(s,"Ec");
198     unsigned long long max_eph_count = parse(s,"Em");
199     unsigned long long compressed_pages = parse(s,"cp");
200     unsigned long long compressed_sum_size = parse(s,"cb");
201     unsigned long long compress_poor = parse(s,"cn");
202     unsigned long long compress_nomem = parse(s,"cm");
203     unsigned long long total_cycles = parse(s,"Tc");
204     unsigned long long succ_eph_gets = parse(s,"Ge");
205     unsigned long long succ_pers_puts = parse(s,"Pp");
206     unsigned long long succ_pers_gets = parse(s,"Gp");
207 
208     printf("domid%lu: weight=%lu,cap=%lu,compress=%d,frozen=%d,"
209            "total_cycles=%llu,succ_eph_gets=%llu,"
210            "succ_pers_puts=%llu,succ_pers_gets=%llu,"
211            "eph_count=%llu,max_eph=%llu,"
212            "compression ratio=%lu%% (samples=%llu,poor=%llu,nomem=%llu)\n",
213            cli_id, weight, cap, compress?1:0, frozen?1:0,
214            total_cycles, succ_eph_gets, succ_pers_puts, succ_pers_gets,
215            eph_count, max_eph_count,
216            compressed_pages ?  (long)((compressed_sum_size*100LL) /
217                                       (compressed_pages*PAGE_SIZE)) : 0,
218            compressed_pages, compress_poor, compress_nomem);
219 
220 }
221 
parse_pool(char * s)222 void parse_pool(char *s)
223 {
224     char pool_type[3];
225     unsigned long cli_id = parse(s,"CI");
226     unsigned long pool_id = parse(s,"PI");
227     unsigned long long pgp_count = parse(s,"Pc");
228     unsigned long long max_pgp_count = parse(s,"Pm");
229     unsigned long long obj_count = parse(s,"Oc");
230     unsigned long long max_obj_count = parse(s,"Om");
231     unsigned long long objnode_count = parse(s,"Nc");
232     unsigned long long max_objnode_count = parse(s,"Nm");
233     unsigned long long good_puts = parse(s,"ps");
234     unsigned long long puts = parse(s,"pt");
235     unsigned long long no_mem_puts = parse(s,"px");
236     unsigned long long dup_puts_flushed = parse(s,"pd");
237     unsigned long long dup_puts_replaced = parse(s,"pr");
238     unsigned long long found_gets = parse(s,"gs");
239     unsigned long long gets = parse(s,"gt");
240     unsigned long long flushs_found = parse(s,"fs");
241     unsigned long long flushs = parse(s,"ft");
242     unsigned long long flush_objs_found = parse(s,"os");
243     unsigned long long flush_objs = parse(s,"ot");
244 
245     parse_string(s,"PT",pool_type,2);
246     pool_type[2] = '\0';
247     if (pool_type[1] == 'S')
248         return; /* no need to repeat print data for shared pools */
249     printf("domid%lu,id%lu[%s]:pgp=%llu(max=%llu) obj=%llu(%llu) "
250            "objnode=%llu(%llu) puts=%llu/%llu/%llu(dup=%llu/%llu) "
251            "gets=%llu/%llu(%llu%%) "
252            "flush=%llu/%llu flobj=%llu/%llu\n",
253            cli_id, pool_id, pool_type,
254            pgp_count, max_pgp_count, obj_count, max_obj_count,
255            objnode_count, max_objnode_count,
256            good_puts, puts, no_mem_puts,
257            dup_puts_flushed, dup_puts_replaced,
258            found_gets, gets,
259            gets ? (found_gets*100LL)/gets : 0,
260            flushs_found, flushs, flush_objs_found, flush_objs);
261 
262 }
263 
parse_shared_pool(char * s)264 void parse_shared_pool(char *s)
265 {
266     char pool_type[3];
267     char buf[BUFSIZE];
268     unsigned long pool_id = parse(s,"PI");
269     unsigned long long uid0 = parse_hex(s,"U0");
270     unsigned long long uid1 = parse_hex(s,"U1");
271     unsigned long long pgp_count = parse(s,"Pc");
272     unsigned long long max_pgp_count = parse(s,"Pm");
273     unsigned long long obj_count = parse(s,"Oc");
274     unsigned long long max_obj_count = parse(s,"Om");
275     unsigned long long objnode_count = parse(s,"Nc");
276     unsigned long long max_objnode_count = parse(s,"Nm");
277     unsigned long long good_puts = parse(s,"ps");
278     unsigned long long puts = parse(s,"pt");
279     unsigned long long no_mem_puts = parse(s,"px");
280     unsigned long long dup_puts_flushed = parse(s,"pd");
281     unsigned long long dup_puts_replaced = parse(s,"pr");
282     unsigned long long found_gets = parse(s,"gs");
283     unsigned long long gets = parse(s,"gt");
284     unsigned long long flushs_found = parse(s,"fs");
285     unsigned long long flushs = parse(s,"ft");
286     unsigned long long flush_objs_found = parse(s,"os");
287     unsigned long long flush_objs = parse(s,"ot");
288 
289     parse_string(s,"PT",pool_type,2);
290     pool_type[2] = '\0';
291     parse_sharers(s,"SC",buf,BUFSIZE);
292     printf("poolid=%lu[%s] uuid=%llx.%llx, shared-by:%s: "
293            "pgp=%llu(max=%llu) obj=%llu(%llu) "
294            "objnode=%llu(%llu) puts=%llu/%llu/%llu(dup=%llu/%llu) "
295            "gets=%llu/%llu(%llu%%) "
296            "flush=%llu/%llu flobj=%llu/%llu\n",
297            pool_id, pool_type, uid0, uid1, buf,
298            pgp_count, max_pgp_count, obj_count, max_obj_count,
299            objnode_count, max_objnode_count,
300            good_puts, puts, no_mem_puts,
301            dup_puts_flushed, dup_puts_replaced,
302            found_gets, gets,
303            gets ? (found_gets*100LL)/gets : 0,
304            flushs_found, flushs, flush_objs_found, flush_objs);
305 }
306 
main(int ac,char ** av)307 int main(int ac, char **av)
308 {
309     char *p, c;
310     char buf[BUFSIZE];
311 
312     while ( (p = fgets(buf,BUFSIZE,stdin)) != NULL )
313     {
314         c = *p++;
315         if ( *p++ != '=' )
316             continue;
317         switch ( c )
318         {
319         case 'G':
320             parse_global(p);
321             break;
322         case 'T':
323             parse_time_stats(p);
324             break;
325         case 'C':
326             parse_client(p);
327             break;
328         case 'P':
329             parse_pool(p);
330             break;
331         case 'S':
332             parse_shared_pool(p);
333             break;
334         default:
335             continue;
336         }
337     }
338     return 0;
339 }
340