crash/ipcs.c

1164 lines
32 KiB
C

/* ipcs.c - provide information on ipc facilities
*
* Copyright (C) 2012 FUJITSU LIMITED
* Auther: Qiao Nuohan <qiaonuohan@cn.fujitsu.com>
*
* 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;
}