All of lore.kernel.org
 help / color / mirror / Atom feed
* [Demo/lkm] Kcoredump -- Do coredump in any where of kernel(not same  with kcore)
@ 2009-09-22  8:41 Hui Zhu
  2009-09-22  9:31 ` Américo Wang
  0 siblings, 1 reply; 3+ messages in thread
From: Hui Zhu @ 2009-09-22  8:41 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1103 bytes --]

Hi guys,

Kcoredump do a coredump in most part of kernel (it use kprobe, it can
be set most of part of kernel).  For example:
insmod kcoredump.ko name=do_fork offset=11
gdb ./vmlinux /proc/kcoredump
Core was generated by `'.
[New process 0]
#0  do_fork (clone_flags=18874385, stack_start=3219612376, regs=0xc6c8ffb4,
    stack_size=0, parent_tidptr=0x0, child_tidptr=0xb764b758)
    at /home/teawater/kernel/linux-2.6/kernel/fork.c:1343

warning: Source file is more recent than executable.
1343		if (clone_flags & CLONE_NEWUSER) {
(gdb) bt
#0  do_fork (clone_flags=18874385, stack_start=3219612376, regs=0xc6c8ffb4,
    stack_size=0, parent_tidptr=0x0, child_tidptr=0xb764b758)
    at /home/teawater/kernel/linux-2.6/kernel/fork.c:1343
#1  0xc01016d5 in sys_clone (regs=0xc6c8ffb4)
    at /home/teawater/kernel/linux-2.6/arch/x86/kernel/process_32.c:445
#2  0xc0102da1 in system_call ()
    at /home/teawater/kernel/linux-2.6/arch/x86/kernel/entry_32.S:529
#3  0x00000000 in ?? ()

Now, it is just a demo version.  It just support x86_32.  But make it
support other arch is not very hard.

Thanks,
Hui

[-- Attachment #2: kcoredump.c --]
[-- Type: text/x-csrc, Size: 9542 bytes --]

/*
 * Kernel core dump.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * Copyright (C) Hui Zhu (teawater@gmail.com), 2009
 *
 * 2002-09-22	Demo release.  Just support X86_32.
 */

#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/elfcore.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

#ifndef CONFIG_KPROBES
#error "Linux Kernel doesn't support KPROBES.  Please open it in 'General setup->Kprobes'."
#endif

#define KNAME_NOT_SET		"NOT SET"
#define ELFNOTELEN(datalen)	(sizeof(struct elf_note) + roundup(sizeof ("CORE"), 4) + roundup(datalen, 4))
#define STACKLEN		512
#define KBUFLEN			(sizeof(struct elfhdr) + sizeof(struct elf_phdr) * 2 + ELFNOTELEN(sizeof(struct elf_prstatus)) + ELFNOTELEN(sizeof(struct elf_prpsinfo)) + STACKLEN)

static char			*kname = KNAME_NOT_SET;
static unsigned int		koffset = 0;
static unsigned int		kaddr = 0;
static struct proc_dir_entry	*kcoredump_file = NULL;

static uint8_t			kbuf[KBUFLEN];
static struct elf_prstatus	*kpstat = NULL;
static struct elf_phdr		*kphdr = NULL;
static uint8_t			*kstack;

static int
handler_pre(struct kprobe *p, struct pt_regs *regs)
{
	static int dumped = 0;
	static DEFINE_SPINLOCK(dumped_lock);

	spin_lock(&dumped_lock);
	if (dumped) {
		spin_unlock(&dumped_lock);
		return 0;
	}
	dumped = 1;
	spin_unlock(&dumped_lock);

#ifdef CONFIG_X86_32
	/* Set regs to kpstat->pr_reg. */
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,24))
	kpstat->pr_reg[0] = regs->bx;
	kpstat->pr_reg[1] = regs->cx;
	kpstat->pr_reg[2] = regs->dx;
	kpstat->pr_reg[3] = regs->si;
	kpstat->pr_reg[4] = regs->di;
	kpstat->pr_reg[5] = regs->bp;
	kpstat->pr_reg[6] = regs->ax;
	kpstat->pr_reg[7] = regs->ds;
	kpstat->pr_reg[8] = regs->es;
	kpstat->pr_reg[9] = regs->fs;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
	kpstat->pr_reg[10] = regs->gs;
