DCMD_PROC_MAPINFO and DCMD_PROC_PAGEDATA
The next thing that we can do with a process is look at the memory segments that it has in use. There are two devctl() commands to accomplish this: DCMD_PROC_MAPINFO and DCMD_PROC_PAGEDATA (also described in the QNX Neutrino Programmer's Guide).
Both commands use the same data structure (edited for clarity):
typedef struct _procfs_map_info {
uint64_t vaddr;
uint64_t size;
uint32_t flags;
dev_t dev;
off_t offset;
ino_t ino;
} procfs_mapinfo;
The original data structure declaration has #ifdef's for 32 versus 64 bit sizes of the
offset and ino members.
The procfs_mapinfo is used in its array form, meaning that we must allocate sufficient space for all of the memory segments that we will be getting information about. Practically speaking, I've managed just fine with 512 (MAX_SEGMENTS) elements. When I use this call in code, I compare the number of elements available (returned by the devctl() function) and ensure that it is less than the constant MAX_SEGMENTS. In the unlikely event that 512 elements are insufficient, you can allocate the array dynamically and reissue the devctl() call with a bigger buffer. In practice, the 10 to 100 element range is sufficient; 512 is overkill.
Here's how the call is used:
#define MAX_SEGMENTS 512
void
dump_procfs_map_info (int fd, int pid)
{
procfs_mapinfo membufs [MAX_SEGMENTS];
int nmembuf;
int i;
int sts;
// fetch information about the memory regions for this pid
sts = devctl (fd, DCMD_PROC_PAGEDATA, membufs, sizeof (membufs), &nmembuf);
if (sts != EOK) {
fprintf(stderr, "%s: PAGEDATA process %d, error %d (%s)\n",
progname, pid, sts, strerror (sts));
exit (EXIT_FAILURE);
}
// check to see we haven't overflowed
if (nmembuf > MAX_SEGMENTS) {
fprintf (stderr, "%s: proc %d has > %d memsegs (%d)!!!\n",
progname, pid, MAX_SEGMENTS, nmembuf);
exit (EXIT_FAILURE);
}
for (i = 0; i < nmembuf; i++) {
// now we can print/analyze the data
}
}
Here's the output for the pipe process (I've added blank lines for clarity):
Info from DCMD_PROC_PAGEDATA
Buff# --vaddr--- ---size--- ---flags-- ---dev---- ---ino----
[ 0] 0x07F22000 0x00001000 0x01001083 0x00000002 0x00000001
[ 1] 0x07F23000 0x0001F000 0x01001783 0x00000002 0x00000001
[ 2] 0x07F42000 0x00001000 0x01401783 0x00000002 0x00000001
[ 3] 0x07F43000 0x00001000 0x01001083 0x00000002 0x00000001
[ 4] 0x07F44000 0x0001F000 0x01001783 0x00000002 0x00000001
[ 5] 0x07F63000 0x00001000 0x01401783 0x00000002 0x00000001
[ 6] 0x07F64000 0x00001000 0x01001083 0x00000002 0x00000001
[ 7] 0x07F65000 0x0001F000 0x01001783 0x00000002 0x00000001
[ 8] 0x07F84000 0x00001000 0x01401783 0x00000002 0x00000001
[ 9] 0x07FA6000 0x00001000 0x01001083 0x00000002 0x00000001
[ 10] 0x07FA7000 0x0001F000 0x01001783 0x00000002 0x00000001
[ 11] 0x07FC6000 0x00001000 0x01401783 0x00000002 0x00000001
[ 12] 0x07FC7000 0x00001000 0x01001083 0x00000002 0x00000001
[ 13] 0x07FC8000 0x0007E000 0x01001383 0x00000002 0x00000001
[ 14] 0x08046000 0x00002000 0x01401383 0x00000002 0x00000001
[ 15] 0x08048000 0x00004000 0x00400571 0x00000001 0x00000009
[ 16] 0x0804C000 0x00001000 0x01400372 0x00000001 0x00000009
[ 17] 0x0804D000 0x00024000 0x01400303 0x00000002 0x00000001
[ 18] 0xB0300000 0x0004E000 0x00410561 0x00000004 0xB0300000
[ 19] 0xB034E000 0x00004000 0x01400772 0x00000004 0xB0300000
This tells us that there are 20 memory regions in use, and gives us the virtual address, the size, flags, device number, and inode for each one. Let's correlate this to the pidin output:
# pidin -p4105 mem
pid tid name prio STATE code data stack
4105 1 sbin/pipe 10o RECEIVE 16K 148K 4096(132K)
4105 2 sbin/pipe 10o RECEIVE 16K 148K 4096(132K)
4105 4 sbin/pipe 10o RECEIVE 16K 148K 4096(132K)
4105 5 sbin/pipe 10o RECEIVE 16K 148K 4096(132K)
libc.so.5 @b0300000 312K 16K
- Regions 0, 3, 6, 9 and 12
- These are the guard pages at the end of the stacks, one for each of the five threads.
- Regions 1, 4, 7, 10 and 13
- These are the growth areas for the stacks, one for each of the five threads.
This memory is physically allocated on demand; these regions serve to reserve the virtual address ranges.
This corresponds to the
from the pidin output.(132K) - Regions 2, 5, 8, 11 and 14
- These are the in-use 4 KB stack segments, one for each of the five threads. Only four threads are alive—we'll discuss this below. This corresponds to the 4096 from the pidin output.
- Region 15
- This is the 16 KB of code for pipe.
- Regions 16 and 17
- These are the data areas (4 KB and 144 KB, for a total of 148 KB).
- Regions 18 and 19
- These are for the shared object, libc.so.5. Region 18 is the code area, region 19 is the data area. These correspond to the libc.so.5 line from the pidin output.
The key to decoding the regions is to look at the flags member. You'll notice that there are two commands: DCMD_PROC_PAGEDATA and DCMD_PROC_MAPINFO. Both of these are used to obtain information about memory regions. However, DCMD_PROC_MAPINFO merges non-PG_* regions together, whereas DCMD_PROC_PAGEDATA lists them individually. This also implies that the three PG_* flags (PG_HWMAPPED, PG_REFERENCED, and PG_MODIFIED are valid only with DCMD_PROC_PAGEDATA).
The flags member is a bitmap, broken down as follows (with each flag's value, defined in <sys/mman.h>, shown in parentheses):
0 Reserved
0 Reserved
x MAP_CONSTRAINED (0x02000000)
x MAP_SYSRAM (0x01000000)
0 Reserved
x PG_HWMAPPED (0x00400000)
x PG_REFERENCED (0x00200000)
x PG_MODIFIED (0x00100000)
x MAP_ANON (0x00080000)
0 Reserved
0 Reserved
x MAP_PHYS (0x00010000)
0 Reserved
0 Reserved
x MAP_BELOW (0x00002000)
x MAP_STACK (0x00001000)
x PROT_NOCACHE (0x00000800)
x PROT_EXEC (0x00000400)
x PROT_WRITE (0x00000200)
x PROT_READ (0x00000100)
x MAP_LAZY (0x00000080)
x MAP_NOSYNCFILE (0x00000040)
x MAP_ELF (0x00000020)
x MAP_FIXED (0x00000010)
0 Reserved
0 Reserved
x See below.
x See below.
The last two bits are used together to indicate these flags:
00 MAP_FILE (0x00000000)
01 MAP_SHARED (0x00000001)
10 MAP_PRIVATE (0x00000002)
By looking for a tell-tale
flag, namely MAP_STACK (0x00001000), I was
able to find all of the stack segments (regions 0 through 14).
Having eliminated those, regions 15, 18, and 19 are marked
as PROT_EXEC (0x00000400), so must be executable (the data area of the shared library
is marked executable).
By process of elimination, regions 16 and 17 aren't marked executable; therefore, they're data.
