2 * Written by Dave Hansen <dave.hansen@intel.com>
10 #include <sys/types.h>
16 #include "mpx-debug.h"
20 unsigned long bounds_dir_global
;
22 #define mpx_dig_abort() __mpx_dig_abort(__FILE__, __func__, __LINE__)
23 static void inline __mpx_dig_abort(const char *file
, const char *func
, int line
)
25 fprintf(stderr
, "MPX dig abort @ %s::%d in %s()\n", file
, line
, func
);
26 printf("MPX dig abort @ %s::%d in %s()\n", file
, line
, func
);
31 * run like this (BDIR finds the probably bounds directory):
33 * BDIR="$(cat /proc/$pid/smaps | grep -B1 2097152 \
34 * | head -1 | awk -F- '{print $1}')";
35 * ./mpx-dig $pid 0x$BDIR
38 * assumes that the only 2097152-kb VMA is the bounds dir
41 long nr_incore(void *ptr
, unsigned long size_bytes
)
45 long vec_len
= size_bytes
/ PAGE_SIZE
;
46 unsigned char *vec
= malloc(vec_len
);
52 incore_ret
= mincore(ptr
, size_bytes
, vec
);
54 printf("mincore ret: %d\n", incore_ret
);
58 for (i
= 0; i
< vec_len
; i
++)
64 int open_proc(int pid
, char *file
)
69 snprintf(&buf
[0], sizeof(buf
), "/proc/%d/%s", pid
, file
);
70 fd
= open(&buf
[0], O_RDONLY
);
81 struct vaddr_range
*ranges
;
82 int nr_ranges_allocated
;
83 int nr_ranges_populated
;
86 int __pid_load_vaddrs(int pid
)
89 int proc_maps_fd
= open_proc(pid
, "maps");
94 FILE *f
= fdopen(proc_maps_fd
, "r");
98 nr_ranges_populated
= 0;
100 char *readret
= fgets(linebuf
, sizeof(linebuf
), f
);
103 if (readret
== NULL
) {
109 parsed
= sscanf(linebuf
, "%lx-%lx%s", &start
, &end
, rest
);
113 dprintf4("result[%d]: %lx-%lx<->%s\n", parsed
, start
, end
, rest
);
114 if (nr_ranges_populated
>= nr_ranges_allocated
) {
118 ranges
[nr_ranges_populated
].start
= start
;
119 ranges
[nr_ranges_populated
].end
= end
;
120 nr_ranges_populated
++;
128 int pid_load_vaddrs(int pid
)
132 dprintf2("%s(%d)\n", __func__
, pid
);
134 nr_ranges_allocated
= 4;
135 ranges
= malloc(nr_ranges_allocated
* sizeof(ranges
[0]));
136 dprintf2("%s(%d) allocated %d ranges @ %p\n", __func__
, pid
,
137 nr_ranges_allocated
, ranges
);
138 assert(ranges
!= NULL
);
141 ret
= __pid_load_vaddrs(pid
);
145 dprintf2("%s(%d) need to realloc\n", __func__
, pid
);
146 nr_ranges_allocated
*= 2;
147 ranges
= realloc(ranges
,
148 nr_ranges_allocated
* sizeof(ranges
[0]));
149 dprintf2("%s(%d) allocated %d ranges @ %p\n", __func__
,
150 pid
, nr_ranges_allocated
, ranges
);
151 assert(ranges
!= NULL
);
152 dprintf1("reallocating to hold %d ranges\n", nr_ranges_allocated
);
156 dprintf2("%s(%d) done\n", __func__
, pid
);
161 static inline int vaddr_in_range(unsigned long vaddr
, struct vaddr_range
*r
)
163 if (vaddr
< r
->start
)
170 static inline int vaddr_mapped_by_range(unsigned long vaddr
)
174 if (last_range
> 0 && vaddr_in_range(vaddr
, &ranges
[last_range
]))
177 for (i
= 0; i
< nr_ranges_populated
; i
++) {
178 struct vaddr_range
*r
= &ranges
[i
];
180 if (vaddr_in_range(vaddr
, r
))
188 const int bt_entry_size_bytes
= sizeof(unsigned long) * 4;
190 void *read_bounds_table_into_buf(unsigned long table_vaddr
)
192 #ifdef MPX_DIG_STANDALONE
193 static char bt_buf
[MPX_BOUNDS_TABLE_SIZE_BYTES
];
194 off_t seek_ret
= lseek(fd
, table_vaddr
, SEEK_SET
);
195 if (seek_ret
!= table_vaddr
)
198 int read_ret
= read(fd
, &bt_buf
, sizeof(bt_buf
));
199 if (read_ret
!= sizeof(bt_buf
))
203 return (void *)table_vaddr
;
207 int dump_table(unsigned long table_vaddr
, unsigned long base_controlled_vaddr
,
208 unsigned long bde_vaddr
)
210 unsigned long offset_inside_bt
;
215 dprintf3("%s() base_controlled_vaddr: 0x%012lx bde_vaddr: 0x%012lx\n",
216 __func__
, base_controlled_vaddr
, bde_vaddr
);
218 bt_buf
= read_bounds_table_into_buf(table_vaddr
);
220 dprintf4("%s() read done\n", __func__
);
222 for (offset_inside_bt
= 0;
223 offset_inside_bt
< MPX_BOUNDS_TABLE_SIZE_BYTES
;
224 offset_inside_bt
+= bt_entry_size_bytes
) {
225 unsigned long bt_entry_index
;
226 unsigned long bt_entry_controls
;
227 unsigned long this_bt_entry_for_vaddr
;
228 unsigned long *bt_entry_buf
;
231 dprintf4("%s() offset_inside_bt: 0x%lx of 0x%llx\n", __func__
,
232 offset_inside_bt
, MPX_BOUNDS_TABLE_SIZE_BYTES
);
233 bt_entry_buf
= (void *)&bt_buf
[offset_inside_bt
];
235 printf("null bt_buf\n");
239 printf("null bt_entry_buf\n");
242 dprintf4("%s() reading *bt_entry_buf @ %p\n", __func__
,
244 if (!bt_entry_buf
[0] &&
252 bt_entry_index
= offset_inside_bt
/bt_entry_size_bytes
;
253 bt_entry_controls
= sizeof(void *);
254 this_bt_entry_for_vaddr
=
255 base_controlled_vaddr
+ bt_entry_index
*bt_entry_controls
;
257 * We sign extend vaddr bits 48->63 which effectively
258 * creates a hole in the virtual address space.
259 * This calculation corrects for the hole.
261 if (this_bt_entry_for_vaddr
> 0x00007fffffffffffUL
)
262 this_bt_entry_for_vaddr
|= 0xffff800000000000;
264 if (!vaddr_mapped_by_range(this_bt_entry_for_vaddr
)) {
265 printf("bt_entry_buf: %p\n", bt_entry_buf
);
266 printf("there is a bte for %lx but no mapping\n",
267 this_bt_entry_for_vaddr
);
268 printf(" bde vaddr: %016lx\n", bde_vaddr
);
269 printf("base_controlled_vaddr: %016lx\n", base_controlled_vaddr
);
270 printf(" table_vaddr: %016lx\n", table_vaddr
);
271 printf(" entry vaddr: %016lx @ offset %lx\n",
272 table_vaddr
+ offset_inside_bt
, offset_inside_bt
);
279 printf("table entry[%lx]: ", offset_inside_bt
);
280 for (i
= 0; i
< bt_entry_size_bytes
; i
+= sizeof(unsigned long))
281 printf("0x%016lx ", bt_entry_buf
[i
]);
286 dprintf4("%s() done\n", __func__
);
290 int search_bd_buf(char *buf
, int len_bytes
, unsigned long bd_offset_bytes
,
291 int *nr_populated_bdes
)
294 int total_entries
= 0;
296 dprintf3("%s(%p, %x, %lx, ...) buf end: %p\n", __func__
, buf
,
297 len_bytes
, bd_offset_bytes
, buf
+ len_bytes
);
299 for (i
= 0; i
< len_bytes
; i
+= sizeof(unsigned long)) {
300 unsigned long bd_index
= (bd_offset_bytes
+ i
) / sizeof(unsigned long);
301 unsigned long *bounds_dir_entry_ptr
= (unsigned long *)&buf
[i
];
302 unsigned long bounds_dir_entry
;
303 unsigned long bd_for_vaddr
;
304 unsigned long bt_start
;
305 unsigned long bt_tail
;
308 dprintf4("%s() loop i: %ld bounds_dir_entry_ptr: %p\n", __func__
, i
,
309 bounds_dir_entry_ptr
);
311 bounds_dir_entry
= *bounds_dir_entry_ptr
;
312 if (!bounds_dir_entry
) {
313 dprintf4("no bounds dir at index 0x%lx / 0x%lx "
314 "start at offset:%lx %lx\n", bd_index
, bd_index
,
318 dprintf3("found bounds_dir_entry: 0x%lx @ "
319 "index 0x%lx buf ptr: %p\n", bounds_dir_entry
, i
,
321 /* mask off the enable bit: */
322 bounds_dir_entry
&= ~0x1;
323 (*nr_populated_bdes
)++;
324 dprintf4("nr_populated_bdes: %p\n", nr_populated_bdes
);
325 dprintf4("*nr_populated_bdes: %d\n", *nr_populated_bdes
);
327 bt_start
= bounds_dir_entry
;
328 bt_tail
= bounds_dir_entry
+ MPX_BOUNDS_TABLE_SIZE_BYTES
- 1;
329 if (!vaddr_mapped_by_range(bt_start
)) {
330 printf("bounds directory 0x%lx points to nowhere\n",
334 if (!vaddr_mapped_by_range(bt_tail
)) {
335 printf("bounds directory end 0x%lx points to nowhere\n",
340 * Each bounds directory entry controls 1MB of virtual address
341 * space. This variable is the virtual address in the process
342 * of the beginning of the area controlled by this bounds_dir.
344 bd_for_vaddr
= bd_index
* (1UL<<20);
346 nr_entries
= dump_table(bounds_dir_entry
, bd_for_vaddr
,
347 bounds_dir_global
+bd_offset_bytes
+i
);
348 total_entries
+= nr_entries
;
349 dprintf5("dir entry[%4ld @ %p]: 0x%lx %6d entries "
350 "total this buf: %7d bd_for_vaddrs: 0x%lx -> 0x%lx\n",
352 bounds_dir_entry
, nr_entries
, total_entries
,
353 bd_for_vaddr
, bd_for_vaddr
+ (1UL<<20));
355 dprintf3("%s(%p, %x, %lx, ...) done\n", __func__
, buf
, len_bytes
,
357 return total_entries
;
360 int proc_pid_mem_fd
= -1;
362 void *fill_bounds_dir_buf_other(long byte_offset_inside_bounds_dir
,
363 long buffer_size_bytes
, void *buffer
)
365 unsigned long seekto
= bounds_dir_global
+ byte_offset_inside_bounds_dir
;
367 off_t seek_ret
= lseek(proc_pid_mem_fd
, seekto
, SEEK_SET
);
369 if (seek_ret
!= seekto
)
372 read_ret
= read(proc_pid_mem_fd
, buffer
, buffer_size_bytes
);
373 /* there shouldn't practically be short reads of /proc/$pid/mem */
374 if (read_ret
!= buffer_size_bytes
)
379 void *fill_bounds_dir_buf_self(long byte_offset_inside_bounds_dir
,
380 long buffer_size_bytes
, void *buffer
)
383 unsigned char vec
[buffer_size_bytes
/ PAGE_SIZE
];
384 char *dig_bounds_dir_ptr
=
385 (void *)(bounds_dir_global
+ byte_offset_inside_bounds_dir
);
387 * use mincore() to quickly find the areas of the bounds directory
388 * that have memory and thus will be worth scanning.
395 dprintf4("%s() dig_bounds_dir_ptr: %p\n", __func__
, dig_bounds_dir_ptr
);
397 incore_ret
= mincore(dig_bounds_dir_ptr
, buffer_size_bytes
, &vec
[0]);
399 printf("mincore ret: %d\n", incore_ret
);
403 for (i
= 0; i
< sizeof(vec
); i
++)
405 dprintf4("%s() total incore: %d\n", __func__
, incore
);
408 dprintf3("%s() total incore: %d\n", __func__
, incore
);
409 return dig_bounds_dir_ptr
;
412 int inspect_pid(int pid
)
415 long offset_inside_bounds_dir
;
416 char bounds_dir_buf
[sizeof(unsigned long) * (1UL << 15)];
417 char *dig_bounds_dir_ptr
;
418 int total_entries
= 0;
419 int nr_populated_bdes
= 0;
422 if (getpid() == pid
) {
423 dprintf4("inspecting self\n");
426 dprintf4("inspecting pid %d\n", pid
);
430 for (offset_inside_bounds_dir
= 0;
431 offset_inside_bounds_dir
< MPX_BOUNDS_TABLE_SIZE_BYTES
;
432 offset_inside_bounds_dir
+= sizeof(bounds_dir_buf
)) {
433 static int bufs_skipped
;
438 fill_bounds_dir_buf_self(offset_inside_bounds_dir
,
439 sizeof(bounds_dir_buf
),
443 fill_bounds_dir_buf_other(offset_inside_bounds_dir
,
444 sizeof(bounds_dir_buf
),
447 if (!dig_bounds_dir_ptr
) {
451 this_entries
= search_bd_buf(dig_bounds_dir_ptr
,
452 sizeof(bounds_dir_buf
),
453 offset_inside_bounds_dir
,
455 total_entries
+= this_entries
;
457 printf("mpx dig (%3d) complete, SUCCESS (%8d / %4d)\n", ++dig_nr
,
458 total_entries
, nr_populated_bdes
);
459 return total_entries
+ nr_populated_bdes
;
462 #ifdef MPX_DIG_REMOTE
463 int main(int argc
, char **argv
)
467 unsigned long bounds_dir_entry
;
470 printf("mpx-dig starting...\n");
471 err
= sscanf(argv
[1], "%d", &pid
);
472 printf("parsing: '%s', err: %d\n", argv
[1], err
);
476 err
= sscanf(argv
[2], "%lx", &bounds_dir_global
);
477 printf("parsing: '%s': %d\n", argv
[2], err
);
481 proc_pid_mem_fd
= open_proc(pid
, "mem");
482 if (proc_pid_mem_fd
< 0)
490 long inspect_me(struct mpx_bounds_dir
*bounds_dir
)
494 pid_load_vaddrs(pid
);
495 bounds_dir_global
= (unsigned long)bounds_dir
;
496 dprintf4("enter %s() bounds dir: %p\n", __func__
, bounds_dir
);
497 return inspect_pid(pid
);