[3/4] Add SLAB allocator understanding.

Message ID 1454276692-7119-4-git-send-email-alnovak@suse.cz
State New, archived
Headers

Commit Message

Ales Novak Jan. 31, 2016, 9:44 p.m. UTC
  From: Vlastimil Babka <vbabka@suse.cz>

---
 gdb/kdump.c | 1259 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 1211 insertions(+), 48 deletions(-)
  

Comments

Kieran Bingham Feb. 1, 2016, 1:21 p.m. UTC | #1
This is interesting work!

I had been discussing how we might achieve managing this with Jan @
FOSDEM yesterday.

I believe a python implementation of this could be possible, and then
this code can live in the Kernel, and be split across architecture
specific layers where necessary to implement handling userspace
application boundaries from the Kernel Awareness.


I believe that if properly abstracted (which I think it looks like this
already will be), with kdump as a target layer, we can implement the
Kernel awareness layers above, so that they can be common to all of our
use case scenarios.

I have recently proposed creating a gdb.Target object, so that we can
layer the kernel specific code on top as a higher stratum layer. This
code can then live in the Kernel, and be version specific there, and
would then cooperate with the layers below, be that a live target over
JTAG, or a virtualised qemu/kvm, or a core dump file:

This way calling "(gdb) maintenance print target-stack" would look like:

The current target stack is:
  - Kernel Architecture Layer (specific implementations for ARM, ARMv8,
x86_64, i386... etc)
  - Kernel Awareness Layer (Common functionality, SLAB reader, Thread
awareness)
  - {remote (Remote serial target in gdb-specific protocol)}, or  -
{kdump , kdump interpretor layer}
  - exec (Local exec file)
  - None (None)

Please let me know about your thoughts on this, and how we can work
together.

--
Regards

Kieran


