The Android Open Source Project | 10e23ee | 2009-03-03 19:30:30 -0800 | [diff] [blame^] | 1 | /** |
| 2 | * @file daemon/opd_kernel.c |
| 3 | * Dealing with the kernel and kernel module samples |
| 4 | * |
| 5 | * @remark Copyright 2002 OProfile authors |
| 6 | * @remark Read the file COPYING |
| 7 | * |
| 8 | * @author John Levon |
| 9 | * @author Philippe Elie |
| 10 | * Modified by Aravind Menon for Xen |
| 11 | * These modifications are: |
| 12 | * Copyright (C) 2005 Hewlett-Packard Co. |
| 13 | */ |
| 14 | |
| 15 | #include "opd_kernel.h" |
| 16 | #include "opd_sfile.h" |
| 17 | #include "opd_trans.h" |
| 18 | #include "opd_printf.h" |
| 19 | #include "opd_stats.h" |
| 20 | #include "oprofiled.h" |
| 21 | |
| 22 | #include "op_fileio.h" |
| 23 | #include "op_config.h" |
| 24 | #include "op_libiberty.h" |
| 25 | |
| 26 | #include <string.h> |
| 27 | #include <stdlib.h> |
| 28 | #include <errno.h> |
| 29 | #include <assert.h> |
| 30 | |
| 31 | static LIST_HEAD(modules); |
| 32 | |
| 33 | static struct kernel_image vmlinux_image; |
| 34 | |
| 35 | static struct kernel_image xen_image; |
| 36 | |
| 37 | void opd_create_vmlinux(char const * name, char const * arg) |
| 38 | { |
| 39 | /* vmlinux is *not* on the list of modules */ |
| 40 | list_init(&vmlinux_image.list); |
| 41 | |
| 42 | /* for no vmlinux */ |
| 43 | if (no_vmlinux) { |
| 44 | vmlinux_image.name = "no-vmlinux"; |
| 45 | return; |
| 46 | } |
| 47 | |
| 48 | vmlinux_image.name = xstrdup(name); |
| 49 | |
| 50 | sscanf(arg, "%llx,%llx", &vmlinux_image.start, &vmlinux_image.end); |
| 51 | |
| 52 | verbprintf(vmisc, "kernel_start = %llx, kernel_end = %llx\n", |
| 53 | vmlinux_image.start, vmlinux_image.end); |
| 54 | |
| 55 | if (!vmlinux_image.start && !vmlinux_image.end) { |
| 56 | fprintf(stderr, "error: mis-parsed kernel range: %llx-%llx\n", |
| 57 | vmlinux_image.start, vmlinux_image.end); |
| 58 | exit(EXIT_FAILURE); |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | void opd_create_xen(char const * name, char const * arg) |
| 63 | { |
| 64 | /* xen is *not* on the list of modules */ |
| 65 | list_init(&xen_image.list); |
| 66 | |
| 67 | /* for no xen */ |
| 68 | if (no_xen) { |
| 69 | xen_image.name = "no-xen"; |
| 70 | return; |
| 71 | } |
| 72 | |
| 73 | xen_image.name = xstrdup(name); |
| 74 | |
| 75 | sscanf(arg, "%llx,%llx", &xen_image.start, &xen_image.end); |
| 76 | |
| 77 | verbprintf(vmisc, "xen_start = %llx, xen_end = %llx\n", |
| 78 | xen_image.start, xen_image.end); |
| 79 | |
| 80 | if (!xen_image.start && !xen_image.end) { |
| 81 | fprintf(stderr, "error: mis-parsed xen range: %llx-%llx\n", |
| 82 | xen_image.start, xen_image.end); |
| 83 | exit(EXIT_FAILURE); |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | |
| 88 | /** |
| 89 | * Allocate and initialise a kernel image description |
| 90 | * @param name image name |
| 91 | * @param start start address |
| 92 | * @param end end address |
| 93 | */ |
| 94 | static struct kernel_image * |
| 95 | opd_create_module(char const * name, vma_t start, vma_t end) |
| 96 | { |
| 97 | struct kernel_image * image = xmalloc(sizeof(struct kernel_image)); |
| 98 | |
| 99 | image->name = xstrdup(name); |
| 100 | image->start = start; |
| 101 | image->end = end; |
| 102 | list_add(&image->list, &modules); |
| 103 | |
| 104 | return image; |
| 105 | } |
| 106 | |
| 107 | |
| 108 | /** |
| 109 | * Clear and free all kernel image information and reset |
| 110 | * values. |
| 111 | */ |
| 112 | static void opd_clear_modules(void) |
| 113 | { |
| 114 | struct list_head * pos; |
| 115 | struct list_head * pos2; |
| 116 | struct kernel_image * image; |
| 117 | |
| 118 | list_for_each_safe(pos, pos2, &modules) { |
| 119 | image = list_entry(pos, struct kernel_image, list); |
| 120 | if (image->name) |
| 121 | free(image->name); |
| 122 | free(image); |
| 123 | } |
| 124 | |
| 125 | list_init(&modules); |
| 126 | |
| 127 | /* clear out lingering references */ |
| 128 | sfile_clear_kernel(); |
| 129 | } |
| 130 | |
| 131 | |
| 132 | /* |
| 133 | * each line is in the format: |
| 134 | * |
| 135 | * module_name 16480 1 dependencies Live 0xe091e000 |
| 136 | * |
| 137 | * without any blank space in each field |
| 138 | */ |
| 139 | void opd_reread_module_info(void) |
| 140 | { |
| 141 | FILE * fp; |
| 142 | char * line; |
| 143 | struct kernel_image * image; |
| 144 | int module_size; |
| 145 | char ref_count[32+1]; |
| 146 | int ret; |
| 147 | char module_name[256+1]; |
| 148 | char live_info[32+1]; |
| 149 | char dependencies[4096+1]; |
| 150 | unsigned long long start_address; |
| 151 | |
| 152 | if (no_vmlinux) |
| 153 | return; |
| 154 | |
| 155 | opd_clear_modules(); |
| 156 | |
| 157 | printf("Reading module info.\n"); |
| 158 | |
| 159 | fp = op_try_open_file("/proc/modules", "r"); |
| 160 | |
| 161 | if (!fp) { |
| 162 | printf("oprofiled: /proc/modules not readable, " |
| 163 | "can't process module samples.\n"); |
| 164 | return; |
| 165 | } |
| 166 | |
| 167 | while (1) { |
| 168 | line = op_get_line(fp); |
| 169 | |
| 170 | if (!line) |
| 171 | break; |
| 172 | |
| 173 | if (line[0] == '\0') { |
| 174 | free(line); |
| 175 | continue; |
| 176 | } |
| 177 | |
| 178 | ret = sscanf(line, "%256s %u %32s %4096s %32s %llx", |
| 179 | module_name, &module_size, ref_count, |
| 180 | dependencies, live_info, &start_address); |
| 181 | if (ret != 6) { |
| 182 | printf("bad /proc/modules entry: %s\n", line); |
| 183 | free(line); |
| 184 | continue; |
| 185 | } |
| 186 | |
| 187 | image = opd_create_module(module_name, start_address, |
| 188 | start_address + module_size); |
| 189 | |
| 190 | verbprintf(vmodule, "module %s start %llx end %llx\n", |
| 191 | image->name, image->start, image->end); |
| 192 | |
| 193 | free(line); |
| 194 | } |
| 195 | |
| 196 | op_close_file(fp); |
| 197 | } |
| 198 | |
| 199 | |
| 200 | /** |
| 201 | * find a kernel image by PC value |
| 202 | * @param trans holds PC value to look up |
| 203 | * |
| 204 | * find the kernel image which contains this PC. |
| 205 | * |
| 206 | * Return %NULL if not found. |
| 207 | */ |
| 208 | struct kernel_image * find_kernel_image(struct transient const * trans) |
| 209 | { |
| 210 | struct list_head * pos; |
| 211 | struct kernel_image * image = &vmlinux_image; |
| 212 | |
| 213 | if (no_vmlinux) |
| 214 | return image; |
| 215 | |
| 216 | if (image->start <= trans->pc && image->end > trans->pc) |
| 217 | return image; |
| 218 | |
| 219 | list_for_each(pos, &modules) { |
| 220 | image = list_entry(pos, struct kernel_image, list); |
| 221 | if (image->start <= trans->pc && image->end > trans->pc) |
| 222 | return image; |
| 223 | } |
| 224 | |
| 225 | if (xen_image.start <= trans->pc && xen_image.end > trans->pc) |
| 226 | return &xen_image; |
| 227 | |
| 228 | return NULL; |
| 229 | } |