/* ipcs.c - provide information on ipc facilities * * Copyright (C) 2012 FUJITSU LIMITED * Auther: Qiao Nuohan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "defs.h" /* From the crash source top-level directory */ #define SPECIFIED_NOTHING 0x0 #define SPECIFIED_ID 0x1 #define SPECIFIED_ADDR 0x2 #define IPCS_INIT 0x1 #define IDR_ORIG 0x2 #define IDR_RADIX 0x4 #define IDR_XARRAY 0x8 #define MAX_ID_SHIFT (sizeof(int)*8 - 1) #define MAX_ID_BIT (1U << MAX_ID_SHIFT) #define MAX_ID_MASK (MAX_ID_BIT - 1) #define SHM_DEST 01000 #define SHM_LOCKED 02000 struct shm_info { ulong shmid_kernel; int key; int shmid; ulong rss; ulong swap; unsigned int uid; unsigned int perms; ulong bytes; ulong nattch; ulong shm_inode; int deleted; }; struct sem_info { ulong sem_array; int key; int semid; unsigned int uid; unsigned int perms; ulong nsems; int deleted; }; struct msg_info { ulong msg_queue; int key; int msgid; unsigned int uid; unsigned int perms; ulong bytes; ulong messages; int deleted; }; struct ipcs_table { int idr_bits; ulong init_flags; ulong hugetlbfs_f_op_addr; ulong shm_f_op_addr; ulong shm_f_op_huge_addr; int use_shm_f_op; int seq_multiplier; int cnt; struct list_pair *lp; }; /* * function declaration */ static int dump_shared_memory(int, ulong, int, ulong); static int dump_semaphore_arrays(int, ulong, int, ulong); static int dump_message_queues(int, ulong, int, ulong); static int ipc_search_idr(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int); static int ipc_search_array(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int); static int dump_shm_info(ulong, int, ulong, int, int); static int dump_sem_info(ulong, int, ulong, int, int); static int dump_msg_info(ulong, int, ulong, int, int); static void get_shm_info(struct shm_info *, ulong, int); static void get_sem_info(struct sem_info *, ulong, int); static void get_msg_info(struct msg_info *, ulong, int); static void add_rss_swap(ulong, int, ulong *, ulong *); static int is_file_hugepages(ulong); static void gather_radix_tree_entries(ulong); static void gather_xarray_entries(ulong); /* * global data */ static struct ipcs_table ipcs_table = { 0 }; void ipcs_init(void) { if (ipcs_table.init_flags & IPCS_INIT) { return; } ipcs_table.init_flags |= IPCS_INIT; MEMBER_OFFSET_INIT(file_f_op, "file", "f_op"); MEMBER_OFFSET_INIT(file_private_data, "file", "private_data"); MEMBER_OFFSET_INIT(hstate_order, "hstate", "order"); MEMBER_OFFSET_INIT(hugetlbfs_sb_info_hstate, "hugetlbfs_sb_info", "hstate"); MEMBER_OFFSET_INIT(idr_layers, "idr", "layers"); MEMBER_OFFSET_INIT(idr_layer_layer, "idr_layer", "layer"); MEMBER_OFFSET_INIT(idr_layer_ary, "idr_layer", "ary"); MEMBER_OFFSET_INIT(idr_top, "idr", "top"); MEMBER_OFFSET_INIT(idr_cur, "idr", "cur"); MEMBER_OFFSET_INIT(ipc_id_ary_p, "ipc_id_ary", "p"); MEMBER_OFFSET_INIT(ipc_ids_entries, "ipc_ids", "entries"); MEMBER_OFFSET_INIT(ipc_ids_max_id, "ipc_ids", "max_id"); MEMBER_OFFSET_INIT(ipc_ids_in_use, "ipc_ids", "in_use"); MEMBER_OFFSET_INIT(ipc_ids_ipcs_idr, "ipc_ids", "ipcs_idr"); MEMBER_OFFSET_INIT(ipc_namespace_ids, "ipc_namespace", "ids"); MEMBER_OFFSET_INIT(kern_ipc_perm_key, "kern_ipc_perm", "key"); MEMBER_OFFSET_INIT(kern_ipc_perm_id, "kern_ipc_perm", "id"); MEMBER_OFFSET_INIT(kern_ipc_perm_uid, "kern_ipc_perm", "uid"); MEMBER_OFFSET_INIT(kern_ipc_perm_mode, "kern_ipc_perm", "mode"); MEMBER_OFFSET_INIT(kern_ipc_perm_deleted, "kern_ipc_perm", "deleted"); MEMBER_OFFSET_INIT(kern_ipc_perm_seq, "kern_ipc_perm", "seq"); MEMBER_OFFSET_INIT(nsproxy_ipc_ns, "nsproxy", "ipc_ns"); MEMBER_OFFSET_INIT(shmem_inode_info_vfs_inode, "shmem_inode_info", "vfs_inode"); MEMBER_OFFSET_INIT(shmem_inode_info_swapped, "shmem_inode_info", "swapped"); if (INVALID_MEMBER(shmem_inode_info_swapped)) ANON_MEMBER_OFFSET_INIT(shmem_inode_info_swapped, "shmem_inode_info", "swapped"); MEMBER_OFFSET_INIT(shm_file_data_file, "shm_file_data", "file"); MEMBER_OFFSET_INIT(shmid_kernel_shm_perm, "shmid_kernel", "shm_perm"); MEMBER_OFFSET_INIT(shmid_kernel_shm_segsz, "shmid_kernel", "shm_segsz"); MEMBER_OFFSET_INIT(shmid_kernel_shm_nattch, "shmid_kernel", "shm_nattch"); MEMBER_OFFSET_INIT(shmid_kernel_shm_file, "shmid_kernel", "shm_file"); MEMBER_OFFSET_INIT(shmid_kernel_id, "shmid_kernel", "id"); MEMBER_OFFSET_INIT(sem_array_sem_perm, "sem_array", "sem_perm"); MEMBER_OFFSET_INIT(sem_array_sem_id, "sem_array", "sem_id"); MEMBER_OFFSET_INIT(sem_array_sem_nsems, "sem_array", "sem_nsems"); MEMBER_OFFSET_INIT(msg_queue_q_perm, "msg_queue", "q_perm"); MEMBER_OFFSET_INIT(msg_queue_q_id, "msg_queue", "q_id"); MEMBER_OFFSET_INIT(msg_queue_q_cbytes, "msg_queue", "q_cbytes"); MEMBER_OFFSET_INIT(msg_queue_q_qnum, "msg_queue", "q_qnum"); MEMBER_OFFSET_INIT(super_block_s_fs_info, "super_block", "s_fs_info"); /* * struct size */ STRUCT_SIZE_INIT(ipc_ids, "ipc_ids"); STRUCT_SIZE_INIT(shmid_kernel, "shmid_kernel"); STRUCT_SIZE_INIT(sem_array, "sem_array"); STRUCT_SIZE_INIT(msg_queue, "msg_queue"); STRUCT_SIZE_INIT(hstate, "hstate"); if (symbol_exists("hugetlbfs_file_operations")) ipcs_table.hugetlbfs_f_op_addr = symbol_value("hugetlbfs_file_operations"); if (symbol_exists("is_file_shm_hugepages")) { ipcs_table.use_shm_f_op = TRUE; ipcs_table.shm_f_op_addr = symbol_value("shm_file_operations"); if (symbol_exists("shm_file_operations_huge")) { ipcs_table.shm_f_op_huge_addr = symbol_value("shm_file_operations_huge"); } else { ipcs_table.shm_f_op_huge_addr = -1; } } else { ipcs_table.use_shm_f_op = FALSE; ipcs_table.shm_f_op_addr = -1; ipcs_table.shm_f_op_huge_addr = -1; } if (VALID_MEMBER(idr_layer_ary) && get_array_length("idr_layer.ary", NULL, 0) > 64) ipcs_table.idr_bits = 8; else if (BITS32()) ipcs_table.idr_bits = 5; else if (BITS64()) ipcs_table.idr_bits = 6; else error(FATAL, "machdep->bits is not 32 or 64"); if (VALID_MEMBER(idr_idr_rt)) { if (STREQ(MEMBER_TYPE_NAME("idr", "idr_rt"), "xarray")) ipcs_table.init_flags |= IDR_XARRAY; else { if (MEMBER_EXISTS("radix_tree_root", "rnode")) ipcs_table.init_flags |= IDR_RADIX; else if (MEMBER_EXISTS("radix_tree_root", "xa_head")) ipcs_table.init_flags |= IDR_XARRAY; } } else ipcs_table.init_flags |= IDR_ORIG; ipcs_table.seq_multiplier = 32768; } /* * Arguments are passed to the command functions in the global args[argcnt] * array. See getopt(3) for info on dash arguments. Check out defs.h and * other crash commands for usage of the myriad of utility routines available * to accomplish what your task. */ void cmd_ipcs(void) { int specified; char *specified_value[MAXARGS]; int value_index; int c; int shm, sem, msg, verbose; int i; ulong value, task; int found; struct task_context *tc; char buf[BUFSIZE]; value_index = 0; specified = SPECIFIED_NOTHING; shm = 0; sem = 0; msg = 0; verbose = 0; tc = NULL; while ((c = getopt(argcnt, args, "smMqn:")) != EOF) { switch(c) { case 's': sem = 1; break; case 'm': shm = 1; break; case 'M': shm = 1; verbose = 1; break; case 'q': msg = 1; break; case 'n': switch (str_to_context(optarg, &value, &tc)) { case STR_PID: case STR_TASK: break; case STR_INVALID: error(FATAL, "invalid task or pid value: %s\n", optarg); break; } break; default: cmd_usage(pc->curcmd, SYNOPSIS);; return; } } while (args[optind]) { if (value_index >= MAXARGS) error(FATAL, "too many id/member specified\n"); specified |= SPECIFIED_ID | SPECIFIED_ADDR; specified_value[value_index] = args[optind]; stol(args[optind], FAULT_ON_ERROR, NULL); optind++; value_index++; } if (THIS_KERNEL_VERSION < LINUX(2,6,0)) command_not_supported(); ipcs_init(); if (!shm && !sem && !msg) shm = sem = msg = 1; task = tc ? tc->task : pid_to_task(0); if (!value_index) { if (shm) dump_shared_memory(specified, 0, verbose, task); if (sem) dump_semaphore_arrays(specified, 0, 0, task); if (msg) dump_message_queues(specified, 0, 0, task); } else { open_tmpfile(); i = 0; while (i < value_index) { found = 0; value = stol(specified_value[i], FAULT_ON_ERROR, NULL); if (shm) found += dump_shared_memory(specified, value, verbose, task); if (sem) found += dump_semaphore_arrays(specified, value, 0, task); if (msg) found += dump_message_queues(specified, value, 0, task); if (!found) fprintf(pc->saved_fp, "invalid id or address: %s\n\n", specified_value[i]); i++; } fflush(fp); rewind(fp); while (fgets(buf, BUFSIZE, fp)) fprintf(pc->saved_fp, "%s", buf); close_tmpfile(); } } static int dump_shared_memory(int specified, ulong specified_value, int verbose, ulong task) { ulong nsproxy_p, ipc_ns_p; ulong ipc_ids_p; int (*ipc_search)(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int); int (*dump_shm)(ulong, int, ulong, int, int); char buf0[BUFSIZE]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE]; char buf5[BUFSIZE]; char buf6[BUFSIZE]; char buf7[BUFSIZE]; if (!verbose && specified == SPECIFIED_NOTHING) { fprintf(fp, "%s %s %s %s %s %s %s %s\n", mkstring(buf0, VADDR_PRLEN<=12?12:VADDR_PRLEN, LJUST, "SHMID_KERNEL"), mkstring(buf1, 8, LJUST, "KEY"), mkstring(buf2, 10, LJUST, "SHMID"), mkstring(buf3, 5, LJUST, "UID"), mkstring(buf4, 5, LJUST, "PERMS"), mkstring(buf5, 10, LJUST, "BYTES"), mkstring(buf6, 6, LJUST, "NATTCH"), mkstring(buf7, 6, LJUST, "STATUS")); } dump_shm = dump_shm_info; if (VALID_MEMBER(kern_ipc_perm_id)) { ipc_search = ipc_search_idr; } else { ipc_search = ipc_search_array; } if (symbol_exists("shm_ids")) { ipc_ids_p = symbol_value("shm_ids"); } else { readmem(task + OFFSET(task_struct_nsproxy), KVADDR, &nsproxy_p, sizeof(ulong), "task_struct.nsproxy", FAULT_ON_ERROR); if (!readmem(nsproxy_p + OFFSET(nsproxy_ipc_ns), KVADDR, &ipc_ns_p, sizeof(ulong), "nsproxy.ipc_ns", RETURN_ON_ERROR|QUIET)) error(FATAL, "cannot determine ipc_namespace location!\n"); if (MEMBER_SIZE("ipc_namespace","ids") == sizeof(ulong) * 3) readmem(ipc_ns_p + OFFSET(ipc_namespace_ids) + sizeof(ulong) * 2, KVADDR, &ipc_ids_p, sizeof(ulong), "ipc_namespace.ids[2]", FAULT_ON_ERROR); else ipc_ids_p = ipc_ns_p + OFFSET(ipc_namespace_ids) + 2 * SIZE(ipc_ids); } if (ipc_search(ipc_ids_p, specified, specified_value, dump_shm, verbose)) { return 1; } else { if (verbose && specified == SPECIFIED_NOTHING) { fprintf(fp, "%s %s %s %s %s %s %s %s\n", mkstring(buf0, VADDR_PRLEN<=12?12:VADDR_PRLEN, LJUST, "SHMID_KERNEL"), mkstring(buf1, 8, LJUST, "KEY"), mkstring(buf2, 10, LJUST, "SHMID"), mkstring(buf3, 5, LJUST, "UID"), mkstring(buf4, 5, LJUST, "PERMS"), mkstring(buf5, 10, LJUST, "BYTES"), mkstring(buf6, 6, LJUST, "NATTCH"), mkstring(buf7, 6, LJUST, "STATUS")); fprintf(fp, "(none allocated)\n\n"); } return 0; } } static int dump_semaphore_arrays(int specified, ulong specified_value, int verbose, ulong task) { ulong nsproxy_p, ipc_ns_p; ulong ipc_ids_p; int (*ipc_search)(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int); int (*dump_sem)(ulong, int, ulong, int, int); char buf0[BUFSIZE]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE]; char buf5[BUFSIZE]; if (specified == SPECIFIED_NOTHING) { fprintf(fp, "%s %s %s %s %s %s\n", mkstring(buf0, VADDR_PRLEN<=10?10:VADDR_PRLEN, LJUST, "SEM_ARRAY"), mkstring(buf1, 8, LJUST, "KEY"), mkstring(buf2, 10, LJUST, "SEMID"), mkstring(buf3, 5, LJUST, "UID"), mkstring(buf4, 5, LJUST, "PERMS"), mkstring(buf5, 10, LJUST, "NSEMS")); } dump_sem = dump_sem_info; if (VALID_MEMBER(kern_ipc_perm_id)) { ipc_search = ipc_search_idr; } else { ipc_search = ipc_search_array; } if (symbol_exists("sem_ids")) { ipc_ids_p = symbol_value("sem_ids"); } else { readmem(task + OFFSET(task_struct_nsproxy), KVADDR, &nsproxy_p, sizeof(ulong), "task_struct.nsproxy", FAULT_ON_ERROR); if (!readmem(nsproxy_p + OFFSET(nsproxy_ipc_ns), KVADDR, &ipc_ns_p, sizeof(ulong), "nsproxy.ipc_ns", FAULT_ON_ERROR|QUIET)) error(FATAL, "cannot determine ipc_namespace location!\n"); if (MEMBER_SIZE("ipc_namespace","ids") == sizeof(ulong) * 3) readmem(ipc_ns_p + OFFSET(ipc_namespace_ids), KVADDR, &ipc_ids_p, sizeof(ulong), "ipc_namespace.ids[2]", FAULT_ON_ERROR); else ipc_ids_p = ipc_ns_p + OFFSET(ipc_namespace_ids); } return ipc_search(ipc_ids_p, specified, specified_value, dump_sem, verbose); } static int dump_message_queues(int specified, ulong specified_value, int verbose, ulong task) { ulong nsproxy_p, ipc_ns_p; ulong ipc_ids_p; int (*ipc_search)(ulong, int, ulong, int (*)(ulong, int, ulong, int, int), int); int (*dump_msg)(ulong, int, ulong, int, int); char buf0[BUFSIZE]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE]; char buf5[BUFSIZE]; char buf6[BUFSIZE]; if (specified == SPECIFIED_NOTHING) { fprintf(fp, "%s %s %s %s %s %s %s\n", mkstring(buf0, VADDR_PRLEN<=10?10:VADDR_PRLEN, LJUST, "MSG_QUEUE"), mkstring(buf1, 8, LJUST, "KEY"), mkstring(buf2, 10, LJUST, "MSQID"), mkstring(buf3, 5, LJUST, "UID"), mkstring(buf4, 5, LJUST, "PERMS"), mkstring(buf5, 12, LJUST, "USED-BYTES"), mkstring(buf6, 12, LJUST, "MESSAGES")); } dump_msg = dump_msg_info; if (VALID_MEMBER(kern_ipc_perm_id)) { ipc_search = ipc_search_idr; } else { ipc_search = ipc_search_array; } if (symbol_exists("msg_ids")) { ipc_ids_p = symbol_value("msg_ids"); } else { readmem(task + OFFSET(task_struct_nsproxy), KVADDR, &nsproxy_p, sizeof(ulong), "task_struct.nsproxy", FAULT_ON_ERROR); if (!readmem(nsproxy_p + OFFSET(nsproxy_ipc_ns), KVADDR, &ipc_ns_p, sizeof(ulong), "nsproxy.ipc_ns", FAULT_ON_ERROR|QUIET)) error(FATAL, "cannot determine ipc_namespace location!\n"); if (MEMBER_SIZE("ipc_namespace","ids") == sizeof(ulong) * 3) readmem(ipc_ns_p + OFFSET(ipc_namespace_ids) + sizeof(ulong), KVADDR, &ipc_ids_p, sizeof(ulong), "ipc_namespace.ids[2]", FAULT_ON_ERROR); else ipc_ids_p = ipc_ns_p + OFFSET(ipc_namespace_ids) + SIZE(ipc_ids); } return ipc_search(ipc_ids_p, specified, specified_value, dump_msg, verbose); } /* * if shared memory information is stored in an array, use this function. */ static int ipc_search_array(ulong ipc_ids_p, int specified, ulong specified_value, int (*fn)(ulong, int, ulong, int, int), int verbose) { ulong entries_p; int max_id, i; ulong *array; int found = 0; int allocated = 0; readmem(ipc_ids_p + OFFSET(ipc_ids_entries), KVADDR, &entries_p, sizeof(ulong), "ipc_ids.entries", FAULT_ON_ERROR); readmem(ipc_ids_p + OFFSET(ipc_ids_max_id), KVADDR, &max_id, sizeof(int), "ipc_ids.max_id", FAULT_ON_ERROR); if (max_id < 0) { if (specified == SPECIFIED_NOTHING && !verbose) fprintf(fp, "(none allocated)\n\n"); return 0; } array = (ulong *)GETBUF(sizeof(ulong *) * (max_id + 1)); if (VALID_MEMBER(ipc_id_ary_p)) readmem(entries_p + OFFSET(ipc_id_ary_p), KVADDR, array, sizeof(ulong *) * (max_id + 1), "ipc_id_ary.p", FAULT_ON_ERROR); else readmem(entries_p, KVADDR, array, sizeof(ulong *)*(max_id+1), "ipc_id array", FAULT_ON_ERROR); for (i=0; i<=max_id; i++) { if (array[i] == 0) continue; if (fn(array[i], specified, specified_value, i, verbose)) { allocated++; found = 1; if (specified != SPECIFIED_NOTHING) break; } } if (specified == SPECIFIED_NOTHING && !verbose) { if (!allocated) fprintf(fp, "(none allocated)\n"); fprintf(fp, "\n"); } FREEBUF(array); if (found) return 1; else return 0; } /* * if shared memory information is stored by using idr, use this function to * get data. */ static int ipc_search_idr(ulong ipc_ids_p, int specified, ulong specified_value, int (*fn)(ulong, int, ulong, int, int), int verbose) { int i, in_use; ulong ipcs_idr_p; ulong ipc; int next_id, total; int found = 0; readmem(ipc_ids_p + OFFSET(ipc_ids_in_use), KVADDR, &in_use, sizeof(int), "ipc_ids.in_use", FAULT_ON_ERROR); ipcs_idr_p = ipc_ids_p + OFFSET(ipc_ids_ipcs_idr); if (!in_use) { if (specified == SPECIFIED_NOTHING && !verbose) fprintf(fp, "(none allocated)\n\n"); return 0; } if (VALID_MEMBER(idr_idr_rt)) { switch (ipcs_table.init_flags & (IDR_RADIX|IDR_XARRAY)) { case IDR_RADIX: gather_radix_tree_entries(ipcs_idr_p); break; case IDR_XARRAY: gather_xarray_entries(ipcs_idr_p); break; } for (i = 0; i < ipcs_table.cnt; i++) { ipc = (ulong)ipcs_table.lp[i].value; if (fn(ipc, specified, specified_value, UNUSED, verbose)) { found = 1; if (specified != SPECIFIED_NOTHING) break; } } if (ipcs_table.lp) FREEBUF(ipcs_table.lp); } else { for (total = 0, next_id = 0; total < in_use; next_id++) { ipc = idr_find(ipcs_idr_p, next_id); if (ipc == 0) continue; total++; if (fn(ipc, specified, specified_value, next_id, verbose)) { found = 1; if (specified != SPECIFIED_NOTHING) break; } } } if (!verbose && specified == SPECIFIED_NOTHING) fprintf(fp, "\n"); if (found || specified == SPECIFIED_NOTHING) return 1; else return 0; } /* * search every idr_layer */ ulong idr_find(ulong idp, int id) { ulong idr_layer_p; int layer; int idr_layers; int n; int index; readmem(idp + OFFSET(idr_top), KVADDR, &idr_layer_p, sizeof(ulong), "idr.top", FAULT_ON_ERROR); if (!idr_layer_p) return 0; if (VALID_MEMBER(idr_layer_layer)) { readmem(idr_layer_p + OFFSET(idr_layer_layer), KVADDR, &layer, sizeof(int), "idr_layer.layer", FAULT_ON_ERROR); n = (layer + 1) * ipcs_table.idr_bits; } else { readmem(idp + OFFSET(idr_layers), KVADDR, &idr_layers, sizeof(int), "idr.layers", FAULT_ON_ERROR); n = idr_layers * ipcs_table.idr_bits; } id &= MAX_ID_MASK; if (id >= (1 << n)) return 0; while (n > 0 && idr_layer_p) { n -= ipcs_table.idr_bits; index = (id >> n) & ((1 << ipcs_table.idr_bits) - 1); readmem(idr_layer_p + OFFSET(idr_layer_ary) + sizeof(ulong) * index, KVADDR, &idr_layer_p, sizeof(ulong), "idr_layer.ary", FAULT_ON_ERROR); } return idr_layer_p; } /* * only specified is not SPECIFIED_NOTHIND, and the specified_value is found, * then return 1 */ static int dump_shm_info(ulong shp, int specified, ulong specified_value, int id, int verbose) { struct shm_info shm_info; char buf[BUFSIZE]; char buf0[BUFSIZE]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE]; char buf5[BUFSIZE]; char buf6[BUFSIZE]; char buf7[BUFSIZE]; get_shm_info(&shm_info, shp, id); if (shm_info.deleted) return 0; if (((specified & SPECIFIED_ID) && shm_info.shmid == specified_value) || ((specified & SPECIFIED_ADDR) && shm_info.shmid_kernel == specified_value) || specified == SPECIFIED_NOTHING) { if (verbose || specified != SPECIFIED_NOTHING) { fprintf(fp, "%s %s %s %s %s %s %s %s\n", mkstring(buf0, VADDR_PRLEN<=12?12:VADDR_PRLEN, LJUST, "SHMID_KERNEL"), mkstring(buf1, 8, LJUST, "KEY"), mkstring(buf2, 10, LJUST, "SHMID"), mkstring(buf3, 5, LJUST, "UID"), mkstring(buf4, 5, LJUST, "PERMS"), mkstring(buf5, 10, LJUST, "BYTES"), mkstring(buf6, 6, LJUST, "NATTCH"), mkstring(buf7, 6, LJUST, "STATUS")); } fprintf(fp, "%s %08x %-10d %-5d %-5o %-10ld %-6ld %-s %-s\n", mkstring(buf, VADDR_PRLEN <= 12 ? 12 : VADDR_PRLEN, LJUST|LONG_HEX, (char *)shm_info.shmid_kernel), shm_info.key, shm_info.shmid, shm_info.uid, shm_info.perms & 0777, shm_info.bytes, shm_info.nattch, shm_info.perms & SHM_DEST ? "dest" : "", shm_info.perms & SHM_LOCKED ? "locked" : ""); if (verbose) { fprintf(fp, "PAGES ALLOCATED/RESIDENT/SWAPPED: %ld/%ld/%ld\n", (shm_info.bytes+PAGESIZE()-1) >> PAGESHIFT(), shm_info.rss, shm_info.swap); fprintf(fp, "INODE: %lx\n", shm_info.shm_inode); } if (verbose || specified != SPECIFIED_NOTHING) fprintf(fp, "\n"); return 1; } else return 0; } /* * only specified is not SPECIFIED_NOTHIND, and the specified_value is found, * then return 1 */ static int dump_sem_info(ulong shp, int specified, ulong specified_value, int id, int verbose) { struct sem_info sem_info; char buf[BUFSIZE]; char buf0[BUFSIZE]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE]; char buf5[BUFSIZE]; get_sem_info(&sem_info, shp, id); if (sem_info.deleted) return 0; if (((specified & SPECIFIED_ID) && sem_info.semid == specified_value) || ((specified & SPECIFIED_ADDR) && sem_info.sem_array == specified_value) || specified == SPECIFIED_NOTHING) { if (specified != SPECIFIED_NOTHING) { fprintf(fp, "%s %s %s %s %s %s\n", mkstring(buf0, VADDR_PRLEN<=10?10:VADDR_PRLEN, LJUST, "SEM_ARRAY"), mkstring(buf1, 8, LJUST, "KEY"), mkstring(buf2, 10, LJUST, "SEMID"), mkstring(buf3, 5, LJUST, "UID"), mkstring(buf4, 5, LJUST, "PERMS"), mkstring(buf5, 10, LJUST, "NSEMS")); } fprintf(fp, "%s %08x %-10d %-5d %-5o %-10ld\n", mkstring(buf, VADDR_PRLEN <= 10 ? 10 : VADDR_PRLEN, LJUST|LONG_HEX, (char *)sem_info.sem_array), sem_info.key, sem_info.semid, sem_info.uid, sem_info.perms & 0777, sem_info.nsems); if (specified != SPECIFIED_NOTHING) fprintf(fp, "\n"); return 1; } else return 0; } /* * only specified is not SPECIFIED_NOTHIND, and the specified_value is found, * then return 1 */ static int dump_msg_info(ulong shp, int specified, ulong specified_value, int id, int verbose) { struct msg_info msg_info; char buf[BUFSIZE]; char buf0[BUFSIZE]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE]; char buf5[BUFSIZE]; char buf6[BUFSIZE]; get_msg_info(&msg_info, shp, id); if (msg_info.deleted) return 0; if (((specified & SPECIFIED_ID) && msg_info.msgid == specified_value) || ((specified & SPECIFIED_ADDR) && msg_info.msg_queue == specified_value) || specified == SPECIFIED_NOTHING) { if (specified != SPECIFIED_NOTHING) { fprintf(fp, "%s %s %s %s %s %s %s\n", mkstring(buf0, VADDR_PRLEN<=10?10:VADDR_PRLEN, LJUST, "MSG_QUEUE"), mkstring(buf1, 8, LJUST, "KEY"), mkstring(buf2, 10, LJUST, "MSQID"), mkstring(buf3, 5, LJUST, "UID"), mkstring(buf4, 5, LJUST, "PERMS"), mkstring(buf5, 12, LJUST, "USED-BYTES"), mkstring(buf6, 12, LJUST, "MESSAGES")); } fprintf(fp, "%s %08x %-10d %-5d %-5o %-12ld %-12ld\n", mkstring(buf, VADDR_PRLEN <= 10 ? 10 : VADDR_PRLEN, LJUST|LONG_HEX, (char *)msg_info.msg_queue), msg_info.key, msg_info.msgid, msg_info.uid, msg_info.perms & 0777, msg_info.bytes, msg_info.messages); if (specified != SPECIFIED_NOTHING) fprintf(fp, "\n"); return 1; } else return 0; } static void get_shm_info(struct shm_info *shm_info, ulong shp, int id) { char buf[BUFSIZE]; ulong filep, dentryp, inodep; shm_info->shmid_kernel = shp - OFFSET(shmid_kernel_shm_perm); /* * cache shmid_kernel */ readmem(shm_info->shmid_kernel, KVADDR, buf, SIZE(shmid_kernel), "shmid_kernel", FAULT_ON_ERROR); shm_info->key = INT(buf + OFFSET(shmid_kernel_shm_perm) + OFFSET(kern_ipc_perm_key)); if (VALID_MEMBER(shmid_kernel_id)) shm_info->shmid = INT(buf + OFFSET(shmid_kernel_id)); else shm_info->shmid = INT(buf + OFFSET(shmid_kernel_shm_perm) + OFFSET(kern_ipc_perm_id)); shm_info->uid = UINT(buf + OFFSET(shmid_kernel_shm_perm) + OFFSET(kern_ipc_perm_uid)); if (BITS32()) shm_info->perms = USHORT(buf + OFFSET(shmid_kernel_shm_perm) + OFFSET(kern_ipc_perm_mode)); else shm_info->perms = UINT(buf + OFFSET(shmid_kernel_shm_perm) + OFFSET(kern_ipc_perm_mode)); shm_info->bytes = ULONG(buf + OFFSET(shmid_kernel_shm_segsz)); shm_info->nattch = ULONG(buf + OFFSET(shmid_kernel_shm_nattch)); filep = ULONG(buf + OFFSET(shmid_kernel_shm_file)); readmem(filep + OFFSET(file_f_dentry), KVADDR, &dentryp, sizeof(ulong), "file.f_dentry", FAULT_ON_ERROR); readmem(dentryp + OFFSET(dentry_d_inode), KVADDR, &inodep, sizeof(ulong), "dentry.d_inode", FAULT_ON_ERROR); /* * shm_inode here is the vfs_inode of struct shmem_inode_info */ shm_info->shm_inode = inodep; shm_info->rss = 0; shm_info->swap = 0; add_rss_swap(inodep, is_file_hugepages(filep), &shm_info->rss, &shm_info->swap); shm_info->deleted = UCHAR(buf + OFFSET(shmid_kernel_shm_perm) + OFFSET(kern_ipc_perm_deleted)); } static void get_sem_info(struct sem_info *sem_info, ulong shp, int id) { char buf[BUFSIZE]; sem_info->sem_array = shp - OFFSET(sem_array_sem_perm); /* * cache sem_array */ readmem(sem_info->sem_array, KVADDR, buf, SIZE(sem_array), "sem_array", FAULT_ON_ERROR); sem_info->key = INT(buf + OFFSET(sem_array_sem_perm) + OFFSET(kern_ipc_perm_key)); if (VALID_MEMBER(sem_array_sem_id)) sem_info->semid = INT(buf + OFFSET(sem_array_sem_id)); else if (VALID_MEMBER(kern_ipc_perm_id)) sem_info->semid = INT(buf + OFFSET(sem_array_sem_perm) + OFFSET(kern_ipc_perm_id)); else { ulong seq; seq = ULONG(buf + OFFSET(sem_array_sem_perm) + OFFSET(kern_ipc_perm_seq)); sem_info->semid = ipcs_table.seq_multiplier * seq + id; } sem_info->uid = UINT(buf + OFFSET(sem_array_sem_perm) + OFFSET(kern_ipc_perm_uid)); if (BITS32()) sem_info->perms = USHORT(buf + OFFSET(sem_array_sem_perm) + OFFSET(kern_ipc_perm_mode)); else sem_info->perms = UINT(buf + OFFSET(sem_array_sem_perm) + OFFSET(kern_ipc_perm_mode)); sem_info->nsems = ULONG(buf + OFFSET(sem_array_sem_nsems)); sem_info->deleted = UCHAR(buf + OFFSET(sem_array_sem_perm) + OFFSET(kern_ipc_perm_deleted)); } static void get_msg_info(struct msg_info *msg_info, ulong shp, int id) { char buf[BUFSIZE]; msg_info->msg_queue = shp - OFFSET(msg_queue_q_perm); /* * cache msg_queue */ readmem(msg_info->msg_queue, KVADDR, buf, SIZE(msg_queue), "msg_queue", FAULT_ON_ERROR); msg_info->key = INT(buf + OFFSET(msg_queue_q_perm) + OFFSET(kern_ipc_perm_key)); if (VALID_MEMBER(msg_queue_q_id)) msg_info->msgid = INT(buf + OFFSET(msg_queue_q_id)); else if (VALID_MEMBER(kern_ipc_perm_id)) msg_info->msgid = INT(buf + OFFSET(msg_queue_q_perm) + OFFSET(kern_ipc_perm_id)); else { ulong seq; seq = ULONG(buf + OFFSET(msg_queue_q_perm) + OFFSET(kern_ipc_perm_seq)); msg_info->msgid = ipcs_table.seq_multiplier * seq + id; } msg_info->uid = UINT(buf + OFFSET(msg_queue_q_perm) + OFFSET(kern_ipc_perm_uid)); if (BITS32()) msg_info->perms = USHORT(buf + OFFSET(msg_queue_q_perm) + OFFSET(kern_ipc_perm_mode)); else msg_info->perms = UINT(buf + OFFSET(msg_queue_q_perm) + OFFSET(kern_ipc_perm_mode)); msg_info->bytes = ULONG(buf + OFFSET(msg_queue_q_cbytes)); msg_info->messages = ULONG(buf + OFFSET(msg_queue_q_qnum)); msg_info->deleted = UCHAR(buf + OFFSET(msg_queue_q_perm) + OFFSET(kern_ipc_perm_deleted)); } /* * get rss & swap related to every shared memory, and get the total number of rss * & swap */ static void add_rss_swap(ulong inode_p, int hugepage, ulong *rss, ulong *swap) { unsigned long mapping_p, nr_pages; readmem(inode_p + OFFSET(inode_i_mapping), KVADDR, &mapping_p, sizeof(ulong), "inode.i_mapping", FAULT_ON_ERROR); readmem(mapping_p + OFFSET(address_space_nrpages), KVADDR, &nr_pages, sizeof(ulong), "address_space.nrpages", FAULT_ON_ERROR); if (hugepage) { unsigned long pages_per_hugepage; if (VALID_SIZE(hstate)) { unsigned long i_sb_p, hsb_p, hstate_p; unsigned int order; readmem(inode_p + OFFSET(inode_i_sb), KVADDR, &i_sb_p, sizeof(ulong), "inode.i_sb", FAULT_ON_ERROR); readmem(i_sb_p + OFFSET(super_block_s_fs_info), KVADDR, &hsb_p, sizeof(ulong), "super_block.s_fs_info", FAULT_ON_ERROR); readmem(hsb_p + OFFSET(hugetlbfs_sb_info_hstate), KVADDR, &hstate_p, sizeof(ulong), "hugetlbfs_sb_info.hstate", FAULT_ON_ERROR); readmem(hstate_p + OFFSET(hstate_order), KVADDR, &order, sizeof(uint), "hstate.order", FAULT_ON_ERROR); pages_per_hugepage = 1 << order; } else { unsigned long hpage_shift; /* * HPAGE_SHIFT is 21 after commit 83a5101b * (kernel > 2.6.24) */ if (THIS_KERNEL_VERSION > LINUX(2, 6, 24)) { hpage_shift = 21; } else { /* * HPAGE_SHIFT: * x86(PAE): 21 * x86(no PAE): 22 * x86_64: 21 */ if ((machine_type("X86") && !(machdep->flags & PAE))) hpage_shift = 22; else hpage_shift = 21; } pages_per_hugepage = (1 << hpage_shift) / PAGESIZE(); } *rss += pages_per_hugepage * nr_pages; } else { unsigned long swapped; *rss += nr_pages; readmem(inode_p - OFFSET(shmem_inode_info_vfs_inode) + OFFSET(shmem_inode_info_swapped), KVADDR, &swapped, sizeof(ulong), "shmem_inode_info.swapped", FAULT_ON_ERROR); *swap += swapped; } } static int is_file_hugepages(ulong file_p) { unsigned long f_op, sfd_p; again: readmem(file_p + OFFSET(file_f_op), KVADDR, &f_op, sizeof(ulong), "file.f_op", FAULT_ON_ERROR); if (f_op == ipcs_table.hugetlbfs_f_op_addr) return 1; if (ipcs_table.use_shm_f_op) { if (ipcs_table.shm_f_op_huge_addr != -1) { if (f_op == ipcs_table.shm_f_op_huge_addr) return 1; } else { if (f_op == ipcs_table.shm_f_op_addr) { readmem(file_p + OFFSET(file_private_data), KVADDR, &sfd_p, sizeof(ulong), "file.private_data", FAULT_ON_ERROR); readmem(sfd_p + OFFSET(shm_file_data_file), KVADDR, &file_p, sizeof(ulong), "shm_file_data.file", FAULT_ON_ERROR); goto again; } } } return 0; } static void gather_radix_tree_entries(ulong ipcs_idr_p) { long len; ipcs_table.cnt = do_radix_tree(ipcs_idr_p, RADIX_TREE_COUNT, NULL); if (ipcs_table.cnt) { len = sizeof(struct list_pair) * (ipcs_table.cnt+1); ipcs_table.lp = (struct list_pair *)GETBUF(len); ipcs_table.lp[0].index = ipcs_table.cnt; ipcs_table.cnt = do_radix_tree(ipcs_idr_p, RADIX_TREE_GATHER, ipcs_table.lp); } else ipcs_table.lp = NULL; } static void gather_xarray_entries(ulong ipcs_idr_p) { long len; ipcs_table.cnt = do_xarray(ipcs_idr_p, XARRAY_COUNT, NULL); if (ipcs_table.cnt) { len = sizeof(struct list_pair) * (ipcs_table.cnt+1); ipcs_table.lp = (struct list_pair *)GETBUF(len); ipcs_table.lp[0].index = ipcs_table.cnt; ipcs_table.cnt = do_xarray(ipcs_idr_p, XARRAY_GATHER, ipcs_table.lp); } else ipcs_table.lp = NULL; }