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) ldqnx.so.2 @b0300000 312K 16K
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.