On 31/01/16 21:44, Ales Novak wrote:
> From: Vlastimil Babka <vbabka@suse.cz>
>
> ---
>  gdb/kdump.c | 1259 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 1211 insertions(+), 48 deletions(-)
>
> diff --git a/gdb/kdump.c b/gdb/kdump.c
> index b7b0ef5..e231559 100644
> --- a/gdb/kdump.c
> +++ b/gdb/kdump.c
> @@ -58,6 +58,7 @@
>  #include <sys/types.h>
>  #include <sys/stat.h>
>  #include <unistd.h>
> +#include <hashtab.h>
>  
>  
>  #include <dirent.h>
> @@ -73,6 +74,7 @@ typedef unsigned long long offset;
>  #define F_UNKN_ENDIAN    4
>  
>  unsigned long long kt_int_value (void *buff);
> +unsigned long long kt_long_value (void *buff);
>  unsigned long long kt_ptr_value (void *buff);
>  
>  int kt_hlist_head_for_each_node (char *addr, int(*func)(void *,offset), void *data);
> @@ -97,12 +99,17 @@ static void core_close (struct target_ops *self);
>  
>  typedef unsigned long long offset;
>  
> +static int nr_node_ids = 1;
> +static int nr_cpu_ids = 1;
> +
>  #define KDUMP_TYPE const char *_name; int _size; int _offset; struct type *_origtype
>  #define GET_GDB_TYPE(typ) types. typ ._origtype
>  #define GET_TYPE_SIZE(typ) (TYPE_LENGTH(GET_GDB_TYPE(typ)))
>  #define MEMBER_OFFSET(type,member) types. type. member
> -#define KDUMP_TYPE_ALLOC(type) kdump_type_alloc(GET_GDB_TYPE(type))
> -#define KDUMP_TYPE_GET(type,off,where) kdump_type_get(GET_GDB_TYPE(type), off, 0, where)
> +#define KDUMP_TYPE_ALLOC(type) kdump_type_alloc(GET_GDB_TYPE(type), 0)
> +#define KDUMP_TYPE_ALLOC_EXTRA(type,extra) kdump_type_alloc(GET_GDB_TYPE(type),extra)
> +#define KDUMP_TYPE_GET(type,off,where) kdump_type_get(GET_GDB_TYPE(type), off, 0, where, 0)
> +#define KDUMP_TYPE_GET_EXTRA(type,off,where,extra) kdump_type_get(GET_GDB_TYPE(type), off, 0, where, extra)
>  #define KDUMP_TYPE_FREE(where) free(where)
>  #define SYMBOL(var,name) do { var = lookup_symbol(name, NULL, VAR_DOMAIN, NULL); if (! var) { fprintf(stderr, "Cannot lookup_symbol(" name ")\n"); goto error; } } while(0)
>  #define OFFSET(x) (types.offsets. x)
> @@ -112,12 +119,12 @@ typedef unsigned long long offset;
>  #define GET_REGISTER_OFFSET(reg) (MEMBER_OFFSET(user_regs_struct,reg)/GET_TYPE_SIZE(_voidp))
>  #define GET_REGISTER_OFFSET_pt(reg) (MEMBER_OFFSET(pt_regs,reg)/GET_TYPE_SIZE(_voidp))
>  
> -#define list_for_each(pos, head) \
> -	for (pos = kt_ptr_value(head); pos != (head); KDUMP_TYPE_GET(_voidp,pos,&pos)
>  
> -#define list_head_for_each(head,lhb, _nxt) for((_nxt = kt_ptr_value(lhb)), KDUMP_TYPE_GET(list_head, _nxt, lhb);\
> -	(_nxt = kt_ptr_value(lhb)) != head; \
> -	KDUMP_TYPE_GET(list_head, _nxt, lhb))
> +#define list_head_for_each(head, lhb, _nxt)				      \
> +	for(KDUMP_TYPE_GET(list_head, head, lhb), _nxt = kt_ptr_value(lhb),   \
> +					KDUMP_TYPE_GET(list_head, _nxt, lhb); \
> +		_nxt != head;						      \
> +		_nxt = kt_ptr_value(lhb), KDUMP_TYPE_GET(list_head, _nxt, lhb))
>  
>  enum x86_64_regs {
>  	reg_RAX = 0,
> @@ -184,6 +191,10 @@ struct {
>  
>  	struct {
>  		KDUMP_TYPE;
> +	} _long;
> +
> +	struct {
> +		KDUMP_TYPE;
>  	} _voidp;
>  
>  	struct {
> @@ -345,10 +356,54 @@ struct {
>  		offset *percpu_offsets;
>  	} offsets;
>  
> +	struct {
> +		KDUMP_TYPE;
> +		offset flags;
> +		offset lru;
> +		offset first_page;
> +	} page;
> +
> +	struct {
> +		KDUMP_TYPE;
> +		offset array;
> +		offset name;
> +		offset list;
> +		offset nodelists;
> +		offset num;
> +		offset buffer_size;
> +	} kmem_cache;
> +
> +	struct {
> +		KDUMP_TYPE;
> +		offset slabs_partial;
> +		offset slabs_full;
> +		offset slabs_free;
> +		offset shared;
> +		offset alien;
> +		offset free_objects;
> +	} kmem_list3;
> +
> +	struct {
> +		KDUMP_TYPE;
> +		offset avail;
> +		offset limit;
> +		offset entry;
> +	} array_cache;
> +
> +	struct {
> +		KDUMP_TYPE;
> +		offset list;
> +		offset inuse;
> +		offset free;
> +		offset s_mem;
> +	} slab;
> +
>  	struct cpuinfo *cpu;
>  	int ncpus;
>  } types;
>  
> +unsigned PG_tail, PG_slab;
> +
>  struct task_info {
>  	offset task_struct;
>  	offset sp;
> @@ -404,6 +459,21 @@ unsigned long long kt_int_value (void *buff)
>  	return val;
>  }
>  
> +unsigned long long kt_long_value (void *buff)
> +{
> +	unsigned long long val;
> +
> +	if (GET_TYPE_SIZE(_long) == 4) {
> +		val = *(int32_t*)buff;
> +		if (types.flags & F_BIG_ENDIAN) val = __bswap_32(val);
> +	} else {
> +		val = *(int64_t*)buff;
> +		if (types.flags & F_BIG_ENDIAN) val = __bswap_64(val);
> +	}
> +
> +	return val;
> +}
> +
>  unsigned long long kt_ptr_value (void *buff)
>  {
>  	unsigned long long val;
> @@ -417,6 +487,49 @@ unsigned long long kt_ptr_value (void *buff)
>  	}
>  	return val;
>  }
> +
> +static unsigned long long kt_ptr_value_off (offset addr)
> +{
> +	char buf[8];
> +	unsigned len = GET_TYPE_SIZE(_voidp);
> +
> +	if (target_read_raw_memory(addr, (void *)buf, len)) {
> +		warning(_("Cannot read target memory addr=%llx length=%u\n"),
> +								addr, len);
> +		return -1;
> +	}
> +
> +	return kt_ptr_value(buf);
> +}
> +
> +static unsigned long long kt_int_value_off (offset addr)
> +{
> +	char buf[8];
> +	unsigned len = GET_TYPE_SIZE(_int);
> +
> +	if (target_read_raw_memory(addr, (void *)buf, len)) {
> +		warning(_("Cannot read target memory addr=%llx length=%u\n"),
> +								addr, len);
> +		return -1;
> +	}
> +
> +	return kt_int_value(buf);
> +}
> +
> +char * kt_strndup (offset src, int n);
> +char * kt_strndup (offset src, int n)
> +{
> +	char *dest = NULL;
> +	int ret, errno;
> +
> +	ret = target_read_string(src, &dest, n, &errno);
> +
> +	if (errno)
> +		fprintf(stderr, "target_read_string errno: %d\n", errno);
> +
> +	return dest;
> +}
> +
>  static offset get_symbol_address(const char *sname);
>  static offset get_symbol_address(const char *sname)
>  {
> @@ -519,35 +632,55 @@ static int kdump_type_member_init (struct type *type, const char *name, offset *
>  {
>  	int i;
>  	struct field *f;
> +	int ret;
> +	enum type_code tcode;
> +	offset off;
> +
>  	f = TYPE_FIELDS(type);
> -	for (i = 0; i < TYPE_NFIELDS(type); i ++) {
> -		if (! strcmp(f->name, name)) {
> -			*poffset = (f->loc.physaddr >> 3);
> +	for (i = 0; i < TYPE_NFIELDS(type); i++, f++) {
> +		//printf("fieldname \'%s\'\n", f->name);
> +		off = (f->loc.physaddr >> 3);
> +		if (!strcmp(f->name, name)) {
> +			*poffset = off;
>  			return 0;
>  		}
> -		f++;
> +		if (strlen(f->name))
> +			continue;
> +		tcode = TYPE_CODE(f->type);
> +		if (tcode == TYPE_CODE_UNION || tcode == TYPE_CODE_STRUCT) {
> +			//printf("recursing into unnamed union/struct\n");
> +			ret = kdump_type_member_init(f->type, name, poffset);
> +			if (ret != -1) {
> +				*poffset += off;
> +				return ret;
> +			}
> +		}
>  	}
>  	return -1;
>  }
>  
> -static void *kdump_type_alloc(struct type *type)
> +static void *kdump_type_alloc(struct type *type, size_t extra_size)
>  {
>  	int allocated = 0;
>  	void *buff;
>  
>  	allocated = 1;
> -	buff = malloc(TYPE_LENGTH(type));
> +	buff = malloc(TYPE_LENGTH(type) + extra_size);
>  	if (buff == NULL) {
> -		warning(_("Cannot allocate memory of %d length\n"), (int)TYPE_LENGTH(type));
> +		warning(_("Cannot allocate memory of %u length + %lu extra\n"),
> +					TYPE_LENGTH(type), extra_size);
>  		return NULL;
>  	}
>  	return buff;
>  }
>  
> -static int kdump_type_get(struct type *type, offset addr, int pos, void *buff)
> +static int kdump_type_get(struct type *type, offset addr, int pos, void *buff,
> +							size_t extra_size)
>  {
> -	if (target_read_raw_memory(addr + (TYPE_LENGTH(type)*pos), buff, TYPE_LENGTH(type))) {
> -		warning(_("Cannot read target memory of %d length\n"), (int)TYPE_LENGTH(type));
> +	if (target_read_raw_memory(addr + (TYPE_LENGTH(type)*pos), buff,
> +					TYPE_LENGTH(type) + extra_size)) {
> +		warning(_("Cannot read target memory of %u length + %lu extra\n"),
> +					TYPE_LENGTH(type), extra_size);
>  		return 1;
>  	}
>  	return 0;
> @@ -568,7 +701,8 @@ int kdump_types_init(int flags)
>  	#define INIT_BASE_TYPE_(name,tname) if(kdump_type_init(&types. tname ._origtype, &types. tname ._size, #name, T_BASE)) { fprintf(stderr, "Cannot base find type \'%s\'", #name); break; }
>  	#define INIT_REF_TYPE(name) if(kdump_type_init(&types. name ._origtype, &types. name ._size, #name, T_REF)) { fprintf(stderr, "Cannot ref find type \'%s\'", #name); break; }
>  	#define INIT_REF_TYPE_(name,tname) if(kdump_type_init(&types. tname ._origtype, &types. tname ._size, #name, T_REF)) { fprintf(stderr, "Cannot ref find type \'%s\'", #name); break; }
> -	#define INIT_STRUCT_MEMBER(sname,mname) if(kdump_type_member_init(types. sname ._origtype, #mname, &types. sname . mname)) { break; }
> +	#define INIT_STRUCT_MEMBER(sname,mname) if(kdump_type_member_init(types. sname ._origtype, #mname, &types. sname . mname)) \
> +		{ fprintf(stderr, "Cannot find struct \'%s\' member \'%s\'", #sname, #mname); break; }
>  
>  	/** initialize member with different name than the containing one */
>  	#define INIT_STRUCT_MEMBER_(sname,mname,mmname) if(kdump_type_member_init(types. sname ._origtype, #mname, &types. sname . mmname)) { break; }
> @@ -576,8 +710,9 @@ int kdump_types_init(int flags)
>  	/** don't fail if the member is not present */
>  	#define INIT_STRUCT_MEMBER__(sname,mname) kdump_type_member_init(types. sname ._origtype, #mname, &types. sname . mname)
>  	do {
> -		INIT_BASE_TYPE_(int,_int);
> -		INIT_REF_TYPE_(void,_voidp);
> +		INIT_BASE_TYPE_(int,_int); 
> +		INIT_BASE_TYPE_(long,_long);
> +		INIT_REF_TYPE_(void,_voidp); 
>  
>  		INIT_STRUCT(list_head);
>  		INIT_STRUCT_MEMBER(list_head,prev);
> @@ -728,9 +863,43 @@ int kdump_types_init(int flags)
>  			INIT_STRUCT_MEMBER__(ppc_pt_regs, rx6);
>  			INIT_STRUCT_MEMBER__(ppc_pt_regs, rx7);
>  		}
> +		INIT_STRUCT(page);
> +		INIT_STRUCT_MEMBER(page, flags);
> +		INIT_STRUCT_MEMBER(page, lru);
> +		INIT_STRUCT_MEMBER(page, first_page);
> +
> +		INIT_STRUCT(kmem_cache);
> +		INIT_STRUCT_MEMBER(kmem_cache, name);
> +		INIT_STRUCT_MEMBER_(kmem_cache, next, list);
> +		INIT_STRUCT_MEMBER(kmem_cache, nodelists);
> +		INIT_STRUCT_MEMBER(kmem_cache, num);
> +		INIT_STRUCT_MEMBER(kmem_cache, array);
> +		INIT_STRUCT_MEMBER(kmem_cache, buffer_size);
> +
> +		INIT_STRUCT(kmem_list3);
> +		INIT_STRUCT_MEMBER(kmem_list3, slabs_partial);
> +		INIT_STRUCT_MEMBER(kmem_list3, slabs_full);
> +		INIT_STRUCT_MEMBER(kmem_list3, slabs_free);
> +		INIT_STRUCT_MEMBER(kmem_list3, shared);
> +		INIT_STRUCT_MEMBER(kmem_list3, alien);
> +		INIT_STRUCT_MEMBER(kmem_list3, free_objects);
> +
> +		INIT_STRUCT(array_cache);
> +		INIT_STRUCT_MEMBER(array_cache, avail);
> +		INIT_STRUCT_MEMBER(array_cache, limit);
> +		INIT_STRUCT_MEMBER(array_cache, entry);
> +
> +		INIT_STRUCT(slab);
> +		INIT_STRUCT_MEMBER(slab, list);
> +		INIT_STRUCT_MEMBER(slab, inuse);
> +		INIT_STRUCT_MEMBER(slab, free);
> +		INIT_STRUCT_MEMBER(slab, s_mem);
>  		ret = 0;
>  	} while(0);
>  
> +	PG_tail = get_symbol_value("PG_tail");
> +	PG_slab = get_symbol_value("PG_slab");
> +
>  	if (ret) {
>  		fprintf(stderr, "Cannot init types\n");
>  	}
> @@ -738,6 +907,148 @@ int kdump_types_init(int flags)
>  	return ret;
>  }
>  
> +struct list_iter {
> +	offset curr;
> +	offset prev;
> +	offset head;
> +	offset last;
> +	offset fast;
> +	int cont;
> +	int error;
> +};
> +
> +static void list_first_from(struct list_iter *iter, offset o_head)
> +{
> +	char b_head[GET_TYPE_SIZE(list_head)];
> +
> +	iter->fast = 0;
> +	iter->error = 0;
> +	iter->cont = 1;
> +
> +	if (KDUMP_TYPE_GET(list_head, o_head, b_head)) {
> +		warning(_("Could not read list_head %llx in list_first()\n"),
> +								o_head);
> +		iter->error = 1;
> +		iter->cont = 0;
> +		return;
> +	}
> +
> +	iter->curr = o_head;
> +	iter->last = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, prev));
> +
> +	iter->head = o_head;
> +	iter->prev = iter->last;
> +}
> +
> +static void list_first(struct list_iter *iter, offset o_head)
> +{
> +	char b_head[GET_TYPE_SIZE(list_head)];
> +
> +	iter->fast = 0;
> +	iter->error = 0;
> +	iter->cont = 1;
> +
> +	if (KDUMP_TYPE_GET(list_head, o_head, b_head)) {
> +		warning(_("Could not read list_head %llx in list_first()\n"),
> +								o_head);
> +		iter->error = 1;
> +		iter->cont = 0;
> +		return;
> +	}
> +
> +	iter->curr = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, next));
> +	iter->last = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, prev));
> +
> +	/* Empty list */
> +	if (iter->curr == o_head) {
> +		if (iter->last != o_head) {
> +			warning(_("list_head %llx is empty, but prev points to %llx\n"),
> +							o_head,	iter->last);
> +			iter->error = 1;
> +		}
> +		iter->cont = 0;
> +		return;
> +	}
> +
> +	iter->head = o_head;
> +	iter->prev = o_head;
> +}
> +
> +static void list_next(struct list_iter *iter)
> +{
> +	char b_head[GET_TYPE_SIZE(list_head)];
> +	offset o_next, o_prev;
> +
> +	if (KDUMP_TYPE_GET(list_head, iter->curr, b_head)) {
> +		warning(_("Could not read list_head %llx in list_next()\n"),
> +								iter->curr);
> +		iter->error = 1;
> +		iter->cont = 0;
> +		return;
> +	}
> +
> +	o_next = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, next));
> +	o_prev = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, prev));
> +
> +	if (o_next == iter->head) {
> +		if (iter->curr != iter->last) {
> +			warning(_("list item %llx appears to be last, but list_head %llx ->prev points to %llx\n"),
> +						iter->curr, iter->head,
> +						iter->last);
> +			iter->error = 1;
> +		}
> +		iter->cont = 0;
> +		return;
> +	}
> +
> +	if (o_prev != iter->prev) {
> +		warning(_("list item %llx ->next is %llx but the latter's ->prev is %llx\n"),
> +					iter->prev, iter->curr, o_prev);
> +		iter->error = 1;
> +		/*
> +		 * broken ->prev link means that there might be cycle that
> +		 * does not include head; start detecting cycles
> +		 */
> +		if (!iter->fast)
> +			iter->fast = iter->curr;
> +	}
> +
> +	/*
> +	 * Are we detecting cycles? If so, advance iter->fast to
> +	 * iter->curr->next->next and compare iter->curr to both next's
> +	 * (Floyd's Tortoise and Hare algorithm)
> +	 *
> +	 */
> +	if (iter->fast) {
> +		int i = 2;
> +		while(i--) {
> +			/*
> +			 *  Simply ignore failure to read fast->next, the next
> +			 *  call to list_next() will find out anyway.
> +			 */
> +			if (KDUMP_TYPE_GET(list_head, iter->fast, b_head))
> +				break;
> +			iter->fast = kt_ptr_value(
> +				b_head + MEMBER_OFFSET(list_head, next));
> +			if (iter->curr == iter->fast) {
> +				warning(_("list_next() detected cycle, aborting traversal\n"));
> +				iter->error = 1;
> +				iter->cont = 0;
> +				return;
> +			}
> +		}
> +	}
> +
> +	iter->prev = iter->curr;
> +	iter->curr = o_next;
> +}
> +
> +#define list_for_each(iter, o_head) \
> +	for (list_first(&(iter), o_head); (iter).cont; list_next(&(iter)))
> +
> +#define list_for_each_from(iter, o_head) \
> +	for (list_first_from(&(iter), o_head); (iter).cont; list_next(&(iter)))
> +
>  int kt_hlist_head_for_each_node (char *addr, int(*func)(void *,offset), void *data)
>  {
>  	char *b = NULL;
> @@ -995,7 +1306,8 @@ static int add_task(offset off_task, int *pid_reserve, char *task)
>  			 * FIXME: use the size obtained from debuginfo
>  			 */
>  			rsp += 0x148;
> -			target_read_raw_memory(rsp - 0x8 * (1 + 6), (void*)regs, 0x8 * 6);
> +			if (target_read_raw_memory(rsp - 0x8 * (1 + 6), (void*)regs, 0x8 * 6))
> +				warning(_("Could not read regs\n"));
>  
>  			regcache_raw_supply(rc, 15, &regs[5]);
>  			regcache_raw_supply(rc, 14, &regs[4]);
> @@ -1026,7 +1338,6 @@ static int add_task(offset off_task, int *pid_reserve, char *task)
>  			REG(reg_RSP,sp);
>  			task_info->sp = reg;
>  			REG(reg_RIP,ip);
> -			printf ("task %p cpu %02d rip = %p\n", (void*)task_info->task_struct, cpu, reg);
>  			task_info->ip = reg;
>  			REG(reg_RAX,ax);
>  			REG(reg_RCX,cx);
> @@ -1092,13 +1403,860 @@ static int add_task(offset off_task, int *pid_reserve, char *task)
>  	return 0;
>  }
>  
> +struct list_head {
> +	offset next;
> +	offset prev;
> +};
> +
> +struct page {
> +	unsigned long flags;
> +	struct list_head lru;
> +	offset first_page;
> +	int valid;
> +};
> +
> +enum slab_type {
> +	slab_partial,
> +	slab_full,
> +	slab_free
> +};
> +
> +static const char *slab_type_names[] = {
> +	"partial",
> +	"full",
> +	"free"
> +};
> +
> +enum ac_type {
> +	ac_percpu,
> +	ac_shared,
> +	ac_alien
> +};
> +
> +static const char *ac_type_names[] = {
> +	"percpu",
> +	"shared",
> +	"alien"
> +};
> +
> +typedef unsigned int kmem_bufctl_t;
> +#define BUFCTL_END      (((kmem_bufctl_t)(~0U))-0)
> +#define BUFCTL_FREE     (((kmem_bufctl_t)(~0U))-1)
> +#define BUFCTL_ACTIVE   (((kmem_bufctl_t)(~0U))-2)
> +#define SLAB_LIMIT      (((kmem_bufctl_t)(~0U))-3)
> +
> +
> +struct kmem_cache {
> +	offset o_cache;
> +	const char *name;
> +	unsigned int num;
> +	htab_t obj_ac;
> +	unsigned int buffer_size;
> +	int array_caches_inited;
> +	int broken;
> +};
> +
> +struct kmem_slab {
> +	offset o_slab;
> +	kmem_bufctl_t free;
> +	unsigned int inuse;
> +	offset s_mem;
> +	kmem_bufctl_t *bufctl;
> +};
> +
> +/* Cache of kmem_cache structs indexed by offset */
> +static htab_t kmem_cache_cache;
> +
> +/* List_head of all kmem_caches */
> +offset o_slab_caches;
> +
> +/* Just get the least significant bits of the offset */
> +static hashval_t kmem_cache_hash(const void *p)
> +{
> +	return ((struct kmem_cache*)p)->o_cache;
> +}
> +
> +static int kmem_cache_eq(const void *cache, const void *off)
> +{
> +	return (((struct kmem_cache*)cache)->o_cache == *(offset *)off);
> +}
> +
> +struct kmem_ac {
> +	offset offset;
> +	enum ac_type type;
> +	/* At which node cache resides (-1 for percpu) */
> +	int at_node;
> +	/* For which node or cpu the cache is (-1 for shared) */
> +	int for_node_cpu;
> +};
> +
> +/* A mapping between object's offset and array_cache */
> +struct kmem_obj_ac {
> +	offset obj;
> +	struct kmem_ac *ac;
> +};
> +
> +static hashval_t kmem_ac_hash(const void *p)
> +{
> +	return ((struct kmem_obj_ac*)p)->obj;
> +}
> +
> +static int kmem_ac_eq(const void *obj, const void *off)
> +{
> +	return (((struct kmem_obj_ac*)obj)->obj == *(offset *)off);
> +}
> +
> +//FIXME: support the CONFIG_PAGEFLAGS_EXTENDED variant?
> +#define PageTail(page)	(page.flags & 1UL << PG_tail)
> +#define PageSlab(page)	(page.flags & 1UL << PG_slab)
> +
> +//TODO: get this via libkdumpfile somehow?
> +#define VMEMMAP_START	0xffffea0000000000UL
> +#define PAGE_SHIFT	12
> +
> +static unsigned long long memmap = VMEMMAP_START;
> +
> +static offset pfn_to_page_memmap(unsigned long pfn)
> +{
> +	return memmap + pfn*GET_TYPE_SIZE(page);
> +}
> +
> +//TODO: once the config querying below works, support all variants
> +#define pfn_to_page(pfn) pfn_to_page_memmap(pfn)
> +
> +static kdump_paddr_t transform_memory(kdump_paddr_t addr);
> +
> +static unsigned long addr_to_pfn(offset addr)
> +{
> +	kdump_paddr_t pa = transform_memory(addr);
> +
> +	return pa >> PAGE_SHIFT;
> +}
> +
> +#define virt_to_opage(addr)	pfn_to_page(addr_to_pfn(addr))
> +static int check_slab_obj(offset obj);
> +static int init_kmem_caches(void);
> +static struct page virt_to_head_page(offset addr);
> +
> +
> +//TODO: have some hashtable-based cache as well?
> +static struct kmem_slab *
> +init_kmem_slab(struct kmem_cache *cachep, offset o_slab)
> +{
> +	char b_slab[GET_TYPE_SIZE(slab)];
> +	struct kmem_slab *slab;
> +	offset o_bufctl = o_slab + GET_TYPE_SIZE(slab);
> +	size_t bufctl_size = cachep->num * sizeof(kmem_bufctl_t);
> +	//FIXME: use target's kmem_bufctl_t typedef, which didn't work in
> +	//INIT_BASE_TYPE though
> +	size_t bufctl_size_target = cachep->num * GET_TYPE_SIZE(_int);
> +	char b_bufctl[bufctl_size_target];
> +	int i;
> +
> +	if (KDUMP_TYPE_GET(slab, o_slab, b_slab)) {
> +		warning(_("error reading struct slab %llx of cache %s\n"),
> +							o_slab, cachep->name);
> +		return NULL;
> +	}
> +
> +	slab = malloc(sizeof(struct kmem_slab));
> +
> +	slab->o_slab = o_slab;
> +	slab->inuse = kt_int_value(b_slab + MEMBER_OFFSET(slab, inuse));
> +	slab->free = kt_int_value(b_slab + MEMBER_OFFSET(slab, free));
> +	slab->s_mem = kt_ptr_value(b_slab + MEMBER_OFFSET(slab, s_mem));
> +
> +	slab->bufctl = malloc(bufctl_size);
> +	if (target_read_raw_memory(o_bufctl, (void *) b_bufctl,
> +				bufctl_size_target)) {
> +		warning(_("error reading bufctl %llx of slab %llx of cache %s\n"),
> +						o_bufctl, o_slab, cachep->name);
> +		for (i = 0; i < cachep->num; i++)
> +			slab->bufctl[i] = BUFCTL_END;
> +
> +		return slab;
> +	}
> +
> +	for (i = 0; i < cachep->num; i++)
> +		slab->bufctl[i] = kt_int_value(b_bufctl + i*GET_TYPE_SIZE(_int));
> +
> +	return slab;
> +}
> +
> +static void free_kmem_slab(struct kmem_slab *slab)
> +{
> +	free(slab->bufctl);
> +	free(slab);
> +}
> +
> +static unsigned int
> +check_kmem_slab(struct kmem_cache *cachep, struct kmem_slab *slab,
> +							enum slab_type type)
> +{
> +	unsigned int counted_free = 0;
> +	kmem_bufctl_t i;
> +	offset o_slab = slab->o_slab;
> +	offset o_obj, o_prev_obj = 0;
> +	struct page page;
> +	offset o_page_cache, o_page_slab;
> +
> +	i = slab->free;
> +	while (i != BUFCTL_END) {
> +		counted_free++;
> +
> +		if (counted_free > cachep->num) {
> +			printf("free bufctl cycle detected in slab %llx\n", o_slab);
> +			break;
> +		}
> +		if (i > cachep->num) {
> +			printf("bufctl value overflow (%d) in slab %llx\n", i, o_slab);
> +			break;
> +		}
> +
> +		i = slab->bufctl[i];
> +	}
> +
> +//	printf("slab inuse=%d cnt_free=%d num=%d\n", slab->inuse, counted_free,
> +//								cachep->num);
> +
> +	if (slab->inuse + counted_free != cachep->num)
> +		 printf("slab %llx #objs mismatch: inuse=%d + cnt_free=%d != num=%d\n",
> +				o_slab, slab->inuse, counted_free, cachep->num);
> +
> +	switch (type) {
> +	case slab_partial:
> +		if (!slab->inuse)
> +			printf("slab %llx has zero inuse but is on slabs_partial\n", o_slab);
> +		else if (slab->inuse == cachep->num)
> +			printf("slab %llx is full (%d) but is on slabs_partial\n", o_slab, slab->inuse);
> +		break;
> +	case slab_full:
> +		if (!slab->inuse)
> +			printf("slab %llx has zero inuse but is on slabs_full\n", o_slab);
> +		else if (slab->inuse < cachep->num)
> +			printf("slab %llx has %d/%d inuse but is on slabs_full\n", o_slab, slab->inuse, cachep->num);
> +		break;
> +	case slab_free:
> +		if (slab->inuse)
> +			printf("slab %llx has %d/%d inuse but is on slabs_empty\n", o_slab, slab->inuse, cachep->num);
> +		break;
> +	default:
> +		exit(1);
> +	}
> +
> +	for (i = 0; i < cachep->num; i++) {
> +		o_obj = slab->s_mem + i * cachep->buffer_size;
> +		if (o_prev_obj >> PAGE_SHIFT == o_obj >> PAGE_SHIFT)
> +			continue;
> +
> +		o_prev_obj = o_obj;
> +		page = virt_to_head_page(o_obj);
> +		if (!page.valid) {
> +			warning(_("slab %llx object %llx could not read struct page\n"),
> +					o_slab, o_obj);
> +			continue;
> +		}
> +		if (!PageSlab(page))
> +			warning(_("slab %llx object %llx is not on PageSlab page\n"),
> +					o_slab, o_obj);
> +		o_page_cache = page.lru.next;
> +		o_page_slab = page.lru.prev;
> +
> +		if (o_page_cache != cachep->o_cache)
> +			warning(_("cache %llx (%s) object %llx is on page where lru.next points to %llx and not the cache\n"),
> +					cachep->o_cache, cachep->name, o_obj,
> +					o_page_cache);
> +		if (o_page_slab != o_slab)
> +			warning(_("slab %llx object %llx is on page where lru.prev points to %llx and not the slab\n"),
> +					o_slab, o_obj, o_page_slab);
> +	}
> +
> +	return counted_free;
> +}
> +
> +static unsigned long
> +check_kmem_slabs(struct kmem_cache *cachep, offset o_slabs,
> +							enum slab_type type)
> +{
> +	struct list_iter iter;
> +	offset o_slab;
> +	struct kmem_slab *slab;
> +	unsigned long counted_free = 0;
> +
> +//	printf("checking slab list %llx type %s\n", o_slabs,
> +//							slab_type_names[type]);
> +
> +	list_for_each(iter, o_slabs) {
> +		o_slab = iter.curr - MEMBER_OFFSET(slab, list);
> +//		printf("found slab: %llx\n", o_slab);
> +		slab = init_kmem_slab(cachep, o_slab);
> +		if (!slab)
> +			continue;
> +
> +		counted_free += check_kmem_slab(cachep, slab, type);
> +		free_kmem_slab(slab);
> +	}
> +
> +	return counted_free;
> +}
> +
> +/* Check that o_obj points to an object on slab of kmem_cache */
> +static void check_kmem_obj(struct kmem_cache *cachep, offset o_obj)
> +{
> +	struct page page;
> +	offset o_cache, o_slab;
> +	offset obj_base;
> +	unsigned int idx;
> +	struct kmem_slab *slabp;
> +
> +	page = virt_to_head_page(o_obj);
> +
> +	if (!PageSlab(page))
> +		warning(_("object %llx is not on PageSlab page\n"), o_obj);
> +
> +	o_cache = page.lru.next;
> +	if (o_cache != cachep->o_cache)
> +		warning(_("object %llx is on page that should belong to cache "
> +				"%llx (%s), but lru.next points to %llx\n"),
> +				o_obj, cachep->o_cache, cachep->name, o_obj);
> +
> +	o_slab = page.lru.prev;
> +	slabp = init_kmem_slab(cachep, o_slab);
> +
> +	//TODO: check also that slabp is in appropriate lists? could be quite slow...
> +	if (!slabp)
> +		return;
> +
> +	//TODO: kernel implementation uses reciprocal_divide, check?
> +	idx = (o_obj - slabp->s_mem) / cachep->buffer_size;
> +	obj_base = slabp->s_mem + idx * cachep->buffer_size;
> +
> +	if (obj_base != o_obj)
> +		warning(_("pointer %llx should point to beginning of object "
> +				"but object's address is %llx\n"), o_obj,
> +				obj_base);
> +
> +	if (idx >= cachep->num)
> +		warning(_("object %llx has index %u, but there should be only "
> +				"%u objects on slabs of cache %llx"),
> +				o_obj, idx, cachep->num, cachep->o_cache);
> +}
> +
> +static void init_kmem_array_cache(struct kmem_cache *cachep,
> +		offset o_array_cache, char *b_array_cache, enum ac_type type,
> +		int id1, int id2)
> +{
> +	unsigned int avail, limit, i;
> +	char *b_entries;
> +	offset o_entries = o_array_cache + MEMBER_OFFSET(array_cache, entry);
> +	offset o_obj;
> +	void **slot;
> +	struct kmem_ac *ac;
> +	struct kmem_obj_ac *obj_ac;
> +
> +	avail = kt_int_value(b_array_cache + MEMBER_OFFSET(array_cache, avail));
> +	limit = kt_int_value(b_array_cache + MEMBER_OFFSET(array_cache, limit));
> +
> +//	printf("found %s[%d,%d] array_cache %llx\n", ac_type_names[type],
> +//						id1, id2, o_array_cache);
> +//	printf("avail=%u limit=%u entries=%llx\n", avail, limit, o_entries);
> +
> +	if (avail > limit)
> +		printf("array_cache %llx has avail=%d > limit=%d\n",
> +						o_array_cache, avail, limit);
> +
> +	if (!avail)
> +		return;
> +
> +	ac = malloc(sizeof(struct kmem_ac));
> +	ac->offset = o_array_cache;
> +	ac->type = type;
> +	ac->at_node = id1;
> +	ac->for_node_cpu = id2;
> +
> +	b_entries = malloc(avail * GET_TYPE_SIZE(_voidp));
> +
> +	if (target_read_raw_memory(o_entries, (void *)b_entries,
> +					avail *	GET_TYPE_SIZE(_voidp))) {
> +		warning(_("could not read entries of array_cache %llx of cache %s\n"),
> +						o_array_cache, cachep->name);
> +		goto done;
> +	}
> +
> +	for (i = 0; i < avail; i++) {
> +		o_obj = kt_ptr_value(b_entries + i * GET_TYPE_SIZE(_voidp));
> +		//printf("cached obj: %llx\n", o_obj);
> +
> +		slot = htab_find_slot_with_hash(cachep->obj_ac, &o_obj, o_obj,
> +								INSERT);
> +
> +		if (*slot)
> +			printf("obj %llx already in array_cache!\n", o_obj);
> +
> +		obj_ac = malloc(sizeof(struct kmem_obj_ac));
> +		obj_ac->obj = o_obj;
> +		obj_ac->ac = ac;
> +
> +		*slot = obj_ac;
> +
> +		check_kmem_obj(cachep, o_obj);
> +	}
> +
> +done:
> +	free(b_entries);
> +}
> +
> +/* Array of array_caches, such as kmem_cache.array or *kmem_list3.alien */
> +static void init_kmem_array_caches(struct kmem_cache *cachep, char * b_caches,
> +					int id1, int nr_ids, enum ac_type type)
> +{
> +	char b_array_cache[GET_TYPE_SIZE(array_cache)];
> +	offset o_array_cache;
> +	int id;
> +
> +	for (id = 0; id < nr_ids; id++, b_caches += GET_TYPE_SIZE(_voidp)) {
> +		/*
> +		 * A node cannot have alien cache on the same node, but some
> +		 * kernels (-xen) apparently don't have the corresponding
> +		 * array_cache pointer NULL, so skip it now.
> +		 */
> +		if (type == ac_alien && id1 == id)
> +			continue;
> +		o_array_cache = kt_ptr_value(b_caches);
> +		if (!o_array_cache)
> +			continue;
> +		if (KDUMP_TYPE_GET(array_cache, o_array_cache, b_array_cache)) {
> +			warning(_("could not read array_cache %llx of cache %s type %s id1=%d id2=%d\n"),
> +					o_array_cache, cachep->name,
> +					ac_type_names[type], id1,
> +					type == ac_shared ? -1 : id);
> +			continue;
> +		}
> +		init_kmem_array_cache(cachep, o_array_cache, b_array_cache,
> +			type, id1, type == ac_shared ? -1 : id);
> +	}
> +}
> +
> +static void init_kmem_list3_arrays(struct kmem_cache *cachep, offset o_list3,
> +								int nid)
> +{
> +	char b_list3[GET_TYPE_SIZE(kmem_list3)];
> +	char *b_shared_caches;
> +	offset o_alien_caches;
> +	char b_alien_caches[nr_node_ids * GET_TYPE_SIZE(_voidp)];
> +
> +	if (KDUMP_TYPE_GET(kmem_list3, o_list3, b_list3)) {
> +                warning(_("error reading kmem_list3 %llx of nid %d of kmem_cache %llx name %s\n"),
> +				o_list3, nid, cachep->o_cache, cachep->name);
> +		return;
> +	}
> +
> +	/* This is a single pointer, but treat it as array to reuse code */
> +	b_shared_caches = b_list3 + MEMBER_OFFSET(kmem_list3, shared);
> +	init_kmem_array_caches(cachep, b_shared_caches, nid, 1, ac_shared);
> +
> +	o_alien_caches = kt_ptr_value(b_list3 + 
> +					MEMBER_OFFSET(kmem_list3, alien));
> +
> +	//TODO: check that this only happens for single-node systems?
> +	if (!o_alien_caches)
> +		return;
> +
> +	if (target_read_raw_memory(o_alien_caches, (void *)b_alien_caches,
> +					nr_node_ids * GET_TYPE_SIZE(_voidp))) {
> +		warning(_("could not read alien array %llx of kmem_list3 %llx of nid %d of cache %s\n"),
> +				o_alien_caches, o_list3, nid, cachep->name);
> +	}
> +
> +
> +	init_kmem_array_caches(cachep, b_alien_caches, nid, nr_node_ids,
> +								ac_alien);
> +}
> +
> +static void check_kmem_list3_slabs(struct kmem_cache *cachep,
> +						offset o_list3,	int nid)
> +{
> +	char b_list3[GET_TYPE_SIZE(kmem_list3)];
> +	offset o_lhb;
> +	unsigned long counted_free = 0;
> +	unsigned long free_objects;
> +
> +	if(KDUMP_TYPE_GET(kmem_list3, o_list3, b_list3)) {
> +                warning(_("error reading kmem_list3 %llx of nid %d of kmem_cache %llx name %s\n"),
> +				o_list3, nid, cachep->o_cache, cachep->name);
> +		return;
> +	}
> +
> +	free_objects = kt_long_value(b_list3 + MEMBER_OFFSET(kmem_list3,
> +							free_objects));
> +
> +	o_lhb = o_list3 + MEMBER_OFFSET(kmem_list3, slabs_partial);
> +	counted_free += check_kmem_slabs(cachep, o_lhb, slab_partial);
> +
> +	o_lhb = o_list3 + MEMBER_OFFSET(kmem_list3, slabs_full);
> +	counted_free += check_kmem_slabs(cachep, o_lhb, slab_full);
> +
> +	o_lhb = o_list3 + MEMBER_OFFSET(kmem_list3, slabs_free);
> +	counted_free += check_kmem_slabs(cachep, o_lhb, slab_free);
> +
> +//	printf("free=%lu counted=%lu\n", free_objects, counted_free);
> +	if (free_objects != counted_free)
> +		warning(_("cache %s should have %lu free objects but we counted %lu\n"),
> +				cachep->name, free_objects, counted_free);
> +}
> +
> +static struct kmem_cache *init_kmem_cache(offset o_cache)
> +{
> +	struct kmem_cache *cache;
> +	char b_cache[GET_TYPE_SIZE(kmem_cache)];
> +	offset o_cache_name;
> +	void **slot;
> +
> +	if (!kmem_cache_cache)
> +		init_kmem_caches();
> +
> +	slot = htab_find_slot_with_hash(kmem_cache_cache, &o_cache, o_cache,
> +								INSERT);
> +	if (*slot) {
> +		cache = (struct kmem_cache*) *slot;
> +//		printf("kmem_cache %s found in hashtab!\n", cache->name);
> +		return cache;
> +	}
> +
> +//	printf("kmem_cache %llx not found in hashtab, inserting\n", o_cache);
> +
> +	cache = malloc(sizeof(struct kmem_cache));
> +	cache->o_cache = o_cache;
> +
> +	if (KDUMP_TYPE_GET(kmem_cache, o_cache, b_cache)) {
> +		warning(_("error reading contents of kmem_cache at %llx\n"),
> +								o_cache);
> +		cache->broken = 1;
> +		cache->name = "(broken)";
> +		goto done;
> +	}
> +
> +	cache->num = kt_int_value(b_cache + MEMBER_OFFSET(kmem_cache, num));
> +	cache->buffer_size = kt_int_value(b_cache + MEMBER_OFFSET(kmem_cache,
> +								buffer_size));
> +	cache->array_caches_inited = 0;
> +
> +	o_cache_name = kt_ptr_value(b_cache + MEMBER_OFFSET(kmem_cache,name));
> +	if (!o_cache_name) {
> +		fprintf(stderr, "cache name pointer NULL\n");
> +		cache->name = "(null)";
> +	}
> +
> +	cache->name = kt_strndup(o_cache_name, 128);
> +	cache->broken = 0;
> +//	printf("cache name is: %s\n", cache->name);
> +
> +done:
> +	*slot = cache;
> +	return cache;
> +}
> +
> +static void init_kmem_cache_arrays(struct kmem_cache *cache)
> +{
> +	char b_cache[GET_TYPE_SIZE(kmem_cache)];
> +	char *b_nodelists, *b_array_caches;
> +	offset o_nodelist, o_array_cache;
> +	char *nodelist, *array_cache;
> +	int node;
> +
> +	if (cache->array_caches_inited || cache->broken)
> +		return;
> +
> +	if (KDUMP_TYPE_GET(kmem_cache, cache->o_cache, b_cache)) {
> +		warning(_("error reading contents of kmem_cache at %llx\n"),
> +							cache->o_cache);
> +		return;
> +	}
> +
> +
> +	cache->obj_ac = htab_create_alloc(64, kmem_ac_hash, kmem_ac_eq,
> +						NULL, xcalloc, xfree);
> +
> +	b_nodelists = b_cache + MEMBER_OFFSET(kmem_cache, nodelists);
> +	for (node = 0; node < nr_node_ids;
> +			node++, b_nodelists += GET_TYPE_SIZE(_voidp)) {
> +		o_nodelist = kt_ptr_value(b_nodelists);
> +		if (!o_nodelist)
> +			continue;
> +//		printf("found nodelist[%d] %llx\n", node, o_nodelist);
> +		init_kmem_list3_arrays(cache, o_nodelist, node);
> +	}
> +
> +	b_array_caches = b_cache + MEMBER_OFFSET(kmem_cache, array);
> +	init_kmem_array_caches(cache, b_array_caches, -1, nr_cpu_ids,
> +								ac_percpu);
> +
> +	cache->array_caches_inited = 1;
> +}
> +
> +static void check_kmem_cache(struct kmem_cache *cache)
> +{
> +	char b_cache[GET_TYPE_SIZE(kmem_cache)];
> +	char *b_nodelists, *b_array_caches;
> +	offset o_nodelist, o_array_cache;
> +	char *nodelist, *array_cache;
> +	int node;
> +
> +	init_kmem_cache_arrays(cache);
> +
> +	if (KDUMP_TYPE_GET(kmem_cache, cache->o_cache, b_cache)) {
> +		warning(_("error reading contents of kmem_cache at %llx\n"),
> +							cache->o_cache);
> +		return;
> +	}
> +
> +	b_nodelists = b_cache + MEMBER_OFFSET(kmem_cache, nodelists);
> +	for (node = 0; node < nr_node_ids;
> +			node++, b_nodelists += GET_TYPE_SIZE(_voidp)) {
> +		o_nodelist = kt_ptr_value(b_nodelists);
> +		if (!o_nodelist)
> +			continue;
> +//		printf("found nodelist[%d] %llx\n", node, o_nodelist);
> +		check_kmem_list3_slabs(cache, o_nodelist, node);
> +	}
> +}
> +
> +static int init_kmem_caches(void)
> +{
> +	offset o_kmem_cache;
> +	struct list_iter iter;
> +	offset o_nr_node_ids, o_nr_cpu_ids;
> +
> +	kmem_cache_cache = htab_create_alloc(64, kmem_cache_hash,
> +					kmem_cache_eq, NULL, xcalloc, xfree);
> +
> +	o_slab_caches = get_symbol_value("slab_caches");
> +	if (! o_slab_caches) {
> +		o_slab_caches = get_symbol_value("cache_chain");
> +		if (!o_slab_caches) {
> +			warning(_("Cannot find slab_caches\n"));
> +			return -1;
> +		}
> +	}
> +	printf("slab_caches: %llx\n", o_slab_caches);
> +
> +	o_nr_cpu_ids = get_symbol_value("nr_cpu_ids");
> +	if (! o_nr_cpu_ids) {
> +		warning(_("nr_cpu_ids not found, assuming 1 for !SMP"));
> +	} else {
> +		printf("o_nr_cpu_ids = %llx\n", o_nr_cpu_ids);
> +		nr_cpu_ids = kt_int_value_off(o_nr_cpu_ids);
> +		printf("nr_cpu_ids = %d\n", nr_cpu_ids);
> +	}
> +
> +	o_nr_node_ids = get_symbol_value("nr_node_ids");
> +	if (! o_nr_node_ids) {
> +		warning(_("nr_node_ids not found, assuming 1 for !NUMA"));
> +	} else {
> +		printf("o_nr_node_ids = %llx\n", o_nr_node_ids);
> +		nr_node_ids = kt_int_value_off(o_nr_node_ids);
> +		printf("nr_node_ids = %d\n", nr_node_ids);
> +	}
> +
> +	list_for_each(iter, o_slab_caches) {
> +		o_kmem_cache = iter.curr - MEMBER_OFFSET(kmem_cache,list);
> +//		printf("found kmem cache: %llx\n", o_kmem_cache);
> +
> +		init_kmem_cache(o_kmem_cache);
> +	}
> +
> +	return 0;
> +}
> +
> +static void check_kmem_caches(void)
> +{
> +	offset o_lhb, o_kmem_cache;
> +	struct list_iter iter;
> +	struct kmem_cache *cache;
> +
> +	if (!kmem_cache_cache)
> +		init_kmem_caches();
> +
> +	list_for_each(iter, o_slab_caches) {
> +		o_kmem_cache = iter.curr - MEMBER_OFFSET(kmem_cache,list);
> +
> +		cache = init_kmem_cache(o_kmem_cache);
> +		printf("checking kmem cache %llx name %s\n", o_kmem_cache,
> +				cache->name);
> +		if (cache->broken) {
> +			printf("cache is too broken, skipping");
> +			continue;
> +		}
> +		check_kmem_cache(cache);
> +	}
> +}
> +
> +
> +
> +
> +static struct page read_page(offset o_page)
> +{
> +	char b_page[GET_TYPE_SIZE(page)];
> +	struct page page;
> +
> +	if (KDUMP_TYPE_GET(page, o_page, b_page)) {
> +		page.valid = 0;
> +		return page;
> +	}
> +
> +	page.flags = kt_long_value(b_page + MEMBER_OFFSET(page, flags));
> +	page.lru.next = kt_ptr_value(b_page + MEMBER_OFFSET(page, lru)
> +					+ MEMBER_OFFSET(list_head, next));
> +	page.lru.prev = kt_ptr_value(b_page + MEMBER_OFFSET(page, lru)
> +					+ MEMBER_OFFSET(list_head, prev));
> +	page.first_page = kt_ptr_value(b_page +
> +					MEMBER_OFFSET(page, first_page));
> +	page.valid = 1;
> +
> +	return page;
> +}
> +
> +static inline struct page compound_head(struct page page)
> +{
> +	if (page.valid && PageTail(page))
> +		return read_page(page.first_page);
> +	return page;
> +}
> +
> +static struct page virt_to_head_page(offset addr)
> +{
> +	struct page page;
> +
> +	page = read_page(virt_to_opage(addr));
> +
> +	return compound_head(page);
> +}
> +
> +static int check_slab_obj(offset obj)
> +{
> +	struct page page;
> +	offset o_cache, o_slab;
> +	struct kmem_cache *cachep;
> +	struct kmem_slab *slabp;
> +	struct kmem_obj_ac *obj_ac;
> +	struct kmem_ac *ac;
> +	unsigned int idx;
> +	offset obj_base;
> +	unsigned int i, cnt = 0;
> +	int free = 0;
> +
> +	page = virt_to_head_page(obj);
> +
> +	if (!page.valid) {
> +		warning(_("unable to read struct page for object at %llx\n"),
> +				obj);
> +		return 0;
> +	}
> +
> +	if (!PageSlab(page))
> +		return 0;
> +
> +	o_cache = page.lru.next;
> +	o_slab = page.lru.prev;
> +	printf("pointer %llx is on slab %llx of cache %llx\n", obj, o_slab,
> +								o_cache);
> +
> +	cachep = init_kmem_cache(o_cache);
> +	init_kmem_cache_arrays(cachep);
> +	slabp = init_kmem_slab(cachep, o_slab);
> +
> +	//TODO: kernel implementation uses reciprocal_divide, check?
> +	idx = (obj - slabp->s_mem) / cachep->buffer_size;
> +	obj_base = slabp->s_mem + idx * cachep->buffer_size;
> +
> +	printf("pointer is to object %llx with index %u\n", obj_base, idx);
> +
> +	i = slabp->free;
> +	while (i != BUFCTL_END) {
> +		cnt++;
> +
> +		if (cnt > cachep->num) {
> +			printf("free bufctl cycle detected in slab %llx\n", o_slab);
> +			break;
> +		}
> +		if (i > cachep->num) {
> +			printf("bufctl value overflow (%d) in slab %llx\n", i, o_slab);
> +			break;
> +		}
> +
> +		if (i == idx)
> +			free = 1;
> +
> +		i = slabp->bufctl[i];
> +	}
> +
> +	printf("object is %s\n", free ? "free" : "allocated");
> +
> +	obj_ac = htab_find_with_hash(cachep->obj_ac, &obj, obj);
> +
> +	if (obj_ac) {
> +		ac = obj_ac->ac;
> +		printf("object is in array_cache %llx type %s[%d,%d]\n",
> +			ac->offset, ac_type_names[ac->type], ac->at_node,
> +			ac->for_node_cpu);
> +	}
> +
> +	free_kmem_slab(slabp);
> +
> +	return 1;
> +}
> +
> +static int init_memmap(void)
> +{
> +	const char *cfg;
> +	offset o_mem_map;
> +	offset o_page;
> +	struct page page;
> +	unsigned long long p_memmap;
> +
> +	//FIXME: why are all NULL?
> +
> +	cfg = kdump_vmcoreinfo_row(dump_ctx, "CONFIG_FLATMEM");
> +	printf("CONFIG_FLATMEM=%s\n", cfg ? cfg : "(null)");
> +
> +	cfg = kdump_vmcoreinfo_row(dump_ctx, "CONFIG_DISCONTIGMEM");
> +	printf("CONFIG_DISCONTIGMEM=%s\n", cfg ? cfg : "(null)");
> +
> +	cfg = kdump_vmcoreinfo_row(dump_ctx, "CONFIG_SPARSEMEM_VMEMMAP");
> +	printf("CONFIG_SPARSEMEM_VMEMMAP=%s\n", cfg ? cfg : "(null)");
> +
> +	o_mem_map = get_symbol_value("mem_map");
> +	printf("memmap: %llx\n", o_mem_map);
> +
> +	if (o_mem_map) {
> +		p_memmap = kt_ptr_value_off(o_mem_map);
> +		printf("memmap is pointer to: %llx\n", p_memmap);
> +		if (p_memmap != -1)
> +			memmap = p_memmap;
> +	}
> +
> +/*
> +	o_page = virt_to_opage(0xffff880138bedf40UL);
> +	printf("ffff880138bedf40 is page %llx\n", o_page);
> +
> +	page = read_page(o_page);
> +	printf("flags=%lx lru=(%llx,%llx) first_page=%llx\n",page.flags,
> +			page.lru.next, page.lru.prev, page.first_page);
> +	printf("PG_slab=%llx\n", get_symbol_value("PG_slab"));
> +	printf("PageSlab(page)==%d\n", PageSlab(page));
> +*/
> +	return 0;
> +}
> +
>  static int init_values(void);
>  static int init_values(void)
>  {
>  	struct symbol *s;
>  	char *b = NULL, *init_task = NULL, *task = NULL;
> -	offset off, off_task, rsp, rip, _rsp;
> +	offset off, o_task, rsp, rip, _rsp;
>  	offset tasks;
> +	offset o_tasks;
> +	offset off_task;
>  	offset stack;
>  	offset o_init_task;
>  	int state;
> @@ -1108,6 +2266,7 @@ static int init_values(void)
>  	int cnt = 0;
>  	int pid_reserve;
>  	struct task_info *task_info;
> +	struct list_iter iter;
>  
>  	s = NULL;
>  	
> @@ -1141,58 +2300,59 @@ static int init_values(void)
>  		goto error;
>  	task = KDUMP_TYPE_ALLOC(task_struct);
>  	if (!task) goto error;
> +
>  	if (KDUMP_TYPE_GET(task_struct, o_init_task, init_task))
>  		goto error;
>  	tasks = kt_ptr_value(init_task + MEMBER_OFFSET(task_struct,tasks));
> +	o_tasks = o_init_task + MEMBER_OFFSET(task_struct, tasks);
>  
>  	i = 0;
> -	off = 0;
>  	pid_reserve = 50000;
>  
>  	print_thread_events = 0;
>  	in = current_inferior();
>  	inferior_appeared (in, 1);
>  
> -	list_head_for_each(tasks, init_task + MEMBER_OFFSET(task_struct,tasks), off) {
> -		
> +	list_for_each_from(iter, o_tasks) {
> +
>  		struct thread_info *info;
>  		int pid;
>  		ptid_t tt;
>  		struct regcache *rc;
>  		long long val;
>  		offset main_tasks, mt;
> -		
> +		struct list_iter iter_thr;
> +		offset o_threads;
>  
>  		//fprintf(stderr, __FILE__":%d: ok\n", __LINE__);
>  		off_task = off - MEMBER_OFFSET(task_struct,tasks);
>  		if (KDUMP_TYPE_GET(task_struct, off_task, task)) continue;
>  
> -		main_tasks = off_task;//kt_ptr_value(task + MEMBER_OFFSET(task_struct,thread_group));
> +		o_task = iter.curr - MEMBER_OFFSET(task_struct, tasks);
> +		o_threads = o_task + MEMBER_OFFSET(task_struct, thread_group);
> +		list_for_each_from(iter_thr, o_threads) {
>  
> -		do {
> -		//list_head_for_each(main_tasks, task + MEMBER_OFFSET(task_struct,thread_group), mt) {
> -
> -			//off_task = mt - MEMBER_OFFSET(task_struct,thread_group);
> -			if (KDUMP_TYPE_GET(task_struct, off_task, task))  {
> +			o_task = iter_thr.curr - MEMBER_OFFSET(task_struct,
> +								thread_group);
> +			if (KDUMP_TYPE_GET(task_struct, o_task, task))
>  				continue;
> -			}
> -
> -			if (add_task(off_task, &pid_reserve, task)) {
> -
> -			} else {
> -				
> -				printf_unfiltered(_("Loaded processes: %d\r"), ++cnt);
> -			}
> -			off_task = kt_ptr_value(task + MEMBER_OFFSET(task_struct, thread_group)) - MEMBER_OFFSET(task_struct, thread_group);
> -			if (off_task == main_tasks) break;
>  
> -		} while (1);
> +			if (!add_task(o_task, &pid_reserve, task))
> +				printf_unfiltered(_("Loaded processes: %d\r"),
> +									++cnt);
> +		}
>  	}
>  
>  	if (b) free(b);
>  	if (init_task) free(init_task);
>  
>  	printf_unfiltered(_("Loaded processes: %d\n"), cnt);
> +	init_memmap();
> +
> +//	check_kmem_caches();
> +//	check_slab_obj(0xffff880138bedf40UL);
> +//	check_slab_obj(0xffff8801359734c0UL);
> +
>  	return 0;
>  error:
>  	if (b) free(b);
> @@ -1373,7 +2533,6 @@ core_detach (struct target_ops *ops, const char *args, int from_tty)
>  		printf_filtered (_("No core file now.\n"));
>  }
>  
> -static kdump_paddr_t transform_memory(kdump_paddr_t addr);
>  static kdump_paddr_t transform_memory(kdump_paddr_t addr)
>  {
>  	kdump_paddr_t out;
> @@ -1396,10 +2555,12 @@ kdump_xfer_partial (struct target_ops *ops, enum target_object object,
>  	{
>  		case TARGET_OBJECT_MEMORY:
>  			offset = transform_memory((kdump_paddr_t)offset);
> -			r = kdump_read(dump_ctx, (kdump_paddr_t)offset, (unsigned char*)readbuf, (size_t)len, KDUMP_PHYSADDR);
> +			r = kdump_read(dump_ctx, KDUMP_KPHYSADDR, (kdump_paddr_t)offset, (unsigned char*)readbuf, (size_t)len);
>  			if (r != len) {
> -				error(_("Cannot read %lu bytes from %lx (%lld)!"), (size_t)len, (long unsigned int)offset, (long long)r);
> -			} else
> +				warning(_("Cannot read %lu bytes from %lx (%lld)!"),
> +						(size_t)len, (long unsigned int)offset, (long long)r);
> +				return TARGET_XFER_E_IO;
> +			} else 
>  				*xfered_len = len;
>  
>  			return TARGET_XFER_OK;
> @@ -1797,7 +2958,9 @@ static void kdumpps_command(char *fn, int from_tty)
>  		if (!task) continue;
>  		if (task->cpu == -1) cpu[0] = '\0';
>  		else snprintf(cpu, 5, "% 4d", task->cpu);
> +#ifdef _DEBUG
>  		printf_filtered(_("% 7d %llx %llx %llx %-4s %s\n"), task->pid, task->task_struct, task->ip, task->sp, cpu, tp->name);
> +#endif
>  	}
>  }
>
  
Doug Evans Feb. 1, 2016, 10:29 p.m. UTC | #2
On Mon, Feb 1, 2016 at 5:21 AM, Kieran Bingham <kieranbingham@gmail.com> wrote:
> This is interesting work!
>
> I had been discussing how we might achieve managing this with Jan @
> FOSDEM yesterday.
>
> I believe a python implementation of this could be possible, and then
> this code can live in the Kernel, and be split across architecture
> specific layers where necessary to implement handling userspace
> application boundaries from the Kernel Awareness.

Keeping application specific code with the application instead of gdb
is definitely a worthy goal.
[one can quibble over whether linux is an application of course,
but that's just terminology]

> I believe that if properly abstracted (which I think it looks like this
> already will be), with kdump as a target layer, we can implement the
> Kernel awareness layers above, so that they can be common to all of our
> use case scenarios.
>
> I have recently proposed creating a gdb.Target object, so that we can
> layer the kernel specific code on top as a higher stratum layer. This
> code can then live in the Kernel, and be version specific there, and
> would then cooperate with the layers below, be that a live target over
> JTAG, or a virtualised qemu/kvm, or a core dump file:

Providing gdb.Target is also a worthy goal.
I hope someone will take this on.
  
Ales Novak Feb. 2, 2016, 2:05 a.m. UTC | #3
On 2016-2-1 23:29, Doug Evans wrote:

> On Mon, Feb 1, 2016 at 5:21 AM, Kieran Bingham <kieranbingham@gmail.com> wrote:
>> This is interesting work!
>>
>> I had been discussing how we might achieve managing this with Jan @
>> FOSDEM yesterday.
>>
>> I believe a python implementation of this could be possible, and then
>> this code can live in the Kernel, and be split across architecture
>> specific layers where necessary to implement handling userspace
>> application boundaries from the Kernel Awareness.
>
> Keeping application specific code with the application instead of gdb
> is definitely a worthy goal.
> [one can quibble over whether linux is an application of course,
> but that's just terminology]

Yeah, you're right. Yet if we're talking about the SLAB in particular - 
considering with how many objects simultaneously has this subsystem to 
cope, I'm afraid that adding any extra overhead (e.g. the Pythonish) will 
be just painful.

It's a pitty that gdb cannot be extended dynamically, afaics.
  
Jan Kiszka Feb. 2, 2016, 7:22 a.m. UTC | #4
On 2016-02-02 03:05, Ales Novak wrote:
> On 2016-2-1 23:29, Doug Evans wrote:
> 
>> On Mon, Feb 1, 2016 at 5:21 AM, Kieran Bingham
>> <kieranbingham@gmail.com> wrote:
>>> This is interesting work!
>>>
>>> I had been discussing how we might achieve managing this with Jan @
>>> FOSDEM yesterday.
>>>
>>> I believe a python implementation of this could be possible, and then
>>> this code can live in the Kernel, and be split across architecture
>>> specific layers where necessary to implement handling userspace
>>> application boundaries from the Kernel Awareness.
>>
>> Keeping application specific code with the application instead of gdb
>> is definitely a worthy goal.
>> [one can quibble over whether linux is an application of course,
>> but that's just terminology]
> 
> Yeah, you're right. Yet if we're talking about the SLAB in particular -
> considering with how many objects simultaneously has this subsystem to
> cope, I'm afraid that adding any extra overhead (e.g. the Pythonish)
> will be just painful.
> 
> It's a pitty that gdb cannot be extended dynamically, afaics.

First, don't be too sceptical before some has tried this. And then there
are still options for optimizations, either on the language side (C
extension to our Python modules, also in-kernel maintained) or more
efficient interfaces for gdb's Python API.

It's definitely worth exploring this first before adding Linux kernel
release specific things to gdb, which is going to be even more painful
to maintain.

Jan
  
Kieran Bingham Feb. 2, 2016, 8:11 a.m. UTC | #5
On 01/02/16 22:29, Doug Evans wrote:
> On Mon, Feb 1, 2016 at 5:21 AM, Kieran Bingham <kieranbingham@gmail.com> wrote:
>> This is interesting work!
>>
>> I had been discussing how we might achieve managing this with Jan @
>> FOSDEM yesterday.
>>
>> I believe a python implementation of this could be possible, and then
>> this code can live in the Kernel, and be split across architecture
>> specific layers where necessary to implement handling userspace
>> application boundaries from the Kernel Awareness.
> 
> Keeping application specific code with the application instead of gdb
> is definitely a worthy goal.
> [one can quibble over whether linux is an application of course,
> but that's just terminology]

It's just a big fancy application which supports modules, and can talk
to hardware. :D </me ducks to avoid the flying bricks>

> 
>> I believe that if properly abstracted (which I think it looks like this
>> already will be), with kdump as a target layer, we can implement the
>> Kernel awareness layers above, so that they can be common to all of our
>> use case scenarios.
>>
>> I have recently proposed creating a gdb.Target object, so that we can
>> layer the kernel specific code on top as a higher stratum layer. This
>> code can then live in the Kernel, and be version specific there, and
>> would then cooperate with the layers below, be that a live target over
>> JTAG, or a virtualised qemu/kvm, or a core dump file:
> 
> Providing gdb.Target is also a worthy goal.

Perfect, I'm glad to hear this

> I hope someone will take this on.

That's my current Work In Progress.!

--
Kieran
  
Vlastimil Babka Feb. 2, 2016, 10:03 a.m. UTC | #6
On 02/01/2016 02:21 PM, Kieran Bingham wrote:
> This is interesting work!
>
> I had been discussing how we might achieve managing this with Jan @
> FOSDEM yesterday.
>
> I believe a python implementation of this could be possible, and then
> this code can live in the Kernel, and be split across architecture
> specific layers where necessary to implement handling userspace
> application boundaries from the Kernel Awareness.

Hi,

I understand that the idea of python scripts living in the kernel tree 
looks desirable, but I see several practical drawbacks. My main goal 
with this is to have a better replacement for the crash [1] tool for 
kernel crash dump analysis. The tool supports dumps from a range of 
kernel versions, and so should the replacement. We regularly deal with 
crash dumps from 3.0-based and newer kernels, so backporting some 
kernel-version-specific python scripts to those kernel versions (or even 
older) is infeasible. Then we would have to assume that any kernel patch 
author changing a subsystem doesn't forget to update the in-kernel 
scripts, otherwise they easily get out of sync in the git history. 
Lastly, it's bit more comfortable if the only input you need is the 
dump, vmlinux and vmlinux.debug, without having to checkout scripts from 
git.

So I believe it's better if the tool could understand and work with a 
range of kernel versions by itself, like crash. The split between 
functionality in C and python is a separate question. I understand you 
wouldn't want to add all the required knowledge into gdb proper, so what 
other options are there? Some kind contrib/python/kernel directory for 
the python scripts? (but not version specific?). How can we similarly 
separate the required C code, if it turns out that doing *everything* in 
python, wrapping only the lowest-level gdb concepts would be too slow?

Thanks,
Vlastimil

[1] https://people.redhat.com/anderson/
  
Petr Tesarik Feb. 2, 2016, 1:21 p.m. UTC | #7
On Tue, 2 Feb 2016 08:22:25 +0100
Jan Kiszka <jan.kiszka@siemens.com> wrote:

> On 2016-02-02 03:05, Ales Novak wrote:
> > On 2016-2-1 23:29, Doug Evans wrote:
> > 
>[...]
> >> Keeping application specific code with the application instead of gdb
> >> is definitely a worthy goal.
> >> [one can quibble over whether linux is an application of course,
> >> but that's just terminology]
> > 
> > Yeah, you're right. Yet if we're talking about the SLAB in particular -
> > considering with how many objects simultaneously has this subsystem to
> > cope, I'm afraid that adding any extra overhead (e.g. the Pythonish)
> > will be just painful.
> > 
> > It's a pitty that gdb cannot be extended dynamically, afaics.
> 
> First, don't be too sceptical before some has tried this. And then there
> are still options for optimizations, either on the language side (C
> extension to our Python modules, also in-kernel maintained) or more
> efficient interfaces for gdb's Python API.
> 
> It's definitely worth exploring this first before adding Linux kernel
> release specific things to gdb, which is going to be even more painful
> to maintain.

I agree that putting Linux-specific code into the GDB main project is a
bit unfortunate. But this indeed happens because there is no way to add
an external module to GDB. In effect, there is little choice: all code
must be either accepted by the (monolithic) GDB project, or it must be
maintained as a custom out-of-tree patch.

Now, maintaining out-of-tree code is just too much pain. This is (in my
opinion) the main reason people are so excited about Python scripting:
it's the only available stable API that can be used to enhance GDB with
things that do not belong to the core GDB. Plus, this API is incomplete
(as evidenced by Jeff's patch set), and extending it is definitely more
work than exporting existing C functions for use by modules, slowing
down further development of GDB.

Note that this limitation is more political than technical, but this
fact probably only means it's less likely to change...

Just my two cents,
Petr T
  
Jeff Mahoney Feb. 2, 2016, 2:41 p.m. UTC | #8
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 2/2/16 8:21 AM, Petr Tesarik wrote:
> On Tue, 2 Feb 2016 08:22:25 +0100 Jan Kiszka
> <jan.kiszka@siemens.com> wrote:
> 
>> On 2016-02-02 03:05, Ales Novak wrote:
>>> On 2016-2-1 23:29, Doug Evans wrote:
>>> 
>> [...]
>>>> Keeping application specific code with the application
>>>> instead of gdb is definitely a worthy goal. [one can quibble
>>>> over whether linux is an application of course, but that's
>>>> just terminology]
>>> 
>>> Yeah, you're right. Yet if we're talking about the SLAB in
>>> particular - considering with how many objects simultaneously
>>> has this subsystem to cope, I'm afraid that adding any extra
>>> overhead (e.g. the Pythonish) will be just painful.
>>> 
>>> It's a pitty that gdb cannot be extended dynamically, afaics.
>> 
>> First, don't be too sceptical before some has tried this. And
>> then there are still options for optimizations, either on the
>> language side (C extension to our Python modules, also in-kernel
>> maintained) or more efficient interfaces for gdb's Python API.
>> 
>> It's definitely worth exploring this first before adding Linux
>> kernel release specific things to gdb, which is going to be even
>> more painful to maintain.
> 
> I agree that putting Linux-specific code into the GDB main project
> is a bit unfortunate. But this indeed happens because there is no
> way to add an external module to GDB. In effect, there is little
> choice: all code must be either accepted by the (monolithic) GDB
> project, or it must be maintained as a custom out-of-tree patch.
> 
> Now, maintaining out-of-tree code is just too much pain. This is
> (in my opinion) the main reason people are so excited about Python
> scripting: it's the only available stable API that can be used to
> enhance GDB with things that do not belong to the core GDB. Plus,
> this API is incomplete (as evidenced by Jeff's patch set), and
> extending it is definitely more work than exporting existing C
> functions for use by modules, slowing down further development of
> GDB.

I only partially agree here.  Using Python to extend GDB to support
e.g. libkdumpfile would be a workaround.  I looked into it briefly and
decided against it. Extending the Python API has been an investment,
though.  Nearly everything I'm doing in the GDB code is generic.  I
really do want to have most of the functionality we have now with
crash implemented as Python modules.  Extensions in crash need to be
compiled in, written in sial, or use the grafted-on python plugin for
it.  All these options are terrible and not at all conducive to
collaborative, iterative improvement.  As we build up more
infrastructure, it becomes a lot easier for people to write quick
commands to automate a lot of the work we end up forced to do to get
crash to do what we need.

- -Jeff

- -- 
Jeff Mahoney
SUSE Labs
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.19 (Darwin)
Comment: GPGTools - http://gpgtools.org

iQIcBAEBAgAGBQJWsMA1AAoJEB57S2MheeWyKQYP/R2kZ2vTYWtom789eS/45FaY
mIWD8EsBlShfF+2ja8EdOHs6TYrkMEGTzjQIhoCJXttegwKY2H8/GXHfODuelaa6
pX5pPWNkV4v1G933NfOsfxJOEecdMAwM8MI+3HFl0I5cjw+/2xXhoEUg6+ZburlT
dU1lljlQwD3+wK5Q4L/w1jBebsTUKDAvJAuoHwVNFygKBkp0h6jqf310J7PfpzR/
S/gcoSsORUrla6pdPEaFAG3lFh6mqlNlaLPKF1GghP2/RdOY0f/Ud81l36Zlu5QI
D8YYtouR3qRzAf8XEV5qFMPUQDRL2c5U6JeLkJ06HxtgK44xV+l2AZar6YNezsaM
zLWyYN42x7W4DDVTrWVqgx9hkrhWYdLxavY48+n8DZncqSraM6F3YorghTfUSGnF
LutLEaBFHHURQfqFDAo8tEN8oT56YAKqRO5NOM2I9vZUdyizVaoCXov5fxiJHBpr
urq2vl8GCXQbg5QLUbR+Fj5E3XJZbe06OT7Oy48hECFRhwEotKqggcKgmJx2/v2H
dk1lLXaKI170OajZz91FVMLrOGARrWe1ZRMUa15slhIyeRUuuru7qH9jbEpBFV+b
LSpAax14+/pWKMFWZrkZglxAtj6QDfFzRz+zVCDzYHLKpO+2Jct51Oas69jL7bnl
xNsosBME7X/IsKjmIfmn
=g4Fu
-----END PGP SIGNATURE-----
  

Patch

diff --git a/gdb/kdump.c b/gdb/kdump.c
index b7b0ef5..e231559 100644
--- a/gdb/kdump.c
+++ b/gdb/kdump.c
@@ -58,6 +58,7 @@ 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <hashtab.h>
 
 
 #include <dirent.h>
@@ -73,6 +74,7 @@  typedef unsigned long long offset;
 #define F_UNKN_ENDIAN    4
 
 unsigned long long kt_int_value (void *buff);
+unsigned long long kt_long_value (void *buff);
 unsigned long long kt_ptr_value (void *buff);
 
 int kt_hlist_head_for_each_node (char *addr, int(*func)(void *,offset), void *data);
@@ -97,12 +99,17 @@  static void core_close (struct target_ops *self);
 
 typedef unsigned long long offset;
 
+static int nr_node_ids = 1;
+static int nr_cpu_ids = 1;
+
 #define KDUMP_TYPE const char *_name; int _size; int _offset; struct type *_origtype
 #define GET_GDB_TYPE(typ) types. typ ._origtype
 #define GET_TYPE_SIZE(typ) (TYPE_LENGTH(GET_GDB_TYPE(typ)))
 #define MEMBER_OFFSET(type,member) types. type. member
-#define KDUMP_TYPE_ALLOC(type) kdump_type_alloc(GET_GDB_TYPE(type))
-#define KDUMP_TYPE_GET(type,off,where) kdump_type_get(GET_GDB_TYPE(type), off, 0, where)
+#define KDUMP_TYPE_ALLOC(type) kdump_type_alloc(GET_GDB_TYPE(type), 0)
+#define KDUMP_TYPE_ALLOC_EXTRA(type,extra) kdump_type_alloc(GET_GDB_TYPE(type),extra)
+#define KDUMP_TYPE_GET(type,off,where) kdump_type_get(GET_GDB_TYPE(type), off, 0, where, 0)
+#define KDUMP_TYPE_GET_EXTRA(type,off,where,extra) kdump_type_get(GET_GDB_TYPE(type), off, 0, where, extra)
 #define KDUMP_TYPE_FREE(where) free(where)
 #define SYMBOL(var,name) do { var = lookup_symbol(name, NULL, VAR_DOMAIN, NULL); if (! var) { fprintf(stderr, "Cannot lookup_symbol(" name ")\n"); goto error; } } while(0)
 #define OFFSET(x) (types.offsets. x)
@@ -112,12 +119,12 @@  typedef unsigned long long offset;
 #define GET_REGISTER_OFFSET(reg) (MEMBER_OFFSET(user_regs_struct,reg)/GET_TYPE_SIZE(_voidp))
 #define GET_REGISTER_OFFSET_pt(reg) (MEMBER_OFFSET(pt_regs,reg)/GET_TYPE_SIZE(_voidp))
 
-#define list_for_each(pos, head) \
-	for (pos = kt_ptr_value(head); pos != (head); KDUMP_TYPE_GET(_voidp,pos,&pos)
 
-#define list_head_for_each(head,lhb, _nxt) for((_nxt = kt_ptr_value(lhb)), KDUMP_TYPE_GET(list_head, _nxt, lhb);\
-	(_nxt = kt_ptr_value(lhb)) != head; \
-	KDUMP_TYPE_GET(list_head, _nxt, lhb))
+#define list_head_for_each(head, lhb, _nxt)				      \
+	for(KDUMP_TYPE_GET(list_head, head, lhb), _nxt = kt_ptr_value(lhb),   \
+					KDUMP_TYPE_GET(list_head, _nxt, lhb); \
+		_nxt != head;						      \
+		_nxt = kt_ptr_value(lhb), KDUMP_TYPE_GET(list_head, _nxt, lhb))
 
 enum x86_64_regs {
 	reg_RAX = 0,
@@ -184,6 +191,10 @@  struct {
 
 	struct {
 		KDUMP_TYPE;
+	} _long;
+
+	struct {
+		KDUMP_TYPE;
 	} _voidp;
 
 	struct {
@@ -345,10 +356,54 @@  struct {
 		offset *percpu_offsets;
 	} offsets;
 
+	struct {
+		KDUMP_TYPE;
+		offset flags;
+		offset lru;
+		offset first_page;
+	} page;
+
+	struct {
+		KDUMP_TYPE;
+		offset array;
+		offset name;
+		offset list;
+		offset nodelists;
+		offset num;
+		offset buffer_size;
+	} kmem_cache;
+
+	struct {
+		KDUMP_TYPE;
+		offset slabs_partial;
+		offset slabs_full;
+		offset slabs_free;
+		offset shared;
+		offset alien;
+		offset free_objects;
+	} kmem_list3;
+
+	struct {
+		KDUMP_TYPE;
+		offset avail;
+		offset limit;
+		offset entry;
+	} array_cache;
+
+	struct {
+		KDUMP_TYPE;
+		offset list;
+		offset inuse;
+		offset free;
+		offset s_mem;
+	} slab;
+
 	struct cpuinfo *cpu;
 	int ncpus;
 } types;
 
+unsigned PG_tail, PG_slab;
+
 struct task_info {
 	offset task_struct;
 	offset sp;
@@ -404,6 +459,21 @@  unsigned long long kt_int_value (void *buff)
 	return val;
 }
 
+unsigned long long kt_long_value (void *buff)
+{
+	unsigned long long val;
+
+	if (GET_TYPE_SIZE(_long) == 4) {
+		val = *(int32_t*)buff;
+		if (types.flags & F_BIG_ENDIAN) val = __bswap_32(val);
+	} else {
+		val = *(int64_t*)buff;
+		if (types.flags & F_BIG_ENDIAN) val = __bswap_64(val);
+	}
+
+	return val;
+}
+
 unsigned long long kt_ptr_value (void *buff)
 {
 	unsigned long long val;
@@ -417,6 +487,49 @@  unsigned long long kt_ptr_value (void *buff)
 	}
 	return val;
 }
+
+static unsigned long long kt_ptr_value_off (offset addr)
+{
+	char buf[8];
+	unsigned len = GET_TYPE_SIZE(_voidp);
+
+	if (target_read_raw_memory(addr, (void *)buf, len)) {
+		warning(_("Cannot read target memory addr=%llx length=%u\n"),
+								addr, len);
+		return -1;
+	}
+
+	return kt_ptr_value(buf);
+}
+
+static unsigned long long kt_int_value_off (offset addr)
+{
+	char buf[8];
+	unsigned len = GET_TYPE_SIZE(_int);
+
+	if (target_read_raw_memory(addr, (void *)buf, len)) {
+		warning(_("Cannot read target memory addr=%llx length=%u\n"),
+								addr, len);
+		return -1;
+	}
+
+	return kt_int_value(buf);
+}
+
+char * kt_strndup (offset src, int n);
+char * kt_strndup (offset src, int n)
+{
+	char *dest = NULL;
+	int ret, errno;
+
+	ret = target_read_string(src, &dest, n, &errno);
+
+	if (errno)
+		fprintf(stderr, "target_read_string errno: %d\n", errno);
+
+	return dest;
+}
+
 static offset get_symbol_address(const char *sname);
 static offset get_symbol_address(const char *sname)
 {
@@ -519,35 +632,55 @@  static int kdump_type_member_init (struct type *type, const char *name, offset *
 {
 	int i;
 	struct field *f;
+	int ret;
+	enum type_code tcode;
+	offset off;
+
 	f = TYPE_FIELDS(type);
-	for (i = 0; i < TYPE_NFIELDS(type); i ++) {
-		if (! strcmp(f->name, name)) {
-			*poffset = (f->loc.physaddr >> 3);
+	for (i = 0; i < TYPE_NFIELDS(type); i++, f++) {
+		//printf("fieldname \'%s\'\n", f->name);
+		off = (f->loc.physaddr >> 3);
+		if (!strcmp(f->name, name)) {
+			*poffset = off;
 			return 0;
 		}
-		f++;
+		if (strlen(f->name))
+			continue;
+		tcode = TYPE_CODE(f->type);
+		if (tcode == TYPE_CODE_UNION || tcode == TYPE_CODE_STRUCT) {
+			//printf("recursing into unnamed union/struct\n");
+			ret = kdump_type_member_init(f->type, name, poffset);
+			if (ret != -1) {
+				*poffset += off;
+				return ret;
+			}
+		}
 	}
 	return -1;
 }
 
-static void *kdump_type_alloc(struct type *type)
+static void *kdump_type_alloc(struct type *type, size_t extra_size)
 {
 	int allocated = 0;
 	void *buff;
 
 	allocated = 1;
-	buff = malloc(TYPE_LENGTH(type));
+	buff = malloc(TYPE_LENGTH(type) + extra_size);
 	if (buff == NULL) {
-		warning(_("Cannot allocate memory of %d length\n"), (int)TYPE_LENGTH(type));
+		warning(_("Cannot allocate memory of %u length + %lu extra\n"),
+					TYPE_LENGTH(type), extra_size);
 		return NULL;
 	}
 	return buff;
 }
 
-static int kdump_type_get(struct type *type, offset addr, int pos, void *buff)
+static int kdump_type_get(struct type *type, offset addr, int pos, void *buff,
+							size_t extra_size)
 {
-	if (target_read_raw_memory(addr + (TYPE_LENGTH(type)*pos), buff, TYPE_LENGTH(type))) {
-		warning(_("Cannot read target memory of %d length\n"), (int)TYPE_LENGTH(type));
+	if (target_read_raw_memory(addr + (TYPE_LENGTH(type)*pos), buff,
+					TYPE_LENGTH(type) + extra_size)) {
+		warning(_("Cannot read target memory of %u length + %lu extra\n"),
+					TYPE_LENGTH(type), extra_size);
 		return 1;
 	}
 	return 0;
@@ -568,7 +701,8 @@  int kdump_types_init(int flags)
 	#define INIT_BASE_TYPE_(name,tname) if(kdump_type_init(&types. tname ._origtype, &types. tname ._size, #name, T_BASE)) { fprintf(stderr, "Cannot base find type \'%s\'", #name); break; }
 	#define INIT_REF_TYPE(name) if(kdump_type_init(&types. name ._origtype, &types. name ._size, #name, T_REF)) { fprintf(stderr, "Cannot ref find type \'%s\'", #name); break; }
 	#define INIT_REF_TYPE_(name,tname) if(kdump_type_init(&types. tname ._origtype, &types. tname ._size, #name, T_REF)) { fprintf(stderr, "Cannot ref find type \'%s\'", #name); break; }
-	#define INIT_STRUCT_MEMBER(sname,mname) if(kdump_type_member_init(types. sname ._origtype, #mname, &types. sname . mname)) { break; }
+	#define INIT_STRUCT_MEMBER(sname,mname) if(kdump_type_member_init(types. sname ._origtype, #mname, &types. sname . mname)) \
+		{ fprintf(stderr, "Cannot find struct \'%s\' member \'%s\'", #sname, #mname); break; }
 
 	/** initialize member with different name than the containing one */
 	#define INIT_STRUCT_MEMBER_(sname,mname,mmname) if(kdump_type_member_init(types. sname ._origtype, #mname, &types. sname . mmname)) { break; }
@@ -576,8 +710,9 @@  int kdump_types_init(int flags)
 	/** don't fail if the member is not present */
 	#define INIT_STRUCT_MEMBER__(sname,mname) kdump_type_member_init(types. sname ._origtype, #mname, &types. sname . mname)
 	do {
-		INIT_BASE_TYPE_(int,_int);
-		INIT_REF_TYPE_(void,_voidp);
+		INIT_BASE_TYPE_(int,_int); 
+		INIT_BASE_TYPE_(long,_long);
+		INIT_REF_TYPE_(void,_voidp); 
 
 		INIT_STRUCT(list_head);
 		INIT_STRUCT_MEMBER(list_head,prev);
@@ -728,9 +863,43 @@  int kdump_types_init(int flags)
 			INIT_STRUCT_MEMBER__(ppc_pt_regs, rx6);
 			INIT_STRUCT_MEMBER__(ppc_pt_regs, rx7);
 		}
+		INIT_STRUCT(page);
+		INIT_STRUCT_MEMBER(page, flags);
+		INIT_STRUCT_MEMBER(page, lru);
+		INIT_STRUCT_MEMBER(page, first_page);
+
+		INIT_STRUCT(kmem_cache);
+		INIT_STRUCT_MEMBER(kmem_cache, name);
+		INIT_STRUCT_MEMBER_(kmem_cache, next, list);
+		INIT_STRUCT_MEMBER(kmem_cache, nodelists);
+		INIT_STRUCT_MEMBER(kmem_cache, num);
+		INIT_STRUCT_MEMBER(kmem_cache, array);
+		INIT_STRUCT_MEMBER(kmem_cache, buffer_size);
+
+		INIT_STRUCT(kmem_list3);
+		INIT_STRUCT_MEMBER(kmem_list3, slabs_partial);
+		INIT_STRUCT_MEMBER(kmem_list3, slabs_full);
+		INIT_STRUCT_MEMBER(kmem_list3, slabs_free);
+		INIT_STRUCT_MEMBER(kmem_list3, shared);
+		INIT_STRUCT_MEMBER(kmem_list3, alien);
+		INIT_STRUCT_MEMBER(kmem_list3, free_objects);
+
+		INIT_STRUCT(array_cache);
+		INIT_STRUCT_MEMBER(array_cache, avail);
+		INIT_STRUCT_MEMBER(array_cache, limit);
+		INIT_STRUCT_MEMBER(array_cache, entry);
+
+		INIT_STRUCT(slab);
+		INIT_STRUCT_MEMBER(slab, list);
+		INIT_STRUCT_MEMBER(slab, inuse);
+		INIT_STRUCT_MEMBER(slab, free);
+		INIT_STRUCT_MEMBER(slab, s_mem);
 		ret = 0;
 	} while(0);
 
+	PG_tail = get_symbol_value("PG_tail");
+	PG_slab = get_symbol_value("PG_slab");
+
 	if (ret) {
 		fprintf(stderr, "Cannot init types\n");
 	}
@@ -738,6 +907,148 @@  int kdump_types_init(int flags)
 	return ret;
 }
 
+struct list_iter {
+	offset curr;
+	offset prev;
+	offset head;
+	offset last;
+	offset fast;
+	int cont;
+	int error;
+};
+
+static void list_first_from(struct list_iter *iter, offset o_head)
+{
+	char b_head[GET_TYPE_SIZE(list_head)];
+
+	iter->fast = 0;
+	iter->error = 0;
+	iter->cont = 1;
+
+	if (KDUMP_TYPE_GET(list_head, o_head, b_head)) {
+		warning(_("Could not read list_head %llx in list_first()\n"),
+								o_head);
+		iter->error = 1;
+		iter->cont = 0;
+		return;
+	}
+
+	iter->curr = o_head;
+	iter->last = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, prev));
+
+	iter->head = o_head;
+	iter->prev = iter->last;
+}
+
+static void list_first(struct list_iter *iter, offset o_head)
+{
+	char b_head[GET_TYPE_SIZE(list_head)];
+
+	iter->fast = 0;
+	iter->error = 0;
+	iter->cont = 1;
+
+	if (KDUMP_TYPE_GET(list_head, o_head, b_head)) {
+		warning(_("Could not read list_head %llx in list_first()\n"),
+								o_head);
+		iter->error = 1;
+		iter->cont = 0;
+		return;
+	}
+
+	iter->curr = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, next));
+	iter->last = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, prev));
+
+	/* Empty list */
+	if (iter->curr == o_head) {
+		if (iter->last != o_head) {
+			warning(_("list_head %llx is empty, but prev points to %llx\n"),
+							o_head,	iter->last);
+			iter->error = 1;
+		}
+		iter->cont = 0;
+		return;
+	}
+
+	iter->head = o_head;
+	iter->prev = o_head;
+}
+
+static void list_next(struct list_iter *iter)
+{
+	char b_head[GET_TYPE_SIZE(list_head)];
+	offset o_next, o_prev;
+
+	if (KDUMP_TYPE_GET(list_head, iter->curr, b_head)) {
+		warning(_("Could not read list_head %llx in list_next()\n"),
+								iter->curr);
+		iter->error = 1;
+		iter->cont = 0;
+		return;
+	}
+
+	o_next = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, next));
+	o_prev = kt_ptr_value(b_head + MEMBER_OFFSET(list_head, prev));
+
+	if (o_next == iter->head) {
+		if (iter->curr != iter->last) {
+			warning(_("list item %llx appears to be last, but list_head %llx ->prev points to %llx\n"),
+						iter->curr, iter->head,
+						iter->last);
+			iter->error = 1;
+		}
+		iter->cont = 0;
+		return;
+	}
+
+	if (o_prev != iter->prev) {
+		warning(_("list item %llx ->next is %llx but the latter's ->prev is %llx\n"),
+					iter->prev, iter->curr, o_prev);
+		iter->error = 1;
+		/*
+		 * broken ->prev link means that there might be cycle that
+		 * does not include head; start detecting cycles
+		 */
+		if (!iter->fast)
+			iter->fast = iter->curr;
+	}
+
+	/*
+	 * Are we detecting cycles? If so, advance iter->fast to
+	 * iter->curr->next->next and compare iter->curr to both next's
+	 * (Floyd's Tortoise and Hare algorithm)
+	 *
+	 */
+	if (iter->fast) {
+		int i = 2;
+		while(i--) {
+			/*
+			 *  Simply ignore failure to read fast->next, the next
+			 *  call to list_next() will find out anyway.
+			 */
+			if (KDUMP_TYPE_GET(list_head, iter->fast, b_head))
+				break;
+			iter->fast = kt_ptr_value(
+				b_head + MEMBER_OFFSET(list_head, next));
+			if (iter->curr == iter->fast) {
+				warning(_("list_next() detected cycle, aborting traversal\n"));
+				iter->error = 1;
+				iter->cont = 0;
+				return;
+			}
+		}
+	}
+
+	iter->prev = iter->curr;
+	iter->curr = o_next;
+}
+
+#define list_for_each(iter, o_head) \
+	for (list_first(&(iter), o_head); (iter).cont; list_next(&(iter)))
+
+#define list_for_each_from(iter, o_head) \
+	for (list_first_from(&(iter), o_head); (iter).cont; list_next(&(iter)))
+
 int kt_hlist_head_for_each_node (char *addr, int(*func)(void *,offset), void *data)
 {
 	char *b = NULL;
@@ -995,7 +1306,8 @@  static int add_task(offset off_task, int *pid_reserve, char *task)
 			 * FIXME: use the size obtained from debuginfo
 			 */
 			rsp += 0x148;
-			target_read_raw_memory(rsp - 0x8 * (1 + 6), (void*)regs, 0x8 * 6);
+			if (target_read_raw_memory(rsp - 0x8 * (1 + 6), (void*)regs, 0x8 * 6))
+				warning(_("Could not read regs\n"));
 
 			regcache_raw_supply(rc, 15, &regs[5]);
 			regcache_raw_supply(rc, 14, &regs[4]);
@@ -1026,7 +1338,6 @@  static int add_task(offset off_task, int *pid_reserve, char *task)
 			REG(reg_RSP,sp);
 			task_info->sp = reg;
 			REG(reg_RIP,ip);
-			printf ("task %p cpu %02d rip = %p\n", (void*)task_info->task_struct, cpu, reg);
 			task_info->ip = reg;
 			REG(reg_RAX,ax);
 			REG(reg_RCX,cx);
@@ -1092,13 +1403,860 @@  static int add_task(offset off_task, int *pid_reserve, char *task)
 	return 0;
 }
 
+struct list_head {
+	offset next;
+	offset prev;
+};
+
+struct page {
+	unsigned long flags;
+	struct list_head lru;
+	offset first_page;
+	int valid;
+};
+
+enum slab_type {
+	slab_partial,
+	slab_full,
+	slab_free
+};
+
+static const char *slab_type_names[] = {
+	"partial",
+	"full",
+	"free"
+};
+
+enum ac_type {
+	ac_percpu,
+	ac_shared,
+	ac_alien
+};
+
+static const char *ac_type_names[] = {
+	"percpu",
+	"shared",
+	"alien"
+};
+
+typedef unsigned int kmem_bufctl_t;
+#define BUFCTL_END      (((kmem_bufctl_t)(~0U))-0)
+#define BUFCTL_FREE     (((kmem_bufctl_t)(~0U))-1)
+#define BUFCTL_ACTIVE   (((kmem_bufctl_t)(~0U))-2)
+#define SLAB_LIMIT      (((kmem_bufctl_t)(~0U))-3)
+
+
+struct kmem_cache {
+	offset o_cache;
+	const char *name;
+	unsigned int num;
+	htab_t obj_ac;
+	unsigned int buffer_size;
+	int array_caches_inited;
+	int broken;
+};
+
+struct kmem_slab {
+	offset o_slab;
+	kmem_bufctl_t free;
+	unsigned int inuse;
+	offset s_mem;
+	kmem_bufctl_t *bufctl;
+};
+
+/* Cache of kmem_cache structs indexed by offset */
+static htab_t kmem_cache_cache;
+
+/* List_head of all kmem_caches */
+offset o_slab_caches;
+
+/* Just get the least significant bits of the offset */
+static hashval_t kmem_cache_hash(const void *p)
+{
+	return ((struct kmem_cache*)p)->o_cache;
+}
+
+static int kmem_cache_eq(const void *cache, const void *off)
+{
+	return (((struct kmem_cache*)cache)->o_cache == *(offset *)off);
+}
+
+struct kmem_ac {
+	offset offset;
+	enum ac_type type;
+	/* At which node cache resides (-1 for percpu) */
+	int at_node;
+	/* For which node or cpu the cache is (-1 for shared) */
+	int for_node_cpu;
+};
+
+/* A mapping between object's offset and array_cache */
+struct kmem_obj_ac {
+	offset obj;
+	struct kmem_ac *ac;
+};
+
+static hashval_t kmem_ac_hash(const void *p)
+{
+	return ((struct kmem_obj_ac*)p)->obj;
+}
+
+static int kmem_ac_eq(const void *obj, const void *off)
+{
+	return (((struct kmem_obj_ac*)obj)->obj == *(offset *)off);
+}
+
+//FIXME: support the CONFIG_PAGEFLAGS_EXTENDED variant?
+#define PageTail(page)	(page.flags & 1UL << PG_tail)
+#define PageSlab(page)	(page.flags & 1UL << PG_slab)
+
+//TODO: get this via libkdumpfile somehow?
+#define VMEMMAP_START	0xffffea0000000000UL
+#define PAGE_SHIFT	12
+
+static unsigned long long memmap = VMEMMAP_START;
+
+static offset pfn_to_page_memmap(unsigned long pfn)
+{
+	return memmap + pfn*GET_TYPE_SIZE(page);
+}
+
+//TODO: once the config querying below works, support all variants
+#define pfn_to_page(pfn) pfn_to_page_memmap(pfn)
+
+static kdump_paddr_t transform_memory(kdump_paddr_t addr);
+
+static unsigned long addr_to_pfn(offset addr)
+{
+	kdump_paddr_t pa = transform_memory(addr);
+
+	return pa >> PAGE_SHIFT;
+}
+
+#define virt_to_opage(addr)	pfn_to_page(addr_to_pfn(addr))
+static int check_slab_obj(offset obj);
+static int init_kmem_caches(void);
+static struct page virt_to_head_page(offset addr);
+
+
+//TODO: have some hashtable-based cache as well?
+static struct kmem_slab *
+init_kmem_slab(struct kmem_cache *cachep, offset o_slab)
+{
+	char b_slab[GET_TYPE_SIZE(slab)];
+	struct kmem_slab *slab;
+	offset o_bufctl = o_slab + GET_TYPE_SIZE(slab);
+	size_t bufctl_size = cachep->num * sizeof(kmem_bufctl_t);
+	//FIXME: use target's kmem_bufctl_t typedef, which didn't work in
+	//INIT_BASE_TYPE though
+	size_t bufctl_size_target = cachep->num * GET_TYPE_SIZE(_int);
+	char b_bufctl[bufctl_size_target];
+	int i;
+
+	if (KDUMP_TYPE_GET(slab, o_slab, b_slab)) {
+		warning(_("error reading struct slab %llx of cache %s\n"),
+							o_slab, cachep->name);
+		return NULL;
+	}
+
+	slab = malloc(sizeof(struct kmem_slab));
+
+	slab->o_slab = o_slab;
+	slab->inuse = kt_int_value(b_slab + MEMBER_OFFSET(slab, inuse));
+	slab->free = kt_int_value(b_slab + MEMBER_OFFSET(slab, free));
+	slab->s_mem = kt_ptr_value(b_slab + MEMBER_OFFSET(slab, s_mem));
+
+	slab->bufctl = malloc(bufctl_size);
+	if (target_read_raw_memory(o_bufctl, (void *) b_bufctl,
+				bufctl_size_target)) {
+		warning(_("error reading bufctl %llx of slab %llx of cache %s\n"),
+						o_bufctl, o_slab, cachep->name);
+		for (i = 0; i < cachep->num; i++)
+			slab->bufctl[i] = BUFCTL_END;
+
+		return slab;
+	}
+
+	for (i = 0; i < cachep->num; i++)
+		slab->bufctl[i] = kt_int_value(b_bufctl + i*GET_TYPE_SIZE(_int));
+
+	return slab;
+}
+
+static void free_kmem_slab(struct kmem_slab *slab)
+{
+	free(slab->bufctl);
+	free(slab);
+}
+
+static unsigned int
+check_kmem_slab(struct kmem_cache *cachep, struct kmem_slab *slab,
+							enum slab_type type)
+{
+	unsigned int counted_free = 0;
+	kmem_bufctl_t i;
+	offset o_slab = slab->o_slab;
+	offset o_obj, o_prev_obj = 0;
+	struct page page;
+	offset o_page_cache, o_page_slab;
+
+	i = slab->free;
+	while (i != BUFCTL_END) {
+		counted_free++;
+
+		if (counted_free > cachep->num) {
+			printf("free bufctl cycle detected in slab %llx\n", o_slab);
+			break;
+		}
+		if (i > cachep->num) {
+			printf("bufctl value overflow (%d) in slab %llx\n", i, o_slab);
+			break;
+		}
+
+		i = slab->bufctl[i];
+	}
+
+//	printf("slab inuse=%d cnt_free=%d num=%d\n", slab->inuse, counted_free,
+//								cachep->num);
+
+	if (slab->inuse + counted_free != cachep->num)
+		 printf("slab %llx #objs mismatch: inuse=%d + cnt_free=%d != num=%d\n",
+				o_slab, slab->inuse, counted_free, cachep->num);
+
+	switch (type) {
+	case slab_partial:
+		if (!slab->inuse)
+			printf("slab %llx has zero inuse but is on slabs_partial\n", o_slab);
+		else if (slab->inuse == cachep->num)
+			printf("slab %llx is full (%d) but is on slabs_partial\n", o_slab, slab->inuse);
+		break;
+	case slab_full:
+		if (!slab->inuse)
+			printf("slab %llx has zero inuse but is on slabs_full\n", o_slab);
+		else if (slab->inuse < cachep->num)
+			printf("slab %llx has %d/%d inuse but is on slabs_full\n", o_slab, slab->inuse, cachep->num);
+		break;
+	case slab_free:
+		if (slab->inuse)
+			printf("slab %llx has %d/%d inuse but is on slabs_empty\n", o_slab, slab->inuse, cachep->num);
+		break;
+	default:
+		exit(1);
+	}
+
+	for (i = 0; i < cachep->num; i++) {
+		o_obj = slab->s_mem + i * cachep->buffer_size;
+		if (o_prev_obj >> PAGE_SHIFT == o_obj >> PAGE_SHIFT)
+			continue;
+
+		o_prev_obj = o_obj;
+		page = virt_to_head_page(o_obj);
+		if (!page.valid) {
+			warning(_("slab %llx object %llx could not read struct page\n"),
+					o_slab, o_obj);
+			continue;
+		}
+		if (!PageSlab(page))
+			warning(_("slab %llx object %llx is not on PageSlab page\n"),
+					o_slab, o_obj);
+		o_page_cache = page.lru.next;
+		o_page_slab = page.lru.prev;
+
+		if (o_page_cache != cachep->o_cache)
+			warning(_("cache %llx (%s) object %llx is on page where lru.next points to %llx and not the cache\n"),
+					cachep->o_cache, cachep->name, o_obj,
+					o_page_cache);
+		if (o_page_slab != o_slab)
+			warning(_("slab %llx object %llx is on page where lru.prev points to %llx and not the slab\n"),
+					o_slab, o_obj, o_page_slab);
+	}
+
+	return counted_free;
+}
+
+static unsigned long
+check_kmem_slabs(struct kmem_cache *cachep, offset o_slabs,
+							enum slab_type type)
+{
+	struct list_iter iter;
+	offset o_slab;
+	struct kmem_slab *slab;
+	unsigned long counted_free = 0;
+
+//	printf("checking slab list %llx type %s\n", o_slabs,
+//							slab_type_names[type]);
+
+	list_for_each(iter, o_slabs) {
+		o_slab = iter.curr - MEMBER_OFFSET(slab, list);
+//		printf("found slab: %llx\n", o_slab);
+		slab = init_kmem_slab(cachep, o_slab);
+		if (!slab)
+			continue;
+
+		counted_free += check_kmem_slab(cachep, slab, type);
+		free_kmem_slab(slab);
+	}
+
+	return counted_free;
+}
+
+/* Check that o_obj points to an object on slab of kmem_cache */
+static void check_kmem_obj(struct kmem_cache *cachep, offset o_obj)
+{
+	struct page page;
+	offset o_cache, o_slab;
+	offset obj_base;
+	unsigned int idx;
+	struct kmem_slab *slabp;
+
+	page = virt_to_head_page(o_obj);
+
+	if (!PageSlab(page))
+		warning(_("object %llx is not on PageSlab page\n"), o_obj);
+
+	o_cache = page.lru.next;
+	if (o_cache != cachep->o_cache)
+		warning(_("object %llx is on page that should belong to cache "
+				"%llx (%s), but lru.next points to %llx\n"),
+				o_obj, cachep->o_cache, cachep->name, o_obj);
+
+	o_slab = page.lru.prev;
+	slabp = init_kmem_slab(cachep, o_slab);
+
+	//TODO: check also that slabp is in appropriate lists? could be quite slow...
+	if (!slabp)
+		return;
+
+	//TODO: kernel implementation uses reciprocal_divide, check?
+	idx = (o_obj - slabp->s_mem) / cachep->buffer_size;
+	obj_base = slabp->s_mem + idx * cachep->buffer_size;
+
+	if (obj_base != o_obj)
+		warning(_("pointer %llx should point to beginning of object "
+				"but object's address is %llx\n"), o_obj,
+				obj_base);
+
+	if (idx >= cachep->num)
+		warning(_("object %llx has index %u, but there should be only "
+				"%u objects on slabs of cache %llx"),
+				o_obj, idx, cachep->num, cachep->o_cache);
+}
+
+static void init_kmem_array_cache(struct kmem_cache *cachep,
+		offset o_array_cache, char *b_array_cache, enum ac_type type,
+		int id1, int id2)
+{
+	unsigned int avail, limit, i;
+	char *b_entries;
+	offset o_entries = o_array_cache + MEMBER_OFFSET(array_cache, entry);
+	offset o_obj;
+	void **slot;
+	struct kmem_ac *ac;
+	struct kmem_obj_ac *obj_ac;
+
+	avail = kt_int_value(b_array_cache + MEMBER_OFFSET(array_cache, avail));
+	limit = kt_int_value(b_array_cache + MEMBER_OFFSET(array_cache, limit));
+
+//	printf("found %s[%d,%d] array_cache %llx\n", ac_type_names[type],
+//						id1, id2, o_array_cache);
+//	printf("avail=%u limit=%u entries=%llx\n", avail, limit, o_entries);
+
+	if (avail > limit)
+		printf("array_cache %llx has avail=%d > limit=%d\n",
+						o_array_cache, avail, limit);
+
+	if (!avail)
+		return;
+
+	ac = malloc(sizeof(struct kmem_ac));
+	ac->offset = o_array_cache;
+	ac->type = type;
+	ac->at_node = id1;
+	ac->for_node_cpu = id2;
+
+	b_entries = malloc(avail * GET_TYPE_SIZE(_voidp));
+
+	if (target_read_raw_memory(o_entries, (void *)b_entries,
+					avail *	GET_TYPE_SIZE(_voidp))) {
+		warning(_("could not read entries of array_cache %llx of cache %s\n"),
+						o_array_cache, cachep->name);
+		goto done;
+	}
+
+	for (i = 0; i < avail; i++) {
+		o_obj = kt_ptr_value(b_entries + i * GET_TYPE_SIZE(_voidp));
+		//printf("cached obj: %llx\n", o_obj);
+
+		slot = htab_find_slot_with_hash(cachep->obj_ac, &o_obj, o_obj,
+								INSERT);
+
+		if (*slot)
+			printf("obj %llx already in array_cache!\n", o_obj);
+
+		obj_ac = malloc(sizeof(struct kmem_obj_ac));
+		obj_ac->obj = o_obj;
+		obj_ac->ac = ac;
+
+		*slot = obj_ac;
+
+		check_kmem_obj(cachep, o_obj);
+	}
+
+done:
+	free(b_entries);
+}
+
+/* Array of array_caches, such as kmem_cache.array or *kmem_list3.alien */
+static void init_kmem_array_caches(struct kmem_cache *cachep, char * b_caches,
+					int id1, int nr_ids, enum ac_type type)
+{
+	char b_array_cache[GET_TYPE_SIZE(array_cache)];
+	offset o_array_cache;
+	int id;
+
+	for (id = 0; id < nr_ids; id++, b_caches += GET_TYPE_SIZE(_voidp)) {
+		/*
+		 * A node cannot have alien cache on the same node, but some
+		 * kernels (-xen) apparently don't have the corresponding
+		 * array_cache pointer NULL, so skip it now.
+		 */
+		if (type == ac_alien && id1 == id)
+			continue;
+		o_array_cache = kt_ptr_value(b_caches);
+		if (!o_array_cache)
+			continue;
+		if (KDUMP_TYPE_GET(array_cache, o_array_cache, b_array_cache)) {
+			warning(_("could not read array_cache %llx of cache %s type %s id1=%d id2=%d\n"),
+					o_array_cache, cachep->name,
+					ac_type_names[type], id1,
+					type == ac_shared ? -1 : id);
+			continue;
+		}
+		init_kmem_array_cache(cachep, o_array_cache, b_array_cache,
+			type, id1, type == ac_shared ? -1 : id);
+	}
+}
+
+static void init_kmem_list3_arrays(struct kmem_cache *cachep, offset o_list3,
+								int nid)
+{
+	char b_list3[GET_TYPE_SIZE(kmem_list3)];
+	char *b_shared_caches;
+	offset o_alien_caches;
+	char b_alien_caches[nr_node_ids * GET_TYPE_SIZE(_voidp)];
+
+	if (KDUMP_TYPE_GET(kmem_list3, o_list3, b_list3)) {
+                warning(_("error reading kmem_list3 %llx of nid %d of kmem_cache %llx name %s\n"),
+				o_list3, nid, cachep->o_cache, cachep->name);
+		return;
+	}
+
+	/* This is a single pointer, but treat it as array to reuse code */
+	b_shared_caches = b_list3 + MEMBER_OFFSET(kmem_list3, shared);
+	init_kmem_array_caches(cachep, b_shared_caches, nid, 1, ac_shared);
+
+	o_alien_caches = kt_ptr_value(b_list3 + 
+					MEMBER_OFFSET(kmem_list3, alien));
+
+	//TODO: check that this only happens for single-node systems?
+	if (!o_alien_caches)
+		return;
+
+	if (target_read_raw_memory(o_alien_caches, (void *)b_alien_caches,
+					nr_node_ids * GET_TYPE_SIZE(_voidp))) {
+		warning(_("could not read alien array %llx of kmem_list3 %llx of nid %d of cache %s\n"),
+				o_alien_caches, o_list3, nid, cachep->name);
+	}
+
+
+	init_kmem_array_caches(cachep, b_alien_caches, nid, nr_node_ids,
+								ac_alien);
+}
+
+static void check_kmem_list3_slabs(struct kmem_cache *cachep,
+						offset o_list3,	int nid)
+{
+	char b_list3[GET_TYPE_SIZE(kmem_list3)];
+	offset o_lhb;
+	unsigned long counted_free = 0;
+	unsigned long free_objects;
+
+	if(KDUMP_TYPE_GET(kmem_list3, o_list3, b_list3)) {
+                warning(_("error reading kmem_list3 %llx of nid %d of kmem_cache %llx name %s\n"),
+				o_list3, nid, cachep->o_cache, cachep->name);
+		return;
+	}
+
+	free_objects = kt_long_value(b_list3 + MEMBER_OFFSET(kmem_list3,
+							free_objects));
+
+	o_lhb = o_list3 + MEMBER_OFFSET(kmem_list3, slabs_partial);
+	counted_free += check_kmem_slabs(cachep, o_lhb, slab_partial);
+
+	o_lhb = o_list3 + MEMBER_OFFSET(kmem_list3, slabs_full);
+	counted_free += check_kmem_slabs(cachep, o_lhb, slab_full);
+
+	o_lhb = o_list3 + MEMBER_OFFSET(kmem_list3, slabs_free);
+	counted_free += check_kmem_slabs(cachep, o_lhb, slab_free);
+
+//	printf("free=%lu counted=%lu\n", free_objects, counted_free);
+	if (free_objects != counted_free)
+		warning(_("cache %s should have %lu free objects but we counted %lu\n"),
+				cachep->name, free_objects, counted_free);
+}
+
+static struct kmem_cache *init_kmem_cache(offset o_cache)
+{
+	struct kmem_cache *cache;
+	char b_cache[GET_TYPE_SIZE(kmem_cache)];
+	offset o_cache_name;
+	void **slot;
+
+	if (!kmem_cache_cache)
+		init_kmem_caches();
+
+	slot = htab_find_slot_with_hash(kmem_cache_cache, &o_cache, o_cache,
+								INSERT);
+	if (*slot) {
+		cache = (struct kmem_cache*) *slot;
+//		printf("kmem_cache %s found in hashtab!\n", cache->name);
+		return cache;
+	}
+
+//	printf("kmem_cache %llx not found in hashtab, inserting\n", o_cache);
+
+	cache = malloc(sizeof(struct kmem_cache));
+	cache->o_cache = o_cache;
+
+	if (KDUMP_TYPE_GET(kmem_cache, o_cache, b_cache)) {
+		warning(_("error reading contents of kmem_cache at %llx\n"),
+								o_cache);
+		cache->broken = 1;
+		cache->name = "(broken)";
+		goto done;
+	}
+
+	cache->num = kt_int_value(b_cache + MEMBER_OFFSET(kmem_cache, num));
+	cache->buffer_size = kt_int_value(b_cache + MEMBER_OFFSET(kmem_cache,
+								buffer_size));
+	cache->array_caches_inited = 0;
+
+	o_cache_name = kt_ptr_value(b_cache + MEMBER_OFFSET(kmem_cache,name));
+	if (!o_cache_name) {
+		fprintf(stderr, "cache name pointer NULL\n");
+		cache->name = "(null)";
+	}
+
+	cache->name = kt_strndup(o_cache_name, 128);
+	cache->broken = 0;
+//	printf("cache name is: %s\n", cache->name);
+
+done:
+	*slot = cache;
+	return cache;
+}
+
+static void init_kmem_cache_arrays(struct kmem_cache *cache)
+{
+	char b_cache[GET_TYPE_SIZE(kmem_cache)];
+	char *b_nodelists, *b_array_caches;
+	offset o_nodelist, o_array_cache;
+	char *nodelist, *array_cache;
+	int node;
+
+	if (cache->array_caches_inited || cache->broken)
+		return;
+
+	if (KDUMP_TYPE_GET(kmem_cache, cache->o_cache, b_cache)) {
+		warning(_("error reading contents of kmem_cache at %llx\n"),
+							cache->o_cache);
+		return;
+	}
+
+
+	cache->obj_ac = htab_create_alloc(64, kmem_ac_hash, kmem_ac_eq,
+						NULL, xcalloc, xfree);
+
+	b_nodelists = b_cache + MEMBER_OFFSET(kmem_cache, nodelists);
+	for (node = 0; node < nr_node_ids;
+			node++, b_nodelists += GET_TYPE_SIZE(_voidp)) {
+		o_nodelist = kt_ptr_value(b_nodelists);
+		if (!o_nodelist)
+			continue;
+//		printf("found nodelist[%d] %llx\n", node, o_nodelist);
+		init_kmem_list3_arrays(cache, o_nodelist, node);
+	}
+
+	b_array_caches = b_cache + MEMBER_OFFSET(kmem_cache, array);
+	init_kmem_array_caches(cache, b_array_caches, -1, nr_cpu_ids,
+								ac_percpu);
+
+	cache->array_caches_inited = 1;
+}
+
+static void check_kmem_cache(struct kmem_cache *cache)
+{
+	char b_cache[GET_TYPE_SIZE(kmem_cache)];
+	char *b_nodelists, *b_array_caches;
+	offset o_nodelist, o_array_cache;
+	char *nodelist, *array_cache;
+	int node;
+
+	init_kmem_cache_arrays(cache);
+
+	if (KDUMP_TYPE_GET(kmem_cache, cache->o_cache, b_cache)) {
+		warning(_("error reading contents of kmem_cache at %llx\n"),
+							cache->o_cache);
+		return;
+	}
+
+	b_nodelists = b_cache + MEMBER_OFFSET(kmem_cache, nodelists);
+	for (node = 0; node < nr_node_ids;
+			node++, b_nodelists += GET_TYPE_SIZE(_voidp)) {
+		o_nodelist = kt_ptr_value(b_nodelists);
+		if (!o_nodelist)
+			continue;
+//		printf("found nodelist[%d] %llx\n", node, o_nodelist);
+		check_kmem_list3_slabs(cache, o_nodelist, node);
+	}
+}
+
+static int init_kmem_caches(void)
+{
+	offset o_kmem_cache;
+	struct list_iter iter;
+	offset o_nr_node_ids, o_nr_cpu_ids;
+
+	kmem_cache_cache = htab_create_alloc(64, kmem_cache_hash,
+					kmem_cache_eq, NULL, xcalloc, xfree);
+
+	o_slab_caches = get_symbol_value("slab_caches");
+	if (! o_slab_caches) {
+		o_slab_caches = get_symbol_value("cache_chain");
+		if (!o_slab_caches) {
+			warning(_("Cannot find slab_caches\n"));
+			return -1;
+		}
+	}
+	printf("slab_caches: %llx\n", o_slab_caches);
+
+	o_nr_cpu_ids = get_symbol_value("nr_cpu_ids");
+	if (! o_nr_cpu_ids) {
+		warning(_("nr_cpu_ids not found, assuming 1 for !SMP"));
+	} else {
+		printf("o_nr_cpu_ids = %llx\n", o_nr_cpu_ids);
+		nr_cpu_ids = kt_int_value_off(o_nr_cpu_ids);
+		printf("nr_cpu_ids = %d\n", nr_cpu_ids);
+	}
+
+	o_nr_node_ids = get_symbol_value("nr_node_ids");
+	if (! o_nr_node_ids) {
+		warning(_("nr_node_ids not found, assuming 1 for !NUMA"));
+	} else {
+		printf("o_nr_node_ids = %llx\n", o_nr_node_ids);
+		nr_node_ids = kt_int_value_off(o_nr_node_ids);
+		printf("nr_node_ids = %d\n", nr_node_ids);
+	}
+
+	list_for_each(iter, o_slab_caches) {
+		o_kmem_cache = iter.curr - MEMBER_OFFSET(kmem_cache,list);
+//		printf("found kmem cache: %llx\n", o_kmem_cache);
+
+		init_kmem_cache(o_kmem_cache);
+	}
+
+	return 0;
+}
+
+static void check_kmem_caches(void)
+{
+	offset o_lhb, o_kmem_cache;
+	struct list_iter iter;
+	struct kmem_cache *cache;
+
+	if (!kmem_cache_cache)
+		init_kmem_caches();
+
+	list_for_each(iter, o_slab_caches) {
+		o_kmem_cache = iter.curr - MEMBER_OFFSET(kmem_cache,list);
+
+		cache = init_kmem_cache(o_kmem_cache);
+		printf("checking kmem cache %llx name %s\n", o_kmem_cache,
+				cache->name);
+		if (cache->broken) {
+			printf("cache is too broken, skipping");
+			continue;
+		}
+		check_kmem_cache(cache);
+	}
+}
+
+
+
+
+static struct page read_page(offset o_page)
+{
+	char b_page[GET_TYPE_SIZE(page)];
+	struct page page;
+
+	if (KDUMP_TYPE_GET(page, o_page, b_page)) {
+		page.valid = 0;
+		return page;
+	}
+
+	page.flags = kt_long_value(b_page + MEMBER_OFFSET(page, flags));
+	page.lru.next = kt_ptr_value(b_page + MEMBER_OFFSET(page, lru)
+					+ MEMBER_OFFSET(list_head, next));
+	page.lru.prev = kt_ptr_value(b_page + MEMBER_OFFSET(page, lru)
+					+ MEMBER_OFFSET(list_head, prev));
+	page.first_page = kt_ptr_value(b_page +
+					MEMBER_OFFSET(page, first_page));
+	page.valid = 1;
+
+	return page;
+}
+
+static inline struct page compound_head(struct page page)
+{
+	if (page.valid && PageTail(page))
+		return read_page(page.first_page);
+	return page;
+}
+
+static struct page virt_to_head_page(offset addr)
+{
+	struct page page;
+
+	page = read_page(virt_to_opage(addr));
+
+	return compound_head(page);
+}
+
+static int check_slab_obj(offset obj)
+{
+	struct page page;
+	offset o_cache, o_slab;
+	struct kmem_cache *cachep;
+	struct kmem_slab *slabp;
+	struct kmem_obj_ac *obj_ac;
+	struct kmem_ac *ac;
+	unsigned int idx;
+	offset obj_base;
+	unsigned int i, cnt = 0;
+	int free = 0;
+
+	page = virt_to_head_page(obj);
+
+	if (!page.valid) {
+		warning(_("unable to read struct page for object at %llx\n"),
+				obj);
+		return 0;
+	}
+
+	if (!PageSlab(page))
+		return 0;
+
+	o_cache = page.lru.next;
+	o_slab = page.lru.prev;
+	printf("pointer %llx is on slab %llx of cache %llx\n", obj, o_slab,
+								o_cache);
+
+	cachep = init_kmem_cache(o_cache);
+	init_kmem_cache_arrays(cachep);
+	slabp = init_kmem_slab(cachep, o_slab);
+
+	//TODO: kernel implementation uses reciprocal_divide, check?
+	idx = (obj - slabp->s_mem) / cachep->buffer_size;
+	obj_base = slabp->s_mem + idx * cachep->buffer_size;
+
+	printf("pointer is to object %llx with index %u\n", obj_base, idx);
+
+	i = slabp->free;
+	while (i != BUFCTL_END) {
+		cnt++;
+
+		if (cnt > cachep->num) {
+			printf("free bufctl cycle detected in slab %llx\n", o_slab);
+			break;
+		}
+		if (i > cachep->num) {
+			printf("bufctl value overflow (%d) in slab %llx\n", i, o_slab);
+			break;
+		}
+
+		if (i == idx)
+			free = 1;
+
+		i = slabp->bufctl[i];
+	}
+
+	printf("object is %s\n", free ? "free" : "allocated");
+
+	obj_ac = htab_find_with_hash(cachep->obj_ac, &obj, obj);
+
+	if (obj_ac) {
+		ac = obj_ac->ac;
+		printf("object is in array_cache %llx type %s[%d,%d]\n",
+			ac->offset, ac_type_names[ac->type], ac->at_node,
+			ac->for_node_cpu);
+	}
+
+	free_kmem_slab(slabp);
+
+	return 1;
+}
+
+static int init_memmap(void)
+{
+	const char *cfg;
+	offset o_mem_map;
+	offset o_page;
+	struct page page;
+	unsigned long long p_memmap;
+
+	//FIXME: why are all NULL?
+
+	cfg = kdump_vmcoreinfo_row(dump_ctx, "CONFIG_FLATMEM");
+	printf("CONFIG_FLATMEM=%s\n", cfg ? cfg : "(null)");
+
+	cfg = kdump_vmcoreinfo_row(dump_ctx, "CONFIG_DISCONTIGMEM");
+	printf("CONFIG_DISCONTIGMEM=%s\n", cfg ? cfg : "(null)");
+
+	cfg = kdump_vmcoreinfo_row(dump_ctx, "CONFIG_SPARSEMEM_VMEMMAP");
+	printf("CONFIG_SPARSEMEM_VMEMMAP=%s\n", cfg ? cfg : "(null)");
+
+	o_mem_map = get_symbol_value("mem_map");
+	printf("memmap: %llx\n", o_mem_map);
+
+	if (o_mem_map) {
+		p_memmap = kt_ptr_value_off(o_mem_map);
+		printf("memmap is pointer to: %llx\n", p_memmap);
+		if (p_memmap != -1)
+			memmap = p_memmap;
+	}
+
+/*
+	o_page = virt_to_opage(0xffff880138bedf40UL);
+	printf("ffff880138bedf40 is page %llx\n", o_page);
+
+	page = read_page(o_page);
+	printf("flags=%lx lru=(%llx,%llx) first_page=%llx\n",page.flags,
+			page.lru.next, page.lru.prev, page.first_page);
+	printf("PG_slab=%llx\n", get_symbol_value("PG_slab"));
+	printf("PageSlab(page)==%d\n", PageSlab(page));
+*/
+	return 0;
+}
+
 static int init_values(void);
 static int init_values(void)
 {
 	struct symbol *s;
 	char *b = NULL, *init_task = NULL, *task = NULL;
-	offset off, off_task, rsp, rip, _rsp;
+	offset off, o_task, rsp, rip, _rsp;
 	offset tasks;
+	offset o_tasks;
+	offset off_task;
 	offset stack;
 	offset o_init_task;
 	int state;
@@ -1108,6 +2266,7 @@  static int init_values(void)
 	int cnt = 0;
 	int pid_reserve;
 	struct task_info *task_info;
+	struct list_iter iter;
 
 	s = NULL;
 	
@@ -1141,58 +2300,59 @@  static int init_values(void)
 		goto error;
 	task = KDUMP_TYPE_ALLOC(task_struct);
 	if (!task) goto error;
+
 	if (KDUMP_TYPE_GET(task_struct, o_init_task, init_task))
 		goto error;
 	tasks = kt_ptr_value(init_task + MEMBER_OFFSET(task_struct,tasks));
+	o_tasks = o_init_task + MEMBER_OFFSET(task_struct, tasks);
 
 	i = 0;
-	off = 0;
 	pid_reserve = 50000;
 
 	print_thread_events = 0;
 	in = current_inferior();
 	inferior_appeared (in, 1);
 
-	list_head_for_each(tasks, init_task + MEMBER_OFFSET(task_struct,tasks), off) {
-		
+	list_for_each_from(iter, o_tasks) {
+
 		struct thread_info *info;
 		int pid;
 		ptid_t tt;
 		struct regcache *rc;
 		long long val;
 		offset main_tasks, mt;
-		
+		struct list_iter iter_thr;
+		offset o_threads;
 
 		//fprintf(stderr, __FILE__":%d: ok\n", __LINE__);
 		off_task = off - MEMBER_OFFSET(task_struct,tasks);
 		if (KDUMP_TYPE_GET(task_struct, off_task, task)) continue;
 
-		main_tasks = off_task;//kt_ptr_value(task + MEMBER_OFFSET(task_struct,thread_group));
+		o_task = iter.curr - MEMBER_OFFSET(task_struct, tasks);
+		o_threads = o_task + MEMBER_OFFSET(task_struct, thread_group);
+		list_for_each_from(iter_thr, o_threads) {
 
-		do {
-		//list_head_for_each(main_tasks, task + MEMBER_OFFSET(task_struct,thread_group), mt) {
-
-			//off_task = mt - MEMBER_OFFSET(task_struct,thread_group);
-			if (KDUMP_TYPE_GET(task_struct, off_task, task))  {
+			o_task = iter_thr.curr - MEMBER_OFFSET(task_struct,
+								thread_group);
+			if (KDUMP_TYPE_GET(task_struct, o_task, task))
 				continue;
-			}
-
-			if (add_task(off_task, &pid_reserve, task)) {
-
-			} else {
-				
-				printf_unfiltered(_("Loaded processes: %d\r"), ++cnt);
-			}
-			off_task = kt_ptr_value(task + MEMBER_OFFSET(task_struct, thread_group)) - MEMBER_OFFSET(task_struct, thread_group);
-			if (off_task == main_tasks) break;
 
-		} while (1);
+			if (!add_task(o_task, &pid_reserve, task))
+				printf_unfiltered(_("Loaded processes: %d\r"),
+									++cnt);
+		}
 	}
 
 	if (b) free(b);
 	if (init_task) free(init_task);
 
 	printf_unfiltered(_("Loaded processes: %d\n"), cnt);
+	init_memmap();
+
+//	check_kmem_caches();
+//	check_slab_obj(0xffff880138bedf40UL);
+//	check_slab_obj(0xffff8801359734c0UL);
+
 	return 0;
 error:
 	if (b) free(b);
@@ -1373,7 +2533,6 @@  core_detach (struct target_ops *ops, const char *args, int from_tty)
 		printf_filtered (_("No core file now.\n"));
 }
 
-static kdump_paddr_t transform_memory(kdump_paddr_t addr);
 static kdump_paddr_t transform_memory(kdump_paddr_t addr)
 {
 	kdump_paddr_t out;
@@ -1396,10 +2555,12 @@  kdump_xfer_partial (struct target_ops *ops, enum target_object object,
 	{
 		case TARGET_OBJECT_MEMORY:
 			offset = transform_memory((kdump_paddr_t)offset);
-			r = kdump_read(dump_ctx, (kdump_paddr_t)offset, (unsigned char*)readbuf, (size_t)len, KDUMP_PHYSADDR);
+			r = kdump_read(dump_ctx, KDUMP_KPHYSADDR, (kdump_paddr_t)offset, (unsigned char*)readbuf, (size_t)len);
 			if (r != len) {
-				error(_("Cannot read %lu bytes from %lx (%lld)!"), (size_t)len, (long unsigned int)offset, (long long)r);
-			} else
+				warning(_("Cannot read %lu bytes from %lx (%lld)!"),
+						(size_t)len, (long unsigned int)offset, (long long)r);
+				return TARGET_XFER_E_IO;
+			} else 
 				*xfered_len = len;
 
 			return TARGET_XFER_OK;
@@ -1797,7 +2958,9 @@  static void kdumpps_command(char *fn, int from_tty)
 		if (!task) continue;
 		if (task->cpu == -1) cpu[0] = '\0';
 		else snprintf(cpu, 5, "% 4d", task->cpu);
+#ifdef _DEBUG
 		printf_filtered(_("% 7d %llx %llx %llx %-4s %s\n"), task->pid, task->task_struct, task->ip, task->sp, cpu, tp->name);
+#endif
 	}
 }