Home | History | Annotate | Download | only in liblegacy
      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) */
     43 static 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  */
     92 static 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 	if (image_name)
    159 		free(image_name);
    160 
    161 	op_close_file(fp);
    162 }
    163 
    164 
    165 static u32 read_tgid(u32 tid)
    166 {
    167 	char status_file[30] = "/proc/";
    168 	char * line;
    169 	FILE * fp;
    170 	u32 tgid;
    171 
    172 	snprintf(status_file + 6, 6, "%hu", tid);
    173 
    174 	strcat(status_file, "/status");
    175 
    176 	fp = op_try_open_file(status_file, "r");
    177 	if (!fp)
    178 		return 0;
    179 
    180 	while (1) {
    181 		line = op_get_line(fp);
    182 		if (!line)
    183 			break;
    184 
    185 		if (sscanf(line, "Tgid: %u", &tgid) == 1) {
    186 			free(line);
    187 			op_close_file(fp);
    188 			return tgid;
    189 		}
    190 		free(line);
    191 	}
    192 
    193 	op_close_file(fp);
    194 
    195 	return 0;
    196 }
    197 
    198 
    199 void opd_get_ascii_procs(void)
    200 {
    201 	DIR * dir;
    202 	struct dirent * dirent;
    203 	struct opd_proc * proc;
    204 	u32 pid;
    205 
    206 	if (!(dir = opendir("/proc"))) {
    207 		perror("oprofiled: /proc directory could not be opened. ");
    208 		exit(EXIT_FAILURE);
    209 	}
    210 
    211 	while ((dirent = readdir(dir))) {
    212 		if (sscanf(dirent->d_name, "%u", &pid) == 1) {
    213 			u32 tgid = read_tgid(pid);
    214 			verbprintf(vmisc, "ASCII added %u %u\n", pid, tgid);
    215 			proc = opd_get_proc(pid, tgid);
    216 			if (!proc)
    217 				proc = opd_new_proc(pid, tgid);
    218 			opd_get_ascii_maps(proc);
    219 		}
    220 	}
    221 
    222 	closedir(dir);
    223 }
    224