/proc/[pid]/pagemaps and /proc/[pid]/maps | linux

前端 未结 3 1976
闹比i
闹比i 2020-12-03 02:06

I\'m trying to get my head around the two files mentioned in the title. I\'ve looked up what the bits are; however, I\'m failing to understand how to extract useful info fro

相关标签:
3条回答
  • 2020-12-03 02:25

    Use page-types.c as a guide to what you are looking for, it parses the contents of both pagemap and maps: https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/plain/Documentation/vm/page-types.c?h=linux-2.6.32.y

    0 讨论(0)
  • 2020-12-03 02:26

    Oooh K, the index was correct but comparing off64_t o (8bytes) with long index was interpreting o wrong hence why I was getting that error. Ha! this was a stupid mistake. So adding the appropriate header took care of that.

    Missing header :-/ sigh fixes the issue of comparing off64_t with a unsigned long.

    0 讨论(0)
  • 2020-12-03 02:35

    F**/proc/<pid>/pagemap + /proc/<pid>/maps dump example program**

    Here is a pagemap example that converts virtual addresses to physical: Is there any API for determining the physical address from virtual address in Linux?

    The following program uses both /proc/<pid>/pagemap + /proc/<pid>/maps to dump page table information to show how they can be used together. Usage:

    sudo ./pagemap_dump.out <pid>
    

    Sample output:

    addr pfn soft-dirty file/shared swapped present library
    400000 12845d 0 1 0 1 /bin/bash
    401000 12845e 0 1 0 1 /bin/bash
    402000 12845f 0 1 0 1 /bin/bash
    

    This tells us for example that the virtual address 0x400000 maps to the physical address 0x12845d000.

    Why sudo is required: https://unix.stackexchange.com/questions/345915/how-to-change-permission-of-proc-self-pagemap-file/383838#383838

    This program works in two steps:

    • parse the human readable lines lines from /proc/<pid>/maps. This files contains lines of form:

      7ffff7b6d000-7ffff7bdd000 r-xp 00000000 fe:00 658                        /lib/libuClibc-1.0.22.so
      

      which gives us:

      • 7f8af99f8000-7f8af99ff000: a virtual address range that belong to the process, possibly containing multiple pages.
      • /lib/libuClibc-1.0.22.so the name of the library that owns that memory.
    • loop over each page of each address range, and ask /proc/<pid>/pagemap for more information about that page, including the physical address.

    pagemap_dump.c

    #define _XOPEN_SOURCE 700
    #include <errno.h>
    #include <fcntl.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    typedef struct {
        uint64_t pfn : 55;
        unsigned int soft_dirty : 1;
        unsigned int file_page : 1;
        unsigned int swapped : 1;
        unsigned int present : 1;
    } PagemapEntry;
    
    /* Parse the pagemap entry for the given virtual address.
     *
     * @param[out] entry      the parsed entry
     * @param[in]  pagemap_fd file descriptor to an open /proc/pid/pagemap file
     * @param[in]  vaddr      virtual address to get entry for
     * @return 0 for success, 1 for failure
     */
    int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
    {
        size_t nread;
        ssize_t ret;
        uint64_t data;
    
        nread = 0;
        while (nread < sizeof(data)) {
            ret = pread(pagemap_fd, ((uint8_t*)&data) + nread, sizeof(data) - nread,
                    (vaddr / sysconf(_SC_PAGE_SIZE)) * sizeof(data) + nread);
            nread += ret;
            if (ret <= 0) {
                return 1;
            }
        }
        entry->pfn = data & (((uint64_t)1 << 55) - 1);
        entry->soft_dirty = (data >> 55) & 1;
        entry->file_page = (data >> 61) & 1;
        entry->swapped = (data >> 62) & 1;
        entry->present = (data >> 63) & 1;
        return 0;
    }
    
    /* Convert the given virtual address to physical using /proc/PID/pagemap.
     *
     * @param[out] paddr physical address
     * @param[in]  pid   process to convert for
     * @param[in] vaddr virtual address to get entry for
     * @return 0 for success, 1 for failure
     */
    int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
    {
        char pagemap_file[BUFSIZ];
        int pagemap_fd;
    
        snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
        pagemap_fd = open(pagemap_file, O_RDONLY);
        if (pagemap_fd < 0) {
            return 1;
        }
        PagemapEntry entry;
        if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
            return 1;
        }
        close(pagemap_fd);
        *paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
        return 0;
    }
    
    int main(int argc, char **argv)
    {
        char buffer[BUFSIZ];
        char maps_file[BUFSIZ];
        char pagemap_file[BUFSIZ];
        int maps_fd;
        int offset = 0;
        int pagemap_fd;
        pid_t pid;
    
        if (argc < 2) {
            printf("Usage: %s pid\n", argv[0]);
            return EXIT_FAILURE;
        }
        pid = strtoull(argv[1], NULL, 0);
        snprintf(maps_file, sizeof(maps_file), "/proc/%ju/maps", (uintmax_t)pid);
        snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
        maps_fd = open(maps_file, O_RDONLY);
        if (maps_fd < 0) {
            perror("open maps");
            return EXIT_FAILURE;
        }
        pagemap_fd = open(pagemap_file, O_RDONLY);
        if (pagemap_fd < 0) {
            perror("open pagemap");
            return EXIT_FAILURE;
        }
        printf("addr pfn soft-dirty file/shared swapped present library\n");
        for (;;) {
            ssize_t length = read(maps_fd, buffer + offset, sizeof buffer - offset);
            if (length <= 0) break;
            length += offset;
            for (size_t i = offset; i < (size_t)length; i++) {
                uintptr_t low = 0, high = 0;
                if (buffer[i] == '\n' && i) {
                    const char *lib_name;
                    size_t y;
                    /* Parse a line from maps. Each line contains a range that contains many pages. */
                    {
                        size_t x = i - 1;
                        while (x && buffer[x] != '\n') x--;
                        if (buffer[x] == '\n') x++;
                        while (buffer[x] != '-' && x < sizeof buffer) {
                            char c = buffer[x++];
                            low *= 16;
                            if (c >= '0' && c <= '9') {
                                low += c - '0';
                            } else if (c >= 'a' && c <= 'f') {
                                low += c - 'a' + 10;
                            } else {
                                break;
                            }
                        }
                        while (buffer[x] != '-' && x < sizeof buffer) x++;
                        if (buffer[x] == '-') x++;
                        while (buffer[x] != ' ' && x < sizeof buffer) {
                            char c = buffer[x++];
                            high *= 16;
                            if (c >= '0' && c <= '9') {
                                high += c - '0';
                            } else if (c >= 'a' && c <= 'f') {
                                high += c - 'a' + 10;
                            } else {
                                break;
                            }
                        }
                        lib_name = 0;
                        for (int field = 0; field < 4; field++) {
                            x++;
                            while(buffer[x] != ' ' && x < sizeof buffer) x++;
                        }
                        while (buffer[x] == ' ' && x < sizeof buffer) x++;
                        y = x;
                        while (buffer[y] != '\n' && y < sizeof buffer) y++;
                        buffer[y] = 0;
                        lib_name = buffer + x;
                    }
                    /* Get info about all pages in this page range with pagemap. */
                    {
                        PagemapEntry entry;
                        for (uintptr_t addr = low; addr < high; addr += sysconf(_SC_PAGE_SIZE)) {
                            /* TODO always fails for the last page (vsyscall), why? pread returns 0. */
                            if (!pagemap_get_entry(&entry, pagemap_fd, addr)) {
                                printf("%jx %jx %u %u %u %u %s\n",
                                    (uintmax_t)addr,
                                    (uintmax_t)entry.pfn,
                                    entry.soft_dirty,
                                    entry.file_page,
                                    entry.swapped,
                                    entry.present,
                                    lib_name
                                );
                            }
                        }
                    }
                    buffer[y] = '\n';
                }
            }
        }
        close(maps_fd);
        close(pagemap_fd);
        return EXIT_SUCCESS;
    }
    
    0 讨论(0)
提交回复
热议问题