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