/** * @file opd_parse_proc.c * Parsing of /proc/#pid * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author John Levon * @author Philippe Elie */ #include "op_libiberty.h" #include "opd_parse_proc.h" #include "opd_proc.h" #include "opd_mapping.h" #include "opd_image.h" #include "opd_printf.h" #include "op_file.h" #include "op_fileio.h" #include <dirent.h> #include <stdio.h> #include <stdlib.h> #include <string.h> /** * opd_add_ascii_map - parse an ASCII map string for a process * @param proc process to add map to * @param line 0-terminated ASCII string * @param image_name the binary application name * * Attempt to parse the string @line for map information * and add the info to the process @proc. Returns %1 * on success, %0 otherwise. * * The parsing is based on Linux 2.4 format, which looks like this : * * 4001e000-400fc000 r-xp 00000000 03:04 31011 /lib/libc-2.1.2.so */ /* FIXME: handle (deleted) */ static int opd_add_ascii_map(struct opd_proc * proc, char const * line, char * const image_name) { unsigned long offset, start, end; struct opd_image * image; char const * cp = line; /* skip to protection field */ while (*cp && *cp != ' ') cp++; /* handle rwx */ if (!*cp || (!*(++cp)) || (!*(++cp)) || (*(++cp) != 'x')) return 0; /* get start and end from "40000000-4001f000" */ if (sscanf(line, "%lx-%lx", &start, &end) != 2) return 0; /* "p " */ cp += 2; /* read offset */ if (sscanf(cp, "%lx", &offset) != 1) return 0; while (*cp && *cp != '/') cp++; if (!*cp) return 0; image = opd_get_image(cp, image_name, 0, proc->tid, proc->tgid); if (!image) return 0; opd_add_mapping(proc, image, start, offset, end); return 1; } /** * opd_get_ascii_maps - read all maps for a process * @param proc process to work on * * Read the /proc/<pid>/maps file and add all * mapping information found to the process @proc. */ static void opd_get_ascii_maps(struct opd_proc * proc) { FILE * fp; char mapsfile[20] = "/proc/"; char * line; char exe_name[20]; char * image_name; struct list_head * pos; snprintf(mapsfile + 6, 6, "%hu", proc->tid); strcpy(exe_name, mapsfile); strcat(mapsfile, "/maps"); fp = op_try_open_file(mapsfile, "r"); if (!fp) return; strcat(exe_name, "/exe"); image_name = xmalloc(PATH_MAX); if (!realpath(exe_name, image_name)) /* kernel thread are invalid symlink */ strcpy(image_name, exe_name); verbprintf(vmisc, "image name %s for pid %u %u\n", image_name, proc->tid, proc->tgid); while (1) { line = op_get_line(fp); if (!line) break; opd_add_ascii_map(proc, line, image_name); free(line); } /* dae assume than the first map added is the primary image name, this * is always true at exec time but not for /proc/pid so restore * the primary image name */ list_for_each(pos, &proc->maps) { struct opd_map * map = list_entry(pos, struct opd_map, next); if (!strcmp(map->image->name, image_name)) { if (pos != proc->maps.next) { fprintf(stderr, "swap map for image %s from %s to %s\n", image_name, proc->name, map->image->name); free((char *)proc->name); proc->name = xstrdup(map->image->name); } break; } } if (list_empty(&proc->maps)) { /* we always need a valid proc->maps[0], we artificially give * a map of length zero so on no samples will never go to this * map. This is used only with --separate-samples and kernel * thread when adding vmlinux and module maps to proc->maps[] */ /* FIXME: use the first field of /proc/pid/status as proc name * for now we use /proc/%pid/exe as name */ struct opd_image * image = opd_get_image(image_name, image_name, 0, proc->tid, proc->tgid); if (image) opd_add_mapping(proc, image, 0, 0, 0); } if (image_name) free(image_name); op_close_file(fp); } static u32 read_tgid(u32 tid) { char status_file[30] = "/proc/"; char * line; FILE * fp; u32 tgid; snprintf(status_file + 6, 6, "%hu", tid); strcat(status_file, "/status"); fp = op_try_open_file(status_file, "r"); if (!fp) return 0; while (1) { line = op_get_line(fp); if (!line) break; if (sscanf(line, "Tgid: %u", &tgid) == 1) { free(line); op_close_file(fp); return tgid; } free(line); } op_close_file(fp); return 0; } void opd_get_ascii_procs(void) { DIR * dir; struct dirent * dirent; struct opd_proc * proc; u32 pid; if (!(dir = opendir("/proc"))) { perror("oprofiled: /proc directory could not be opened. "); exit(EXIT_FAILURE); } while ((dirent = readdir(dir))) { if (sscanf(dirent->d_name, "%u", &pid) == 1) { u32 tgid = read_tgid(pid); verbprintf(vmisc, "ASCII added %u %u\n", pid, tgid); proc = opd_get_proc(pid, tgid); if (!proc) proc = opd_new_proc(pid, tgid); opd_get_ascii_maps(proc); } } closedir(dir); }