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