I need to programmatically calculate the total page table size of a given process in Linux using the /proc/pid/maps and /proc/pid/pagemap interfaces in C.
I now how to get a page entry and calculate the page frame number using these interfaces. But I cannot figure out how to calculate the table size given this information.
Here's my code:
void alltablesize(int pid) {
//open /proc/pid/maps to read virtual addresses
char file_path[50];
sprintf(file_path, MAPS_FILE, pid);
FILE *maps = fopen(file_path, "r");
if(maps == NULL) {
perror("Failed to open /proc/pid/maps");
return;
}
char line[256];
uint64_t start, end;
uint64_t vpn, pfn;
uint64_t num_entries;
uint64_t total_page_size;
while(fgets(line, sizeof(line), maps) != NULL) {
char fname[256];
sscanf(line, "%lx-%lx %*s %*lx %*x:%*x %*d %s", &start, &end, fname);
for(vpn = start/PAGE_SIZE; vpn < end/PAGE_SIZE; vpn++) {
//open 7proc/pid/pagemap to extract information about the va
char pagemap_path[50];
sprintf(pagemap_path, PAGEMAP_FILE, pid);
int pagemap_fd = open(pagemap_path, O_RDONLY);
if (pagemap_fd == -1) {
perror("Failed to open /proc/pid/pagemap");
fclose(maps);
return;
}
uint64_t entry; //entry is 64 bits
off_t offset = vpn * PAGEMAP_LENGTH;
lseek(pagemap_fd, offset, SEEK_SET);
read(pagemap_fd, &entry, sizeof(uint64_t));
pfn = get_pfn(entry);
num_entries++;
}
}
}
Recall that an address is broken up by:
If a page is valid (ie. exists in the last level index), there must be a page in each of the levels above it. So a single page of memory would require 4 pages of page tables. However, there could be up to 512 [12..20] pages that shared those 4 pages of page tables if their virtual addresses matched in bits 21..63.
This applies recursively; so at each level of page table there can be up to 512 contiguous entries before you have to add a new page one level up.
This implies that you need to process the maps by address and reconstruct these indices to figure out how many you need.
An added complication is large pages. For example, in stead of having a "last level page table", I could have a single large 2M (512 * 4096) page, so could skip the last one. Also there are 1G large pages which cover the "second last level page table".