#else
	kpstat->pr_reg[10] = 0;
#endif
	kpstat->pr_reg[11] = regs->orig_ax;
	kpstat->pr_reg[12] = regs->ip - 1;
	kpstat->pr_reg[13] = regs->cs;
	kpstat->pr_reg[14] = regs->flags;
	kpstat->pr_reg[15] = (unsigned long)&regs->sp;
	kpstat->pr_reg[16] = 0;
#else
	kpstat->pr_reg[0] = regs->ebx;
	kpstat->pr_reg[1] = regs->ecx;
	kpstat->pr_reg[2] = regs->edx;
	kpstat->pr_reg[3] = regs->esi;
	kpstat->pr_reg[4] = regs->edi;
	kpstat->pr_reg[5] = regs->ebp;
	kpstat->pr_reg[6] = regs->eax;
	kpstat->pr_reg[7] = regs->xds;
	kpstat->pr_reg[8] = regs->xes;
	kpstat->pr_reg[9] = regs->xfs;
	/* kpstat->pr_reg[10] = regs->gs; */
	kpstat->pr_reg[11] = regs->orig_eax;
	kpstat->pr_reg[12] = regs->eip - 1;
	kpstat->pr_reg[13] = regs->xcs;
	kpstat->pr_reg[14] = regs->eflags;
	kpstat->pr_reg[15] = (unsigned long)&regs->esp;
	kpstat->pr_reg[16] = 0;
#endif
	/* Set kpstat->pr_reg[15] to kphdr->p_vaddr. */
	kphdr->p_vaddr = kpstat->pr_reg[15] & ~63;

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25))
	if (probe_kernel_read (kstack, (void *)kphdr->p_vaddr, STACKLEN))
		memset (kstack, 0, STACKLEN);
#else
	memcpy (kstack, (void *)kphdr->p_vaddr, STACKLEN);
#endif

	return 0;
#else
#error "This architecture doesn't support kcoredump."
#endif
}

static void
handler_post(struct kprobe *p, struct pt_regs *regs,
				unsigned long flags)
{
}

static int
handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
	printk(KERN_ERR "kcoredump: get fault addr = 0x%p, trap #%dn\n",
		p->addr, trapnr);
	return 0;
}

static struct kprobe kp = {
	.pre_handler	= handler_pre,
	.post_handler	= handler_post,
	.fault_handler	= handler_fault,
};

static int
fill_elf_note (uint8_t *p, const char *name, int type, void **data, int data_len)
{
	int		ret = 0;
	struct elf_note	*en = (struct elf_note *) p;

	en = (struct elf_note *) p;
	p += sizeof(struct elf_note);
	ret += sizeof(struct elf_note);
	en->n_namesz = strlen(name) + 1;
	en->n_type = type;
	en->n_descsz = data_len;

	memcpy (p, name, en->n_namesz);
	p += en->n_namesz;
	p = (uint8_t *) roundup((unsigned long) p, 4);
	ret += roundup((en->n_namesz), 4);

	*data = p;
	p += data_len;
	p = (uint8_t *) roundup((unsigned long) p, 4);
	ret += roundup(data_len, 4);

	return ret;
}

