• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file daemon/oprofiled.c
3  * Initialisation and setup
4  *
5  * @remark Copyright 2002, 2003 OProfile authors
6  * @remark Read the file COPYING
7  *
8  * @author John Levon
9  * @author Philippe Elie
10  * Modified by Aravind Menon for Xen
11  * These modifications are:
12  * Copyright (C) 2005 Hewlett-Packard Co.
13  */
14 
15 #include "config.h"
16 
17 #include "oprofiled.h"
18 #include "opd_printf.h"
19 #include "opd_events.h"
20 
21 #include "op_config.h"
22 #include "op_version.h"
23 #include "op_hw_config.h"
24 #include "op_libiberty.h"
25 #include "op_file.h"
26 #include "op_abi.h"
27 #include "op_string.h"
28 #include "op_cpu_type.h"
29 #include "op_popt.h"
30 #include "op_lockfile.h"
31 #include "op_list.h"
32 #include "op_fileio.h"
33 
34 #include <sys/types.h>
35 #include <sys/resource.h>
36 #include <stdlib.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <assert.h>
43 #include <dirent.h>
44 #include <limits.h>
45 
46 sig_atomic_t signal_alarm;
47 sig_atomic_t signal_hup;
48 sig_atomic_t signal_term;
49 sig_atomic_t signal_child;
50 sig_atomic_t signal_usr1;
51 sig_atomic_t signal_usr2;
52 
53 uint op_nr_counters;
54 op_cpu cpu_type;
55 int vsfile;
56 int vsamples;
57 int varcs;
58 int vmodule;
59 int vmisc;
60 int separate_lib;
61 int separate_kernel;
62 int separate_thread;
63 int separate_cpu;
64 int no_vmlinux;
65 char * vmlinux;
66 char * kernel_range;
67 char * session_dir;
68 int no_xen;
69 char * xenimage;
70 char * xen_range;
71 static char * verbose;
72 static char * binary_name_filter;
73 static char * events;
74 static int showvers;
75 static struct oprofiled_ops * opd_ops;
76 extern struct oprofiled_ops opd_24_ops;
77 extern struct oprofiled_ops opd_26_ops;
78 
79 #define OPD_IMAGE_FILTER_HASH_SIZE 32
80 static struct list_head images_filter[OPD_IMAGE_FILTER_HASH_SIZE];
81 
82 static struct poptOption options[] = {
83 	{ "session-dir", 0, POPT_ARG_STRING, &session_dir, 0, "place sample database in dir instead of default location", "/var/lib/oprofile", },
84 	{ "kernel-range", 'r', POPT_ARG_STRING, &kernel_range, 0, "Kernel VMA range", "start-end", },
85 	{ "vmlinux", 'k', POPT_ARG_STRING, &vmlinux, 0, "vmlinux kernel image", "file", },
86 	{ "no-vmlinux", 0, POPT_ARG_NONE, &no_vmlinux, 0, "vmlinux kernel image file not available", NULL, },
87 	{ "xen-range", 0, POPT_ARG_STRING, &xen_range, 0, "Xen VMA range", "start-end", },
88 	{ "xen-image", 0, POPT_ARG_STRING, &xenimage, 0, "Xen image", "file", },
89 	{ "image", 0, POPT_ARG_STRING, &binary_name_filter, 0, "image name filter", "profile these comma separated image" },
90 	{ "separate-lib", 0, POPT_ARG_INT, &separate_lib, 0, "separate library samples for each distinct application", "[0|1]", },
91 	{ "separate-kernel", 0, POPT_ARG_INT, &separate_kernel, 0, "separate kernel samples for each distinct application", "[0|1]", },
92 	{ "separate-thread", 0, POPT_ARG_INT, &separate_thread, 0, "thread-profiling mode", "[0|1]" },
93 	{ "separate-cpu", 0, POPT_ARG_INT, &separate_cpu, 0, "separate samples for each CPU", "[0|1]" },
94 	{ "events", 'e', POPT_ARG_STRING, &events, 0, "events list", "[events]" },
95 	{ "version", 'v', POPT_ARG_NONE, &showvers, 0, "show version", NULL, },
96 	{ "verbose", 'V', POPT_ARG_STRING, &verbose, 0, "be verbose in log file", "all,sfile,arcs,samples,module,misc", },
97 	POPT_AUTOHELP
98 	{ NULL, 0, 0, NULL, 0, NULL, NULL, },
99 };
100 
101 
opd_open_logfile(void)102 void opd_open_logfile(void)
103 {
104 	if (open(op_log_file, O_WRONLY|O_CREAT|O_NOCTTY|O_APPEND, 0644) == -1) {
105 		perror("oprofiled: couldn't re-open stdout: ");
106 		exit(EXIT_FAILURE);
107 	}
108 
109 	if (dup2(1, 2) == -1) {
110 		perror("oprofiled: couldn't dup stdout to stderr: ");
111 		exit(EXIT_FAILURE);
112 	}
113 }
114 
115 
116 /**
117  * opd_fork - fork and return as child
118  *
119  * fork() and exit the parent with _exit().
120  * Failure is fatal.
121  */
opd_fork(void)122 static void opd_fork(void)
123 {
124 	switch (fork()) {
125 		case -1:
126 			perror("oprofiled: fork() failed: ");
127 			exit(EXIT_FAILURE);
128 			break;
129 		case 0:
130 			break;
131 		default:
132 			/* parent */
133 			_exit(EXIT_SUCCESS);
134 			break;
135 	}
136 }
137 
138 
opd_go_daemon(void)139 static void opd_go_daemon(void)
140 {
141 	opd_fork();
142 
143 	if (chdir(op_session_dir)) {
144 		fprintf(stderr, "oprofiled: opd_go_daemon: couldn't chdir to %s: %s",
145 			op_session_dir, strerror(errno));
146 		exit(EXIT_FAILURE);
147 	}
148 
149 	if (setsid() < 0) {
150 		perror("oprofiled: opd_go_daemon: couldn't setsid: ");
151 		exit(EXIT_FAILURE);
152 	}
153 
154 	opd_fork();
155 }
156 
157 
opd_write_abi(void)158 static void opd_write_abi(void)
159 {
160 	char * cbuf;
161 
162 	cbuf = xmalloc(strlen(op_session_dir) + 5);
163 	strcpy(cbuf, op_session_dir);
164 	strcat(cbuf, "/abi");
165 	op_write_abi_to_file(cbuf);
166 	free(cbuf);
167 }
168 
169 
170 /**
171  * opd_alarm - sync files and report stats
172  */
opd_alarm(int val)173 static void opd_alarm(int val __attribute__((unused)))
174 {
175 	signal_alarm = 1;
176 }
177 
178 
179 /* re-open logfile for logrotate */
opd_sighup(int val)180 static void opd_sighup(int val __attribute__((unused)))
181 {
182 	signal_hup = 1;
183 }
184 
185 
opd_sigterm(int val)186 static void opd_sigterm(int val __attribute__((unused)))
187 {
188 	signal_term = 1;
189 }
190 
opd_sigchild(int val)191 static void opd_sigchild(int val __attribute__((unused)))
192 {
193 	signal_child = 1;
194 }
195 
196 
opd_sigusr1(int val)197 static void opd_sigusr1(int val __attribute__((unused)))
198 {
199 	signal_usr1 = 1;
200 }
201 
202 
opd_sigusr2(int val)203 static void opd_sigusr2(int val __attribute__((unused)))
204 {
205 	signal_usr2 = 1;
206 }
207 
208 
opd_setup_signals(void)209 static void opd_setup_signals(void)
210 {
211 	struct sigaction act;
212 
213 	act.sa_handler = opd_alarm;
214 	act.sa_flags = 0;
215 	sigemptyset(&act.sa_mask);
216 
217 	if (sigaction(SIGALRM, &act, NULL)) {
218 		perror("oprofiled: install of SIGALRM handler failed: ");
219 		exit(EXIT_FAILURE);
220 	}
221 
222 	act.sa_handler = opd_sighup;
223 	act.sa_flags = 0;
224 	sigemptyset(&act.sa_mask);
225 	sigaddset(&act.sa_mask, SIGALRM);
226 
227 	if (sigaction(SIGHUP, &act, NULL)) {
228 		perror("oprofiled: install of SIGHUP handler failed: ");
229 		exit(EXIT_FAILURE);
230 	}
231 
232 	act.sa_handler = opd_sigterm;
233 	act.sa_flags = 0;
234 	sigemptyset(&act.sa_mask);
235 	sigaddset(&act.sa_mask, SIGTERM);
236 
237 	if (sigaction(SIGTERM, &act, NULL)) {
238 		perror("oprofiled: install of SIGTERM handler failed: ");
239 		exit(EXIT_FAILURE);
240 	}
241 
242 	act.sa_handler = opd_sigchild;
243 	act.sa_flags = 0;
244 	sigemptyset(&act.sa_mask);
245 	sigaddset(&act.sa_mask, SIGCHLD);
246 
247 	if (sigaction(SIGCHLD, &act, NULL)) {
248 		perror("oprofiled: install of SIGCHLD handler failed: ");
249 		exit(EXIT_FAILURE);
250 	}
251 
252 	act.sa_handler = opd_sigusr1;
253 	act.sa_flags = 0;
254 	sigemptyset(&act.sa_mask);
255 	sigaddset(&act.sa_mask, SIGTERM);
256 
257 	if (sigaction(SIGUSR1, &act, NULL)) {
258 		perror("oprofiled: install of SIGUSR1 handler failed: ");
259 		exit(EXIT_FAILURE);
260 	}
261 
262 	act.sa_handler = opd_sigusr2;
263 	act.sa_flags = 0;
264 	sigemptyset(&act.sa_mask);
265 	sigaddset(&act.sa_mask, SIGTERM);
266 
267 	if (sigaction(SIGUSR2, &act, NULL)) {
268 		perror("oprofiled: install of SIGUSR2 handler failed: ");
269 		exit(EXIT_FAILURE);
270 	}
271 }
272 
273 
274 struct opd_hashed_name {
275 	char * name;
276 	struct list_head next;
277 };
278 
279 
add_image_filter(char const * name)280 static void add_image_filter(char const * name)
281 {
282 	size_t hash;
283 	struct opd_hashed_name * elt = xmalloc(sizeof(struct opd_hashed_name));
284 	elt->name = xmalloc(PATH_MAX);
285 	if (!realpath(name, elt->name)) {
286 		free(elt->name);
287 		free(elt);
288 		return;
289 	}
290 	hash = op_hash_string(elt->name);
291 	verbprintf(vmisc, "Adding to image filter: \"%s\"\n", elt->name);
292 	list_add(&elt->next, &images_filter[hash % OPD_IMAGE_FILTER_HASH_SIZE]);
293 }
294 
295 
opd_parse_image_filter(void)296 static void opd_parse_image_filter(void)
297 {
298 	size_t i;
299 	char const * last = binary_name_filter;
300 	char const * cur = binary_name_filter;
301 
302 	if (!binary_name_filter)
303 		return;
304 
305 	for (i = 0; i < OPD_IMAGE_FILTER_HASH_SIZE; ++i)
306 		list_init(&images_filter[i]);
307 
308 	while ((cur = strchr(last, ',')) != NULL) {
309 		char * tmp = op_xstrndup(last, cur - last);
310 		add_image_filter(tmp);
311 		free(tmp);
312 		last = cur + 1;
313 	}
314 	add_image_filter(last);
315 }
316 
317 
is_image_ignored(char const * name)318 int is_image_ignored(char const * name)
319 {
320 	size_t hash;
321 	struct list_head * pos;
322 
323 	if (!binary_name_filter)
324 		return 0;
325 
326 	hash = op_hash_string(name);
327 
328 	list_for_each(pos, &images_filter[hash % OPD_IMAGE_FILTER_HASH_SIZE]) {
329 		struct opd_hashed_name * hashed_name =
330 			list_entry(pos, struct opd_hashed_name, next);
331 		if (!strcmp(hashed_name->name, name))
332 			return 0;
333 	}
334 
335 	return 1;
336 }
337 
338 
339 /** return the int in the given oprofilefs file */
opd_read_fs_int(char const * path,char const * name,int fatal)340 int opd_read_fs_int(char const * path, char const * name, int fatal)
341 {
342 	char filename[PATH_MAX + 1];
343 	snprintf(filename, PATH_MAX, "%s/%s", path, name);
344 	return op_read_int_from_file(filename, fatal);
345 }
346 
347 
opd_handle_verbose_option(char const * name)348 static void opd_handle_verbose_option(char const * name)
349 {
350 	if (!strcmp(name, "all")) {
351 		vsfile = 1;
352 		vsamples = 1;
353 		varcs = 1;
354 		vmodule = 1;
355 		vmisc = 1;
356 	} else if (!strcmp(name, "sfile")) {
357 		vsfile = 1;
358 	} else if (!strcmp(name, "arcs")) {
359 		varcs = 1;
360 	} else if (!strcmp(name, "samples")) {
361 		vsamples = 1;
362 	} else if (!strcmp(name, "module")) {
363 		vmodule = 1;
364 	} else if (!strcmp(name, "misc")) {
365 		vmisc = 1;
366 	} else {
367 		fprintf(stderr, "unknown verbose options\n");
368 		exit(EXIT_FAILURE);
369 	}
370 }
371 
opd_parse_verbose(void)372 static void opd_parse_verbose(void)
373 {
374 	char const * last = verbose;
375 	char const * cur = verbose;
376 
377 	if (!verbose)
378 		return;
379 
380 	while ((cur = strchr(last, ',')) != NULL) {
381 		char * tmp = op_xstrndup(last, cur - last);
382 		opd_handle_verbose_option(tmp);
383 		free(tmp);
384 		last = cur + 1;
385 	}
386 	opd_handle_verbose_option(last);
387 }
388 
389 
opd_options(int argc,char const * argv[])390 static void opd_options(int argc, char const * argv[])
391 {
392 	poptContext optcon;
393 	char * tmp;
394 
395 	optcon = op_poptGetContext(NULL, argc, argv, options, 0);
396 
397 	if (showvers)
398 		show_version(argv[0]);
399 
400 	opd_parse_verbose();
401 
402 	if (separate_kernel)
403 		separate_lib = 1;
404 
405 	cpu_type = op_get_cpu_type();
406 	op_nr_counters = op_get_nr_counters(cpu_type);
407 
408 	if (!no_vmlinux) {
409 		if (!vmlinux || !strcmp("", vmlinux)) {
410 			fprintf(stderr, "oprofiled: no vmlinux specified.\n");
411 			poptPrintHelp(optcon, stderr, 0);
412 			exit(EXIT_FAILURE);
413 		}
414 
415 		/* canonicalise vmlinux filename. fix #637805 */
416 		tmp = xmalloc(PATH_MAX);
417 		if (realpath(vmlinux, tmp))
418 			vmlinux = tmp;
419 		else
420 			free(tmp);
421 
422 		if (!kernel_range || !strcmp("", kernel_range)) {
423 			fprintf(stderr, "oprofiled: no kernel VMA range specified.\n");
424 			poptPrintHelp(optcon, stderr, 0);
425 			exit(EXIT_FAILURE);
426 		}
427 	}
428 
429 	if (events == NULL) {
430 		fprintf(stderr, "oprofiled: no events specified.\n");
431 		poptPrintHelp(optcon, stderr, 0);
432 		exit(EXIT_FAILURE);
433 	}
434 
435 	if (!xenimage || !strcmp("", xenimage)) {
436 		no_xen = 1;
437 	} else {
438 		no_xen = 0;
439 
440 		/* canonicalise xen image filename. */
441 		tmp = xmalloc(PATH_MAX);
442 		if (realpath(xenimage, tmp))
443 			xenimage = tmp;
444 		else
445 			free(tmp);
446 
447 		if (!xen_range || !strcmp("", xen_range)) {
448 			fprintf(stderr, "oprofiled: no Xen VMA range specified.\n");
449 			poptPrintHelp(optcon, stderr, 0);
450 			exit(EXIT_FAILURE);
451 		}
452 	}
453 
454 	opd_parse_events(events);
455 
456 	opd_parse_image_filter();
457 
458 	poptFreeContext(optcon);
459 }
460 
461 
462 /* determine what kernel we're running and which daemon
463  * to use
464  */
get_ops(void)465 static struct oprofiled_ops * get_ops(void)
466 {
467 	switch (op_get_interface()) {
468 #ifndef ANDROID
469 		case OP_INTERFACE_24:
470 			printf("Using 2.4 OProfile kernel interface.\n");
471 			return &opd_24_ops;
472 #endif
473 		case OP_INTERFACE_26:
474 			printf("Using 2.6+ OProfile kernel interface.\n");
475 			return &opd_26_ops;
476 		default:
477 			break;
478 	}
479 
480 	fprintf(stderr, "Couldn't determine kernel version.\n");
481 	exit(EXIT_FAILURE);
482 	return NULL;
483 }
484 
485 
main(int argc,char const * argv[])486 int main(int argc, char const * argv[])
487 {
488 	int err;
489 	struct rlimit rlim = { 2048, 2048 };
490 
491 	opd_options(argc, argv);
492 	init_op_config_dirs(session_dir);
493 
494 	opd_setup_signals();
495 
496 	err = setrlimit(RLIMIT_NOFILE, &rlim);
497 	if (err)
498 		perror("warning: could not set RLIMIT_NOFILE to 2048: ");
499 
500 	opd_write_abi();
501 
502 	opd_ops = get_ops();
503 
504 	opd_ops->init();
505 
506 	opd_go_daemon();
507 
508 	/* clean up every 10 minutes */
509 	alarm(60 * 10);
510 
511 	if (op_write_lock_file(op_lock_file)) {
512 		fprintf(stderr, "oprofiled: could not create lock file %s\n",
513 			op_lock_file);
514 		exit(EXIT_FAILURE);
515 	}
516 
517 	opd_ops->start();
518 
519 	opd_ops->exit();
520 
521 	return 0;
522 }
523