| /** |
| * @file daemon/opd_kernel.c |
| * Dealing with the kernel and kernel module samples |
| * |
| * @remark Copyright 2002 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author John Levon |
| * @author Philippe Elie |
| * Modified by Aravind Menon for Xen |
| * These modifications are: |
| * Copyright (C) 2005 Hewlett-Packard Co. |
| */ |
| |
| #include "opd_kernel.h" |
| #include "opd_sfile.h" |
| #include "opd_trans.h" |
| #include "opd_printf.h" |
| #include "opd_stats.h" |
| #include "oprofiled.h" |
| |
| #include "op_fileio.h" |
| #include "op_config.h" |
| #include "op_libiberty.h" |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <assert.h> |
| |
| static LIST_HEAD(modules); |
| |
| static struct kernel_image vmlinux_image; |
| |
| static struct kernel_image xen_image; |
| |
| void opd_create_vmlinux(char const * name, char const * arg) |
| { |
| /* vmlinux is *not* on the list of modules */ |
| list_init(&vmlinux_image.list); |
| |
| /* for no vmlinux */ |
| if (no_vmlinux) { |
| vmlinux_image.name = "no-vmlinux"; |
| return; |
| } |
| |
| vmlinux_image.name = xstrdup(name); |
| |
| sscanf(arg, "%llx,%llx", &vmlinux_image.start, &vmlinux_image.end); |
| |
| verbprintf(vmisc, "kernel_start = %llx, kernel_end = %llx\n", |
| vmlinux_image.start, vmlinux_image.end); |
| |
| if (!vmlinux_image.start && !vmlinux_image.end) { |
| fprintf(stderr, "error: mis-parsed kernel range: %llx-%llx\n", |
| vmlinux_image.start, vmlinux_image.end); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| void opd_create_xen(char const * name, char const * arg) |
| { |
| /* xen is *not* on the list of modules */ |
| list_init(&xen_image.list); |
| |
| /* for no xen */ |
| if (no_xen) { |
| xen_image.name = "no-xen"; |
| return; |
| } |
| |
| xen_image.name = xstrdup(name); |
| |
| sscanf(arg, "%llx,%llx", &xen_image.start, &xen_image.end); |
| |
| verbprintf(vmisc, "xen_start = %llx, xen_end = %llx\n", |
| xen_image.start, xen_image.end); |
| |
| if (!xen_image.start && !xen_image.end) { |
| fprintf(stderr, "error: mis-parsed xen range: %llx-%llx\n", |
| xen_image.start, xen_image.end); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| |
| /** |
| * Allocate and initialise a kernel image description |
| * @param name image name |
| * @param start start address |
| * @param end end address |
| */ |
| static struct kernel_image * |
| opd_create_module(char const * name, vma_t start, vma_t end) |
| { |
| struct kernel_image * image = xmalloc(sizeof(struct kernel_image)); |
| |
| image->name = xstrdup(name); |
| image->start = start; |
| image->end = end; |
| list_add(&image->list, &modules); |
| |
| return image; |
| } |
| |
| |
| /** |
| * Clear and free all kernel image information and reset |
| * values. |
| */ |
| static void opd_clear_modules(void) |
| { |
| struct list_head * pos; |
| struct list_head * pos2; |
| struct kernel_image * image; |
| |
| list_for_each_safe(pos, pos2, &modules) { |
| image = list_entry(pos, struct kernel_image, list); |
| if (image->name) |
| free(image->name); |
| free(image); |
| } |
| |
| list_init(&modules); |
| |
| /* clear out lingering references */ |
| sfile_clear_kernel(); |
| } |
| |
| |
| /* |
| * each line is in the format: |
| * |
| * module_name 16480 1 dependencies Live 0xe091e000 |
| * |
| * without any blank space in each field |
| */ |
| void opd_reread_module_info(void) |
| { |
| FILE * fp; |
| char * line; |
| struct kernel_image * image; |
| int module_size; |
| char ref_count[32+1]; |
| int ret; |
| char module_name[256+1]; |
| char live_info[32+1]; |
| char dependencies[4096+1]; |
| unsigned long long start_address; |
| |
| if (no_vmlinux) |
| return; |
| |
| opd_clear_modules(); |
| |
| printf("Reading module info.\n"); |
| |
| fp = op_try_open_file("/proc/modules", "r"); |
| |
| if (!fp) { |
| printf("oprofiled: /proc/modules not readable, " |
| "can't process module samples.\n"); |
| return; |
| } |
| |
| while (1) { |
| line = op_get_line(fp); |
| |
| if (!line) |
| break; |
| |
| if (line[0] == '\0') { |
| free(line); |
| continue; |
| } |
| |
| ret = sscanf(line, "%256s %u %32s %4096s %32s %llx", |
| module_name, &module_size, ref_count, |
| dependencies, live_info, &start_address); |
| if (ret != 6) { |
| printf("bad /proc/modules entry: %s\n", line); |
| free(line); |
| continue; |
| } |
| |
| image = opd_create_module(module_name, start_address, |
| start_address + module_size); |
| |
| verbprintf(vmodule, "module %s start %llx end %llx\n", |
| image->name, image->start, image->end); |
| |
| free(line); |
| } |
| |
| op_close_file(fp); |
| } |
| |
| |
| /** |
| * find a kernel image by PC value |
| * @param trans holds PC value to look up |
| * |
| * find the kernel image which contains this PC. |
| * |
| * Return %NULL if not found. |
| */ |
| struct kernel_image * find_kernel_image(struct transient const * trans) |
| { |
| struct list_head * pos; |
| struct kernel_image * image = &vmlinux_image; |
| |
| if (no_vmlinux) |
| return image; |
| |
| if (image->start <= trans->pc && image->end > trans->pc) |
| return image; |
| |
| list_for_each(pos, &modules) { |
| image = list_entry(pos, struct kernel_image, list); |
| if (image->start <= trans->pc && image->end > trans->pc) |
| return image; |
| } |
| |
| if (xen_image.start <= trans->pc && xen_image.end > trans->pc) |
| return &xen_image; |
| |
| return NULL; |
| } |