• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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) */
opd_add_ascii_map(struct opd_proc * proc,char const * line,char * const image_name)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  */
opd_get_ascii_maps(struct opd_proc * proc)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 
read_tgid(u32 tid)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 
opd_get_ascii_procs(void)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