static int
init_kbuf (void)
{
	uint8_t			*wp = kbuf;
	struct elfhdr		*elf;
	struct elf_phdr		*nhdr;
	struct elf_prpsinfo	*pinfo;

	/* elf head */
	elf = (struct elfhdr *) wp;
	wp += sizeof(struct elfhdr);
	memset(elf, 0, sizeof(*elf));
	memcpy(elf->e_ident, ELFMAG, SELFMAG);
	elf->e_ident[EI_CLASS] = ELF_CLASS;
	elf->e_ident[EI_DATA] = ELF_DATA;
	elf->e_ident[EI_VERSION] = EV_CURRENT;
	elf->e_ident[EI_OSABI] = ELF_OSABI;
	elf->e_type = ET_CORE;
	elf->e_machine = ELF_ARCH;
	elf->e_version = EV_CURRENT;
	elf->e_phoff = sizeof(struct elfhdr);
	elf->e_flags = 0;
	elf->e_ehsize = sizeof(struct elfhdr);
	elf->e_phentsize = sizeof(struct elf_phdr);
	/* sections number (vma + 1) */
	elf->e_phnum = 1 + 1;

	/* nhdr */
	nhdr = (struct elf_phdr *) wp;
	wp += sizeof(struct elf_phdr);
	nhdr->p_type	= PT_NOTE;
	nhdr->p_offset	= sizeof(struct elfhdr) + sizeof(struct elf_phdr) * 2;
	nhdr->p_vaddr	= 0;
	nhdr->p_paddr	= 0;
	nhdr->p_filesz	= 0;
	nhdr->p_memsz	= 0;
	nhdr->p_flags	= 0;
	nhdr->p_align	= 0;

	/* kphdr */
	kphdr = (struct elf_phdr *) wp;
	wp += sizeof(struct elf_phdr);
	kphdr->p_type = PT_LOAD;
	kphdr->p_flags = PF_R;
	kphdr->p_offset = nhdr->p_offset;
	/* kphdr->p_vaddr will be set in handler_pre.  */
	kphdr->p_vaddr = 0;
	kphdr->p_paddr = 0;
	kphdr->p_filesz = kphdr->p_memsz = STACKLEN;
	kphdr->p_align = 1;

	/* note0 kpstat */
	if (fill_elf_note (wp, "CORE", NT_PRSTATUS, ((void **)&kpstat),
				sizeof(struct elf_prstatus)) != ELFNOTELEN(sizeof(struct elf_prstatus)))
		return -1;
	wp += ELFNOTELEN(sizeof(struct elf_prstatus));
	nhdr->p_filesz += ELFNOTELEN(sizeof(struct elf_prstatus));
	/* kpstat->pr_reg will be set in handler_pre.  */
	memset (kpstat, 0, sizeof(struct elf_prstatus));

	/* note1 pinfo */
	if (fill_elf_note (wp, "CORE", NT_PRPSINFO, ((void **)&pinfo),
				sizeof(struct elf_prpsinfo)) != ELFNOTELEN(sizeof(struct elf_prpsinfo)))
		return -1;
	wp += ELFNOTELEN(sizeof(struct elf_prpsinfo));
	nhdr->p_filesz += ELFNOTELEN(sizeof(struct elf_prpsinfo));
	memset (pinfo, 0, sizeof(struct elf_prpsinfo));
	pinfo->pr_state = 0;
	pinfo->pr_sname = 'R';
	pinfo->pr_zomb = 0;
	snprintf(pinfo->pr_fname, 16, "%s", "vmlinux");
	//snprintf(pinfo->pr_psargs, ELF_PRARGSZ, "%s", saved_command_line);
	snprintf(pinfo->pr_psargs, ELF_PRARGSZ, "%s", "");

	/* kstack */
	kphdr->p_offset += nhdr->p_filesz;
	kstack = wp;
	memset (kstack, 0, STACKLEN);
	wp += STACKLEN;

	/* Check size.  */
	if (wp - kbuf != KBUFLEN)
		return -1;

	return 0;
}

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
static int
seq_write(struct seq_file *seq, const void *data, size_t len)
{
	if (seq->count + len < seq->size) {
		memcpy(seq->buf + seq->count, data, len);
		seq->count += len;
		return 0;
	}
	seq->count = seq->size;
	return -1;
}
#endif

static int
kcoredump_seq_show(struct seq_file *s, void *v)
{
	return seq_write (s, kbuf, KBUFLEN);
}

static int
kcoredump_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, kcoredump_seq_show, NULL);
}

