opd_parse_proc.c revision cc2ee177dbb3befca43e36cfc56778b006c3d050
1/** 2 * @file opd_parse_proc.c 3 * Parsing of /proc/#pid 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 "op_libiberty.h" 13 14#include "opd_parse_proc.h" 15#include "opd_proc.h" 16#include "opd_mapping.h" 17#include "opd_image.h" 18#include "opd_printf.h" 19 20#include "op_file.h" 21#include "op_fileio.h" 22 23#include <dirent.h> 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27 28/** 29 * opd_add_ascii_map - parse an ASCII map string for a process 30 * @param proc process to add map to 31 * @param line 0-terminated ASCII string 32 * @param image_name the binary application name 33 * 34 * Attempt to parse the string @line for map information 35 * and add the info to the process @proc. Returns %1 36 * on success, %0 otherwise. 37 * 38 * The parsing is based on Linux 2.4 format, which looks like this : 39 * 40 * 4001e000-400fc000 r-xp 00000000 03:04 31011 /lib/libc-2.1.2.so 41 */ 42/* FIXME: handle (deleted) */ 43static int opd_add_ascii_map(struct opd_proc * proc, char const * line, 44 char * const image_name) 45{ 46 unsigned long offset, start, end; 47 struct opd_image * image; 48 char const * cp = line; 49 50 /* skip to protection field */ 51 while (*cp && *cp != ' ') 52 cp++; 53 54 /* handle rwx */ 55 if (!*cp || (!*(++cp)) || (!*(++cp)) || (*(++cp) != 'x')) 56 return 0; 57 58 /* get start and end from "40000000-4001f000" */ 59 if (sscanf(line, "%lx-%lx", &start, &end) != 2) 60 return 0; 61 62 /* "p " */ 63 cp += 2; 64 65 /* read offset */ 66 if (sscanf(cp, "%lx", &offset) != 1) 67 return 0; 68 69 while (*cp && *cp != '/') 70 cp++; 71 72 if (!*cp) 73 return 0; 74 75 image = opd_get_image(cp, image_name, 0, proc->tid, proc->tgid); 76 if (!image) 77 return 0; 78 79 opd_add_mapping(proc, image, start, offset, end); 80 81 return 1; 82} 83 84 85/** 86 * opd_get_ascii_maps - read all maps for a process 87 * @param proc process to work on 88 * 89 * Read the /proc/<pid>/maps file and add all 90 * mapping information found to the process @proc. 91 */ 92static void opd_get_ascii_maps(struct opd_proc * proc) 93{ 94 FILE * fp; 95 char mapsfile[20] = "/proc/"; 96 char * line; 97 char exe_name[20]; 98 char * image_name; 99 struct list_head * pos; 100 101 snprintf(mapsfile + 6, 6, "%hu", proc->tid); 102 103 strcpy(exe_name, mapsfile); 104 105 strcat(mapsfile, "/maps"); 106 107 fp = op_try_open_file(mapsfile, "r"); 108 if (!fp) 109 return; 110 111 strcat(exe_name, "/exe"); 112 image_name = xmalloc(PATH_MAX); 113 if (!realpath(exe_name, image_name)) 114 /* kernel thread are invalid symlink */ 115 strcpy(image_name, exe_name); 116 117 verbprintf(vmisc, "image name %s for pid %u %u\n", image_name, proc->tid, proc->tgid); 118 119 while (1) { 120 line = op_get_line(fp); 121 if (!line) 122 break; 123 124 opd_add_ascii_map(proc, line, image_name); 125 free(line); 126 } 127 128 /* dae assume than the first map added is the primary image name, this 129 * is always true at exec time but not for /proc/pid so restore 130 * the primary image name 131 */ 132 list_for_each(pos, &proc->maps) { 133 struct opd_map * map = list_entry(pos, struct opd_map, next); 134 if (!strcmp(map->image->name, image_name)) { 135 if (pos != proc->maps.next) { 136 fprintf(stderr, "swap map for image %s from %s to %s\n", image_name, proc->name, map->image->name); 137 free((char *)proc->name); 138 proc->name = xstrdup(map->image->name); 139 } 140 break; 141 } 142 } 143 144 if (list_empty(&proc->maps)) { 145 /* we always need a valid proc->maps[0], we artificially give 146 * a map of length zero so on no samples will never go to this 147 * map. This is used only with --separate-samples and kernel 148 * thread when adding vmlinux and module maps to proc->maps[] 149 */ 150 /* FIXME: use the first field of /proc/pid/status as proc name 151 * for now we use /proc/%pid/exe as name */ 152 struct opd_image * image = opd_get_image(image_name, 153 image_name, 0, proc->tid, proc->tgid); 154 if (image) { 155 opd_add_mapping(proc, image, 0, 0, 0); 156 } 157 } 158 159 if (image_name) 160 free(image_name); 161 162 op_close_file(fp); 163} 164 165 166static u32 read_tgid(u32 tid) 167{ 168 char status_file[30] = "/proc/"; 169 char * line; 170 FILE * fp; 171 u32 tgid; 172 173 snprintf(status_file + 6, 6, "%hu", tid); 174 175 strcat(status_file, "/status"); 176 177 fp = op_try_open_file(status_file, "r"); 178 if (!fp) 179 return 0; 180 181 while (1) { 182 line = op_get_line(fp); 183 if (!line) 184 break; 185 186 if (sscanf(line, "Tgid: %u", &tgid) == 1) { 187 free(line); 188 op_close_file(fp); 189 return tgid; 190 } 191 free(line); 192 } 193 194 op_close_file(fp); 195 196 return 0; 197} 198 199 200void opd_get_ascii_procs(void) 201{ 202 DIR * dir; 203 struct dirent * dirent; 204 struct opd_proc * proc; 205 u32 pid; 206 207 if (!(dir = opendir("/proc"))) { 208 perror("oprofiled: /proc directory could not be opened. "); 209 exit(EXIT_FAILURE); 210 } 211 212 while ((dirent = readdir(dir))) { 213 if (sscanf(dirent->d_name, "%u", &pid) == 1) { 214 u32 tgid = read_tgid(pid); 215 verbprintf(vmisc, "ASCII added %u %u\n", pid, tgid); 216 proc = opd_get_proc(pid, tgid); 217 if (!proc) 218 proc = opd_new_proc(pid, tgid); 219 opd_get_ascii_maps(proc); 220 } 221 } 222 223 closedir(dir); 224} 225