Crash工具笔记 (1)—— “current context”

成功启动crash会话后,会有一个task被指定为current context。因为有一些命令是context-sensitive,也即这些命令的运行会依赖于current context,所以知道当前的current context就很重要。

选择current context的标准:
a)coredump文件:

The task that was running when die() was called.
The task that was running when panic() was called.
The task that was running when an ALT-SYSRQ-c keyboard interrupt was received.
The task that was running when the character "c" was echoed to /proc/sysrq-trigger. 

b)当前运行的系统:

`crash`命令本身.

执行set命令显示当前current context

crash> set
    PID: 2366
COMMAND: "crash"
   TASK: ffff88001ae60000  [THREAD_INFO: ffff88001c1f0000]
    CPU: 0
  STATE: TASK_RUNNING (ACTIVE)

也可利用set命令改变当前current context

crash> set 1
    PID: 1
COMMAND: "systemd"
   TASK: ffff88001dfd8000  [THREAD_INFO: ffff88001dfe0000]
    CPU: 0
  STATE: TASK_INTERRUPTIBLE

 

/dev/mem,/dev/kmem和/dev/port

/dev/mem/dev/kmem/dev/port这三个文件分别代表物理内存,kernel虚拟内存和I/O端口。参考下面:

/dev/mem is a character device file that is an image of the main memory of the computer. It may be used, for example, to examine (and even patch) the system. Byte addresses in /dev/mem are interpreted as physical memory addresses. References to nonexistent locations cause errors to be returned.

The file /dev/kmem is the same as /dev/mem, except that the kernel virtual memory rather than physical memory is accessed.

/dev/port is similar to /dev/mem, but the I/O ports are accessed.

 

SystemTap 笔记 (8)—— typecasting

当指针是一个void *类型,或是保存为整数后,可以使用cast运算符指定指针的数据类型:

@cast(p, "type_name"[, "module"])->member

可选的module参数用来指定从哪里得到type_name(The optional module tells the translator where to look for information about that type. Multiple modules may be specified as a list with : separators. If the module is not specified, it will default either to the probe module for dwarf probes, or to “kernel” for functions and all other probes types.)。

另外,translator可以从头文件中创建type信息:

The translator can create its own module with type information from a header surrounded by angle brackets, in case normal debuginfo is not available. For kernel headers, prefix it with “kernel” to use the appropriate build system. All other headers are build with default GCC parameters into a user module. Multiple headers may be specified in sequence to resolve a codependency.

@cast(tv, “timeval”, “<sys/time.h>”)->tvsec
@cast(task, “task
struct”, “kernel<linux/sched.h>”)->tgid
@cast(task, “taskstruct”, “kernel<linux/sched.h><linux/fsstruct.h>”)->fs->umask

参考例子:

# stap -e 'probe kernel.function("do_dentry_open") {printf("%d\n", $f->f_flags); exit(); }'
32768

使用cast运算符:

# stap -e 'probe kernel.function("do_dentry_open") {printf("%d\n", @cast($f, "file", "kernel<linux/fs.h>" )->f_flags); exit(); }'
32768

SystemTap 笔记 (7)—— target variable (2)

SystemTap可以为target variable生成一系列可打印的字符串:

$$vars

Expands to a character string that is equivalent to sprintf(“parm1=%x … parmN=%x var1=%x … varN=%x”, parm1, …, parmN, var1, …, varN) for each variable in scope at the probe point. Some values may be printed as “=?” if their run-time location cannot be found.

$$locals

Expands to a subset of $$vars containing only the local variables.

$$parms

Expands to a subset of $$vars containing only the function parameters.

$$return

Is available in return probes only. It expands to a string that is equivalent to sprintf(“return=%x”, $return) if the probed function has a return value, or else an empty string.

参考下面例子:

# stap -e 'probe kernel.function("do_dentry_open") {printf("%s\n", $$vars); exit(); }'
f=0xffff880022ec6080 open=0x0 cred=0xffff880030d483c0 empty_fops={...} inode=? error=?
# stap -e 'probe kernel.function("do_dentry_open") {printf("%s\n", $$parms); exit(); }'
f=0xffff880030d453c0 open=0x0 cred=0xffff880030d483c0
# stap -e 'probe kernel.function("do_dentry_open") {printf("%s\n", $$locals); exit(); }'
empty_fops={...} inode=? error=?