static const struct file_operations kcoredump_proc_ops = {
	.owner		= THIS_MODULE,
	.open		= kcoredump_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
static inline struct proc_dir_entry *
proc_create(const char *name, mode_t mode, struct proc_dir_entry *parent,
		const struct file_operations *proc_fops)
{
	struct proc_dir_entry *ret = create_proc_entry(name, mode, parent);
	if (!ret)
		return NULL;

	ret->proc_fops = proc_fops;

	return ret;
}
#endif

int init_module(void)
{
	int	ret = 0;

	/* Check argument.  */
	if (strcmp (kname, KNAME_NOT_SET)) {
		kp.symbol_name = kname;
		kp.offset = koffset;
	}
	else {
		if (kaddr) {
			kp.addr = (kprobe_opcode_t *)kaddr;
		}
		else {
			printk (KERN_ERR "kcoredump: must set the name or addr.\n");
			return -EINVAL;
		}
	}

	if (init_kbuf ()) {
		printk (KERN_ERR "kcoredump: init buf error.\n");
		return -EIO;
	}

	kcoredump_file = proc_create ("kcoredump", 0444, NULL, &kcoredump_proc_ops);
	if (!kcoredump_file) {
		printk (KERN_ERR "kcoredump: create proc file error.\n");
		return -ENOMEM;
	}
	/* kcoredump_file->owner = THIS_MODULE; */
	kcoredump_file->size = KBUFLEN;

	ret = register_kprobe(&kp);
	if (ret < 0) {
		printk (KERN_ERR "kcoredump: register kprobe error.\n");
		remove_proc_entry("kcoredump", NULL);
		return ret;
	}

	printk(KERN_NOTICE "kcoredump: dump in 0x%p.\n", kp.addr);

	return 0;
}

void cleanup_module(void)
{
	unregister_kprobe(&kp);
	remove_proc_entry("kcoredump", NULL);
}

module_param_named(name, kname, charp, 0644);
module_param_named(offset, koffset, uint, 0644);
module_param_named(addr, kaddr, uint, 0644);

MODULE_AUTHOR("Hui Zhu");
MODULE_LICENSE("GPL");

[-- Attachment #3: Makefile --]
[-- Type: application/octet-stream, Size: 248 bytes --]

obj-m := kcoredump.o

KERNELDIR := /lib/modules/`uname -r`/build
PWD  := $(shell pwd)

default:
	$(MAKE) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules

clean:
	$(MAKE) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) clean

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Demo/lkm] Kcoredump -- Do coredump in any where of kernel(not  same with kcore)
  2009-09-22  8:41 [Demo/lkm] Kcoredump -- Do coredump in any where of kernel(not same with kcore) Hui Zhu
@ 2009-09-22  9:31 ` Américo Wang
  2009-09-22 14:18   ` Hui Zhu
  0 siblings, 1 reply; 3+ messages in thread
From: Américo Wang @ 2009-09-22  9:31 UTC (permalink / raw)
  To: Hui Zhu; +Cc: linux-kernel

On Tue, Sep 22, 2009 at 4:41 PM, Hui Zhu <teawater@gmail.com> wrote:
> Hi guys,
>
> Kcoredump do a coredump in most part of kernel (it use kprobe, it can
> be set most of part of kernel).  For example:
> insmod kcoredump.ko name=do_fork offset=11
> gdb ./vmlinux /proc/kcoredump
> Core was generated by `'.

But what is your point of generating coredump anywhere?
I mean what do you really want by this?

I think we can do this by systemtap+kdump, can't we?

Thanks.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Demo/lkm] Kcoredump -- Do coredump in any where of kernel(not  same with kcore)
  2009-09-22  9:31 ` Américo Wang
@ 2009-09-22 14:18   ` Hui Zhu
  0 siblings, 0 replies; 3+ messages in thread
From: Hui Zhu @ 2009-09-22 14:18 UTC (permalink / raw)
  To: Américo Wang; +Cc: linux-kernel

On Tue, Sep 22, 2009 at 17:31, Américo Wang <xiyou.wangcong@gmail.com> wrote:
> On Tue, Sep 22, 2009 at 4:41 PM, Hui Zhu <teawater@gmail.com> wrote:
>> Hi guys,
>>
>> Kcoredump do a coredump in most part of kernel (it use kprobe, it can
>> be set most of part of kernel).  For example:
>> insmod kcoredump.ko name=do_fork offset=11
>> gdb ./vmlinux /proc/kcoredump
>> Core was generated by `'.
>
> But what is your point of generating coredump anywhere?
> I mean what do you really want by this?

We can get the kernel status in do_fork+11 and put it to core file (It
like gdb command "gcore").  GDB can parse it.

>
> I think we can do this by systemtap+kdump, can't we?
>
It seems that kdump just can get core when kernel crash.

Thanks,
Hui

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2009-09-22 14:18 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-22  8:41 [Demo/lkm] Kcoredump -- Do coredump in any where of kernel(not same with kcore) Hui Zhu
2009-09-22  9:31 ` Américo Wang
2009-09-22 14:18   ` Hui Zhu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.