Upstream | cc2ee17 | 1970-01-12 13:46:40 +0000 | [diff] [blame^] | 1 | /** |
| 2 | * @file daemon/liblegacy/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 | */ |
| 11 | |
| 12 | #include "opd_kernel.h" |
| 13 | #include "opd_proc.h" |
| 14 | #include "opd_image.h" |
| 15 | #include "opd_mapping.h" |
| 16 | #include "opd_printf.h" |
| 17 | #include "opd_24_stats.h" |
| 18 | #include "oprofiled.h" |
| 19 | |
| 20 | #include "op_fileio.h" |
| 21 | #include "op_config_24.h" |
| 22 | #include "op_libiberty.h" |
| 23 | |
| 24 | #include "p_module.h" |
| 25 | #include <string.h> |
| 26 | #include <stdlib.h> |
| 27 | #include <errno.h> |
| 28 | |
| 29 | /* kernel module */ |
| 30 | struct opd_module { |
| 31 | char * name; |
| 32 | struct opd_image * image; |
| 33 | unsigned long start; |
| 34 | unsigned long end; |
| 35 | struct list_head module_list; |
| 36 | }; |
| 37 | |
| 38 | static struct opd_image * kernel_image; |
| 39 | |
| 40 | /* kernel and module support */ |
| 41 | static unsigned long kernel_start; |
| 42 | static unsigned long kernel_end; |
| 43 | static struct list_head opd_modules = { &opd_modules, &opd_modules }; |
| 44 | static unsigned int nr_modules=0; |
| 45 | |
| 46 | void opd_init_kernel_image(void) |
| 47 | { |
| 48 | /* for no vmlinux */ |
| 49 | if (!vmlinux) |
| 50 | vmlinux = "no-vmlinux"; |
| 51 | kernel_image = opd_get_kernel_image(vmlinux, NULL, 0, 0); |
| 52 | kernel_image->ref_count++; |
| 53 | } |
| 54 | |
| 55 | |
| 56 | void opd_parse_kernel_range(char const * arg) |
| 57 | { |
| 58 | sscanf(arg, "%lx,%lx", &kernel_start, &kernel_end); |
| 59 | |
| 60 | verbprintf(vmisc, "OPD_PARSE_KERNEL_RANGE: kernel_start = %lx, kernel_end = %lx\n", |
| 61 | kernel_start, kernel_end); |
| 62 | |
| 63 | if (!kernel_start && !kernel_end) { |
| 64 | fprintf(stderr, |
| 65 | "Warning: mis-parsed kernel range: %lx-%lx\n", |
| 66 | kernel_start, kernel_end); |
| 67 | fprintf(stderr, "kernel profiles will be wrong.\n"); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | |
| 72 | /** |
| 73 | * opd_create_module - allocate and initialise a module description |
| 74 | * @param name module name |
| 75 | * @param start start address |
| 76 | * @param end end address |
| 77 | */ |
| 78 | static struct opd_module * |
| 79 | opd_create_module(char * name, unsigned long start, unsigned long end) |
| 80 | { |
| 81 | struct opd_module * module = xmalloc(sizeof(struct opd_module)); |
| 82 | |
| 83 | module->name = xstrdup(name); |
| 84 | module->image = NULL; |
| 85 | module->start = start; |
| 86 | module->end = end; |
| 87 | list_add(&module->module_list, &opd_modules); |
| 88 | |
| 89 | return module; |
| 90 | } |
| 91 | |
| 92 | |
| 93 | /** |
| 94 | * opd_find_module_by_name - find a module by name, ccreating a new once if |
| 95 | * search fail |
| 96 | * @param name module name |
| 97 | */ |
| 98 | static struct opd_module * opd_find_module_by_name(char * name) |
| 99 | { |
| 100 | struct list_head * pos; |
| 101 | struct opd_module * module; |
| 102 | |
| 103 | list_for_each(pos, &opd_modules) { |
| 104 | module = list_entry(pos, struct opd_module, module_list); |
| 105 | if (!strcmp(name, module->name)) |
| 106 | return module; |
| 107 | } |
| 108 | |
| 109 | return opd_create_module(name, 0, 0); |
| 110 | } |
| 111 | |
| 112 | |
| 113 | void opd_clear_module_info(void) |
| 114 | { |
| 115 | struct list_head * pos; |
| 116 | struct list_head * pos2; |
| 117 | struct opd_module * module; |
| 118 | |
| 119 | verbprintf(vmodule, "Removing module list\n"); |
| 120 | list_for_each_safe(pos, pos2, &opd_modules) { |
| 121 | module = list_entry(pos, struct opd_module, module_list); |
| 122 | free(module->name); |
| 123 | free(module); |
| 124 | } |
| 125 | |
| 126 | list_init(&opd_modules); |
| 127 | |
| 128 | opd_clear_kernel_mapping(); |
| 129 | } |
| 130 | |
| 131 | |
| 132 | /** |
| 133 | * opd_get_module_info - parse mapping information for kernel modules |
| 134 | * |
| 135 | * Parse the file /proc/ksyms to read in mapping information for |
| 136 | * all kernel modules. The modutils package adds special symbols |
| 137 | * to this file which allows determination of the module image |
| 138 | * and mapping addresses of the form : |
| 139 | * |
| 140 | * __insmod_modulename_Oobjectfile_Mmtime_Vversion |
| 141 | * __insmod_modulename_Ssectionname_Llength |
| 142 | * |
| 143 | * Currently the image file "objectfile" is stored, and details of |
| 144 | * ".text" sections. |
| 145 | * |
| 146 | * There is no query_module API that allow to get directly the pathname |
| 147 | * of a module so we need to parse all the /proc/ksyms. |
| 148 | */ |
| 149 | static void opd_get_module_info(void) |
| 150 | { |
| 151 | char * line; |
| 152 | char * cp, * cp2, * cp3; |
| 153 | FILE * fp; |
| 154 | struct opd_module * mod; |
| 155 | char * modname; |
| 156 | char * filename; |
| 157 | |
| 158 | nr_modules=0; |
| 159 | |
| 160 | fp = op_try_open_file("/proc/ksyms", "r"); |
| 161 | |
| 162 | if (!fp) { |
| 163 | printf("oprofiled: /proc/ksyms not readable, can't process module samples.\n"); |
| 164 | return; |
| 165 | } |
| 166 | |
| 167 | verbprintf(vmodule, "Read module info.\n"); |
| 168 | |
| 169 | while (1) { |
| 170 | line = op_get_line(fp); |
| 171 | |
| 172 | if (!line) |
| 173 | break; |
| 174 | |
| 175 | if (!strcmp("", line)) { |
| 176 | free(line); |
| 177 | continue; |
| 178 | } |
| 179 | |
| 180 | if (strlen(line) < 9) { |
| 181 | printf("oprofiled: corrupt /proc/ksyms line \"%s\"\n", line); |
| 182 | break; |
| 183 | } |
| 184 | |
| 185 | if (strncmp("__insmod_", line + 9, 9)) { |
| 186 | free(line); |
| 187 | continue; |
| 188 | } |
| 189 | |
| 190 | cp = line + 18; |
| 191 | cp2 = cp; |
| 192 | while ((*cp2) && !!strncmp("_S", cp2+1, 2) && !!strncmp("_O", cp2+1, 2)) |
| 193 | cp2++; |
| 194 | |
| 195 | if (!*cp2) { |
| 196 | printf("oprofiled: corrupt /proc/ksyms line \"%s\"\n", line); |
| 197 | break; |
| 198 | } |
| 199 | |
| 200 | cp2++; |
| 201 | |
| 202 | modname = xmalloc((size_t)((cp2-cp) + 1)); |
| 203 | strncpy(modname, cp, (size_t)((cp2-cp))); |
| 204 | modname[cp2-cp] = '\0'; |
| 205 | |
| 206 | mod = opd_find_module_by_name(modname); |
| 207 | |
| 208 | free(modname); |
| 209 | |
| 210 | switch (*(++cp2)) { |
| 211 | case 'O': |
| 212 | /* get filename */ |
| 213 | cp2++; |
| 214 | cp3 = cp2; |
| 215 | |
| 216 | while ((*cp3) && !!strncmp("_M", cp3+1, 2)) |
| 217 | cp3++; |
| 218 | |
| 219 | if (!*cp3) { |
| 220 | free(line); |
| 221 | continue; |
| 222 | } |
| 223 | |
| 224 | cp3++; |
| 225 | filename = xmalloc((size_t)(cp3 - cp2 + 1)); |
| 226 | strncpy(filename, cp2, (size_t)(cp3 - cp2)); |
| 227 | filename[cp3-cp2] = '\0'; |
| 228 | |
| 229 | mod->image = opd_get_kernel_image(filename, NULL, 0, 0); |
| 230 | mod->image->ref_count++; |
| 231 | free(filename); |
| 232 | break; |
| 233 | |
| 234 | case 'S': |
| 235 | /* get extent of .text section */ |
| 236 | cp2++; |
| 237 | if (strncmp(".text_L", cp2, 7)) { |
| 238 | free(line); |
| 239 | continue; |
| 240 | } |
| 241 | |
| 242 | cp2 += 7; |
| 243 | sscanf(line, "%lx", &mod->start); |
| 244 | sscanf(cp2, "%lu", &mod->end); |
| 245 | mod->end += mod->start; |
| 246 | break; |
| 247 | } |
| 248 | |
| 249 | free(line); |
| 250 | } |
| 251 | |
| 252 | if (line) |
| 253 | free(line); |
| 254 | op_close_file(fp); |
| 255 | } |
| 256 | |
| 257 | |
| 258 | /** |
| 259 | * opd_drop_module_sample - drop a module sample efficiently |
| 260 | * @param eip eip of sample |
| 261 | * |
| 262 | * This function is called to recover from failing to put a samples even |
| 263 | * after re-reading /proc/ksyms. It's either a rogue sample, or from a module |
| 264 | * that didn't create symbols (like in some initrd setups). So we check with |
| 265 | * query_module() if we can place it in a symbol-less module, and if so create |
| 266 | * a negative entry for it, to quickly ignore future samples. |
| 267 | * |
| 268 | * Problem uncovered by Bob Montgomery <bobm@fc.hp.com> |
| 269 | * |
| 270 | */ |
| 271 | static void opd_drop_module_sample(unsigned long eip) |
| 272 | { |
| 273 | char * module_names; |
| 274 | char * name; |
| 275 | size_t size = 1024; |
| 276 | size_t ret; |
| 277 | uint nr_mods; |
| 278 | uint mod = 0; |
| 279 | |
| 280 | opd_24_stats[OPD_LOST_MODULE]++; |
| 281 | |
| 282 | module_names = xmalloc(size); |
| 283 | while (query_module(NULL, QM_MODULES, module_names, size, &ret)) { |
| 284 | if (errno != ENOSPC) { |
| 285 | verbprintf(vmodule, "query_module failed: %s\n", strerror(errno)); |
| 286 | return; |
| 287 | } |
| 288 | size = ret; |
| 289 | module_names = xrealloc(module_names, size); |
| 290 | } |
| 291 | |
| 292 | nr_mods = ret; |
| 293 | name = module_names; |
| 294 | |
| 295 | while (mod < nr_mods) { |
| 296 | struct module_info info; |
| 297 | if (!query_module(name, QM_INFO, &info, sizeof(info), &ret)) { |
| 298 | if (eip >= info.addr && eip < info.addr + info.size) { |
| 299 | verbprintf(vmodule, "Sample from unprofilable module %s\n", name); |
| 300 | opd_create_module(name, info.addr, info.addr + info.size); |
| 301 | break; |
| 302 | } |
| 303 | } |
| 304 | mod++; |
| 305 | name += strlen(name) + 1; |
| 306 | } |
| 307 | |
| 308 | if (module_names) |
| 309 | free(module_names); |
| 310 | } |
| 311 | |
| 312 | |
| 313 | /** |
| 314 | * opd_find_module_by_eip - find a module by its eip |
| 315 | * @param eip EIP value |
| 316 | * |
| 317 | * find in the modules container the module which |
| 318 | * contain this eip return %NULL if not found. |
| 319 | * caller must check than the module image is valid |
| 320 | */ |
| 321 | static struct opd_module * opd_find_module_by_eip(unsigned long eip) |
| 322 | { |
| 323 | struct list_head * pos; |
| 324 | struct opd_module * module; |
| 325 | |
| 326 | list_for_each(pos, &opd_modules) { |
| 327 | module = list_entry(pos, struct opd_module, module_list); |
| 328 | if (module->start <= eip && module->end > eip) |
| 329 | return module; |
| 330 | } |
| 331 | |
| 332 | return NULL; |
| 333 | } |
| 334 | |
| 335 | |
| 336 | /** |
| 337 | * opd_handle_module_sample - process a module sample |
| 338 | * @param eip EIP value |
| 339 | * @param counter counter number |
| 340 | * |
| 341 | * Process a sample in module address space. The sample eip |
| 342 | * is matched against module information. If the search was |
| 343 | * successful, the sample is output to the relevant file. |
| 344 | * |
| 345 | * Note that for modules and the kernel, the offset will be |
| 346 | * wrong in the file, as it is not a file offset, but the offset |
| 347 | * from the text section. This is fixed up in pp. |
| 348 | * |
| 349 | * If the sample could not be located in a module, it is treated |
| 350 | * as a kernel sample. |
| 351 | */ |
| 352 | static void opd_handle_module_sample(unsigned long eip, u32 counter) |
| 353 | { |
| 354 | struct opd_module * module; |
| 355 | |
| 356 | module = opd_find_module_by_eip(eip); |
| 357 | if (!module) { |
| 358 | /* not found in known modules, re-read our info and retry */ |
| 359 | opd_clear_module_info(); |
| 360 | opd_get_module_info(); |
| 361 | |
| 362 | module = opd_find_module_by_eip(eip); |
| 363 | } |
| 364 | |
| 365 | if (module) { |
| 366 | if (module->image != NULL) { |
| 367 | opd_24_stats[OPD_MODULE]++; |
| 368 | opd_put_image_sample(module->image, |
| 369 | eip - module->start, counter); |
| 370 | } else { |
| 371 | opd_24_stats[OPD_LOST_MODULE]++; |
| 372 | verbprintf(vmodule, "No image for sampled module %s\n", |
| 373 | module->name); |
| 374 | } |
| 375 | } else { |
| 376 | opd_drop_module_sample(eip); |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | |
| 381 | void opd_handle_kernel_sample(unsigned long eip, u32 counter) |
| 382 | { |
| 383 | if (no_vmlinux || eip < kernel_end) { |
| 384 | opd_24_stats[OPD_KERNEL]++; |
| 385 | opd_put_image_sample(kernel_image, eip - kernel_start, counter); |
| 386 | return; |
| 387 | } |
| 388 | |
| 389 | /* in a module */ |
| 390 | opd_handle_module_sample(eip, counter); |
| 391 | } |
| 392 | |
| 393 | |
| 394 | int opd_eip_is_kernel(unsigned long eip) |
| 395 | { |
| 396 | #ifdef __i386 |
| 397 | #define KERNEL_OFFSET 0xC0000000 |
| 398 | /* |
| 399 | * kernel_start == 0 when using --no-vmlinux. |
| 400 | * This is wrong, wrong, wrong, wrong, but we don't have much |
| 401 | * choice. It obviously breaks for IA64. |
| 402 | */ |
| 403 | if (!kernel_start) |
| 404 | return eip >= KERNEL_OFFSET; |
| 405 | #endif |
| 406 | |
| 407 | return eip >= kernel_start; |
| 408 | } |
| 409 | |
| 410 | |
| 411 | void opd_add_kernel_map(struct opd_proc * proc, unsigned long eip) |
| 412 | { |
| 413 | struct opd_module * module; |
| 414 | struct opd_image * image; |
| 415 | char const * app_name; |
| 416 | |
| 417 | app_name = proc->name; |
| 418 | if (!app_name) { |
| 419 | verbprintf(vmisc, "un-named proc for tid %d\n", proc->tid); |
| 420 | return; |
| 421 | } |
| 422 | |
| 423 | |
| 424 | if (eip < kernel_end) { |
| 425 | image = opd_get_kernel_image(vmlinux, app_name, proc->tid, proc->tgid); |
| 426 | if (!image) { |
| 427 | verbprintf(vmisc, "Can't create image for %s %s\n", vmlinux, app_name); |
| 428 | return; |
| 429 | } |
| 430 | |
| 431 | opd_add_mapping(proc, image, kernel_start, 0, kernel_end); |
| 432 | return; |
| 433 | } |
| 434 | |
| 435 | module = opd_find_module_by_eip(eip); |
| 436 | if (!module) { |
| 437 | /* not found in known modules, re-read our info and retry */ |
| 438 | opd_clear_module_info(); |
| 439 | opd_get_module_info(); |
| 440 | |
| 441 | module = opd_find_module_by_eip(eip); |
| 442 | } |
| 443 | |
| 444 | if (module) { |
| 445 | /* module->name is only the module name not the full path */ |
| 446 | char const * module_name = 0; |
| 447 | if (module->image) |
| 448 | module_name = module->image->name; |
| 449 | if (!module_name) { |
| 450 | verbprintf(vmodule, "unable to get path name for module %s\n", |
| 451 | module->name); |
| 452 | module_name = module->name; |
| 453 | } |
| 454 | image = opd_get_kernel_image(module_name, app_name, proc->tid, proc->tgid); |
| 455 | if (!image) { |
| 456 | verbprintf(vmodule, "Can't create image for %s %s\n", |
| 457 | module->name, app_name); |
| 458 | return; |
| 459 | } |
| 460 | opd_add_mapping(proc, image, module->start, 0, module->end); |
| 461 | } else { |
| 462 | opd_drop_module_sample(eip); |
| 463 | } |
| 464 | } |