在上述变量后加上$$$可以打印更详细的结构体信息。参考下例:

# stap -e 'probe kernel.function("do_dentry_open") {printf("%s\n", $$parms$); exit(); }'
f={.f_u={...}, .f_path={...}, .f_inode=0x0, .f_op=0x0, .f_lock={...}, .f_count={...}, .f_flags=32768, .f_mode=0, .f_pos=0, .f_owner={...}, .f_cred=0xffff880030d483c0, .f_ra={...}, .f_version=0, .f_security=0xffff880012982b80, .private_data=0x0, .f_ep_links={...}, .f_tfile_llink={...}, .f_mapping=0x0} open=<function>:0x0 cred={.usage={...}, .uid={...}, .gid={...}, .suid={...}, .sgid={...}, .euid={...}, .egid={...}, .fsuid={...}, .fsgid={...}, .securebits=0, .cap_inheritable={...}, .cap_permitted={...}, .cap
# stap -e 'probe kernel.function("do_dentry_open") {printf("%s\n", $$parms$$); exit(); }'
f={.f_u={.fu_llist={.next=0x0}, .fu_rcuhead={.next=0x0, .func=0x0}}, .f_path={.mnt=0xffff8800337510e0, .dentry=0xffff8800318b82d8}, .f_inode=0x0, .f_op=0x0, .f_lock={<union>={.rlock={.raw_lock={.head_tail=0, <class>={.tickets={.head=0, .tail=0}, .owner=0}}}}}, .f_count={.counter=1}, .f_flags=32768, .f_mode=0, .f_pos=0, .f_owner={.lock={.raw_lock={.lock=1048576, .write=1048576}}, .pid=0x0, .pid_type=0, .uid={.val=0}, .euid={.val=0}, .signum=0}, .f_cred=0xffff880030d89300, .f_ra={.start=0, .size=0, .async_si

 

Linux kernel 笔记 (44)——使用字符设备

Linux kernel 使用 cdev结构体代表字符设备(char device),定义在<linux/cdev.h>

#include <linux/kobject.h>
#include <linux/kdev_t.h>
#include <linux/list.h>

struct file_operations;
struct inode;
struct module;

struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

void cdev_init(struct cdev *, const struct file_operations *);

struct cdev *cdev_alloc(void);

void cdev_put(struct cdev *p);

int cdev_add(struct cdev *, dev_t, unsigned);

void cdev_del(struct cdev *);

void cd_forget(struct inode *);

分配和初始化cdev结构体的两种方式:

(1)

struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_fops;
my_cdev->owner = THIS_MODULE;

(2)另外一种是cdev嵌入到代表设备的结构体中:

struct scull_dev {
    ......
    struct cdev cdev; /* Char device structure */
    ......
};

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
    int err, devno = MKDEV(scull_major, scull_minor + index);
    cdev_init(&dev->cdev, &scull_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &scull_fops;
    ......
}

两种方式都要注意把owner赋值为THIS_MODULE

初始化cdev结构体以后,要使用cdev_add把设备加入系统:

/**
 * cdev_add() - add a char device to the system
 * @p: the cdev structure for the device
 * @dev: the first device number for which this device is responsible
 * @count: the number of consecutive minor numbers corresponding to this
 *         device
 *
 * cdev_add() adds the device represented by @p to the system, making it
 * live immediately.  A negative error code is returned on failure.
 */
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    int error;

    p->dev = dev;
    p->count = count;

    error = kobj_map(cdev_map, dev, count, NULL,
             exact_match, exact_lock, p);
    if (error)
        return error;

    kobject_get(p->kobj.parent);

    return 0;
}

要注意count指定的是连续的minor number数。

删除设备使用cdev_del函数:

/**
 * cdev_del() - remove a cdev from the system
 * @p: the cdev structure to be removed
 *
 * cdev_del() removes @p from the system, possibly freeing the structure
 * itself.
 */
void cdev_del(struct cdev *p)
{
    cdev_unmap(p->dev, p->count);
    kobject_put(&p->kobj);
}

2.6版本之前的注册和删除设备的register_chrdevunregister_chrdev函数已经过时,不再使用。