Advertisement
Fen1848

show-pagemap

Jun 19th, 2025
377
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 9.88 KB | Source Code | 0 0
  1. #include <stdint.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #include <getopt.h>
  9. #include <sysexits.h>
  10.  
  11. static uint64_t     const PAGE_SIZE = 4096;
  12.  
  13. static uint64_t read_pagecount( uint64_t pfn );
  14. static uint64_t read_pagecgroup( uint64_t pfn );
  15.  
  16. static int o_show_details = 0, o_show_cgroup = 0, o_print_refs = 0, o_print_map_name = 0;
  17.  
  18. struct summary_t {
  19.     uint64_t total_pages;
  20.     uint64_t total_active_pages;
  21.     uint64_t total_shared_pages;
  22.  
  23. } g_summary;
  24.  
  25.  
  26. /*  since we cannot use here C++ std::unordered_map
  27.     we shall use just huge arrays for calculation
  28. */
  29.  
  30.  
  31. typedef struct var_array_t {
  32.     uint64_t *ptr;
  33.     uint64_t size;
  34. } var_array_t;
  35.  
  36. static var_array_t a_per_cgroup_stats;
  37.  
  38. void init_array( var_array_t *a )
  39. {
  40.     a->ptr = NULL;
  41.     a->size = 0;
  42. }
  43.  
  44. void free_array( var_array_t *a )
  45. {
  46.     if( a->ptr )
  47.         free(a->ptr);
  48.  
  49.     a->size = 0;
  50. }
  51.  
  52. void put_or_append( var_array_t *a, uint64_t idx, uint64_t value )
  53. {
  54.     if( a->size < idx )
  55.     {
  56.         // realloc
  57.         uint64_t new_size = idx * 1.5;
  58.         a->ptr = realloc(a->ptr, new_size * sizeof(uint64_t));
  59.         if( NULL == a->ptr )
  60.             perror("failed to allocate buffer");
  61.         // fill tail with zeroes
  62.         memset(a->ptr + a->size, 0, (new_size - a->size) * sizeof(uint64_t));
  63.         a->size = new_size;
  64.     }
  65.     a->ptr[idx] += value;
  66. }
  67.  
  68.  
  69. static void usage()
  70. {
  71.     fprintf( stderr, "Usage: show-pagemap [options] <PID>\n" );
  72.     fprintf( stderr, "options are following:\n" );
  73.     fprintf( stderr, "\t-d|--details:                  show per page details.\n" );
  74.     fprintf( stderr, "\t-g|--cgroup:                   show cgroup refs from /proc/kpagecgroup.\n" );
  75.     fprintf( stderr, "\t-r|--refs:                     show sharing refs from /proc/kpagecount.\n" );
  76.     fprintf( stderr, "\t-n|--names:                    show map name if found.\n" );
  77. }
  78.  
  79. static void dump_page(uint64_t address, uint64_t data, const char *map_name)
  80. {
  81.     uint64_t pfn = data & 0x7fffffffffffff;
  82.     uint64_t cnt = o_print_refs ? (pfn ? read_pagecount(pfn) : 0) : 0;
  83.  
  84.     uint64_t page_is_present = (data >> 63) & 1;
  85.  
  86.     int64_t cgroup_id = o_show_cgroup ? read_pagecgroup(pfn) : -1;
  87.  
  88.     if( o_show_details )
  89.     {
  90.         printf("0x%-16lx : PFN %-16lx refs: %ld soft-dirty %ld ex-map: %ld shared %ld "
  91.             "swapped %ld present %ld", address, pfn, cnt,
  92.             (data >> 55) & 1,
  93.             (data >> 56) & 1,
  94.             (data >> 61) & 1,
  95.             (data >> 62) & 1,
  96.             page_is_present);
  97.  
  98.         if( cgroup_id != -1 )
  99.             printf(" cgroup: %ld", cgroup_id);
  100.  
  101.         if( map_name )
  102.             printf(" name: %s", map_name);
  103.        
  104.         printf("\n");
  105.     }
  106.  
  107.     g_summary.total_pages += 1;
  108.     g_summary.total_active_pages += page_is_present ? 1 : 0;
  109.     g_summary.total_shared_pages += cnt > 1 ? 1 : 0;
  110.  
  111.     if( o_show_cgroup && cgroup_id > 0 && page_is_present )
  112.     {
  113.         put_or_append(&a_per_cgroup_stats, cgroup_id, 1);
  114.     }
  115. }
  116.  
  117. void print_summary()
  118. {
  119.     printf("Summary:\n");
  120.     printf("total pages:       %16ld = %ld Kb\n", g_summary.total_pages, g_summary.total_pages * 4);
  121.     printf("total active(RSS): %16ld = %ld Kb\n", g_summary.total_active_pages, g_summary.total_active_pages * 4);
  122.     printf("total shared:      %16ld = %ld Kb\n", g_summary.total_shared_pages, g_summary.total_shared_pages * 4);
  123.  
  124.     if( o_show_cgroup && a_per_cgroup_stats.size )
  125.     {
  126.         printf("cgroup active pages:\n");
  127.         for( uint64_t i = 0; i < a_per_cgroup_stats.size; ++i )
  128.         {
  129.             if( a_per_cgroup_stats.ptr[i] )
  130.                 printf("cgroup-id:       %8ld %8ld = %ld Kb\n", i, a_per_cgroup_stats.ptr[i], a_per_cgroup_stats.ptr[i] * 4);
  131.         }
  132.     }
  133. }
  134.  
  135. void read_vma(int fd, uint64_t start, uint64_t end, const char *map_name)
  136. {
  137.     for(uint64_t i, val; start < end; start += PAGE_SIZE)
  138.     {
  139.         i = (start / PAGE_SIZE) * sizeof(uint64_t);
  140.         if(pread(fd, &val, sizeof(uint64_t), i) != sizeof(uint64_t))
  141.         {
  142.             if(errno) perror("vma pread");
  143.             break;
  144.         }
  145.         dump_page(i, val, map_name);
  146.     }
  147. }
  148.  
  149. void parse_maps( const char *maps_file, const char *pagemap_file )
  150. {
  151.     int maps = open(maps_file, O_RDONLY);
  152.     if(maps < 0)
  153.         perror("open /proc/maps failed");
  154.  
  155.     int pagemap = open(pagemap_file, O_RDONLY);
  156.     if(pagemap < 0) {
  157.         close(maps);
  158.         perror("open /proc/pagemap failed");
  159.         return;
  160.     }
  161.  
  162.     char buffer[BUFSIZ];
  163.     int offset = 0;
  164.     size_t y;
  165.  
  166.     for(;;) {
  167.         ssize_t length = read(maps, buffer + offset, sizeof buffer - offset);
  168.         if(length <= 0) break;
  169.  
  170.         length += offset;
  171.  
  172.         for(size_t i = offset; i < (size_t)length; i ++)
  173.         {
  174.             uint64_t low = 0, high = 0;
  175.             if(buffer[i] == '\n' && i)
  176.             {
  177.                 size_t x = i - 1;
  178.                 while(x && buffer[x] != '\n') x --;
  179.                 if(buffer[x] == '\n') x ++;
  180.                 size_t beginning = x;
  181.  
  182.                 while(buffer[x] != '-' && x+1 < sizeof buffer) {
  183.                     char c = buffer[x ++];
  184.                     low *= 16;
  185.                     if(c >= '0' && c <= '9') {
  186.                         low += c - '0';
  187.                     }
  188.                     else if(c >= 'a' && c <= 'f') {
  189.                         low += c - 'a' + 10;
  190.                     }
  191.                     else break;
  192.                 }
  193.  
  194.                 while(buffer[x] != '-' && x+1 < sizeof buffer) x ++;
  195.                 if(buffer[x] == '-') x ++;
  196.  
  197.                 while(buffer[x] != ' ' && x+1 < sizeof buffer)
  198.                 {
  199.                     char c = buffer[x ++];
  200.                     high *= 16;
  201.                     if(c >= '0' && c <= '9') {
  202.                         high += c - '0';
  203.                     }
  204.                     else if(c >= 'a' && c <= 'f') {
  205.                         high += c - 'a' + 10;
  206.                     }
  207.                     else break;
  208.                 }
  209.  
  210.                 const char *lib_name = 0;
  211.                 if( o_print_map_name )
  212.                 {
  213.                     for(int field = 0; field < 4; field ++) {
  214.                         x ++;  // skip space
  215.                         while(buffer[x] != ' ' && x+1 < sizeof buffer) x ++;
  216.                     }
  217.                     while(buffer[x] == ' ' && x+1 < sizeof buffer) x ++;
  218.  
  219.                     y = x;
  220.                     while(buffer[y] != '\n' && y+1 < sizeof buffer) y ++;
  221.                     buffer[y] = 0;
  222.  
  223.                     lib_name = buffer + x;
  224.                 }
  225.  
  226.                 read_vma(pagemap, low, high, lib_name);
  227.  
  228.                 if( o_print_map_name )
  229.                     buffer[y] = '\n';
  230.             }
  231.         }
  232.     }
  233.  
  234.     close(maps);
  235.     close(pagemap);
  236.  
  237.     print_summary();
  238. }
  239.  
  240. int main( int argc, char *argv[] )
  241. {
  242.     while( 1 )
  243.     {
  244.  
  245.         static struct option long_options[] = {
  246.             { "details",              no_argument,    NULL, 'd' },
  247.             { "cgroup",               no_argument,    NULL, 'g' },
  248.             { "refs",                 no_argument,    NULL, 'r' },
  249.             { "names",                no_argument,    NULL, 'n' },
  250.             { NULL,                             0,    NULL, 0   }
  251.         };
  252.  
  253.         int op_c = getopt_long( argc, argv, "dgrn", long_options, NULL );
  254.         if( op_c == -1 )
  255.             break;
  256.  
  257.         switch( op_c )
  258.         {
  259.             case 'd':
  260.                 o_show_details = 1;
  261.                 break;
  262.             case 'g':
  263.                 o_show_cgroup = 1;
  264.                 break;
  265.             case 'r':
  266.                 o_print_refs = 1;
  267.                 break;
  268.             case 'n':
  269.                 o_print_map_name = 1;
  270.                 break;
  271.             default:
  272.                 fprintf( stderr, "invalid argument\n");
  273.                 exit( EXIT_FAILURE );
  274.         }
  275.     }
  276.  
  277.     argc -= optind;
  278.     argv += optind;
  279.  
  280.     if( argc < 1 )
  281.     {
  282.         usage();
  283.         exit( EX_USAGE );
  284.     }
  285.  
  286.     errno = 0;
  287.     int pid = (int)strtol(*argv, NULL, 0);
  288.     if( errno )
  289.     {
  290.         perror("failed to parse PID");
  291.         return 1;
  292.     }
  293.  
  294.     init_array(&a_per_cgroup_stats);
  295.    
  296.     char maps_file[BUFSIZ];
  297.     char pagemap_file[BUFSIZ];
  298.     snprintf(maps_file, sizeof(maps_file), "/proc/%lu/maps", (uint64_t)pid);
  299.     snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%lu/pagemap", (uint64_t)pid);
  300.  
  301.     parse_maps(maps_file, pagemap_file);
  302.  
  303.     free_array(&a_per_cgroup_stats);
  304.  
  305.     return 0;
  306. }
  307.  
  308. static int fd_pagecount = -1;
  309.  
  310. uint64_t read_pagecount( uint64_t pfn )
  311. {
  312.    /* This file contains a 64-bit count of the number of
  313.    times each page is mapped, indexed by PFN.
  314.    */
  315.    if( -1 == fd_pagecount )
  316.    {
  317.       fd_pagecount = open("/proc/kpagecount", O_RDONLY);
  318.       if( fd_pagecount < 0 )
  319.       {
  320.          perror("open kpagecount");
  321.          return 0;
  322.       }
  323.    }
  324.  
  325.    uint64_t data, index = pfn * sizeof(uint64_t);
  326.  
  327.    if(pread(fd_pagecount, &data, sizeof(data), index) != sizeof(data)) {
  328.       perror("pread kpagecount");
  329.       return 0;
  330.    }
  331.  
  332.    return data;
  333. }
  334.  
  335. static int fd_pagecgroup = -1;
  336.  
  337. uint64_t read_pagecgroup( uint64_t pfn )
  338. {
  339.     /* This file contains a 64-bit inode number of the memory
  340.               cgroup each page is charged to, indexed by page frame
  341.               number (see the discussion of /proc/pid/pagemap).
  342.    */
  343.  
  344.    if( -1 == fd_pagecgroup )
  345.    {
  346.       fd_pagecgroup = open("/proc/kpagecgroup", O_RDONLY);
  347.       if( fd_pagecgroup < 0 )
  348.       {
  349.          perror("open kpagecgroup");
  350.          return 0;
  351.       }
  352.    }
  353.  
  354.    uint64_t data, index = pfn * sizeof(uint64_t);
  355.  
  356.    if(pread(fd_pagecgroup, &data, sizeof(data), index) != sizeof(data)) {
  357.       perror("pread kpagecgroup");
  358.       return 0;
  359.    }
  360.  
  361.    return data;
  362. }
  363.  
  364.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement