1/**
2 * @file daemon/init.c
3 * Daemon set up and main loop for 2.6
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author John Levon
9 * @author Philippe Elie
10 * @Modifications Daniel Hansel
11 * Modified by Aravind Menon for Xen
12 * These modifications are:
13 * Copyright (C) 2005 Hewlett-Packard Co.
14 */
15
16#include "config.h"
17
18#include "oprofiled.h"
19#include "opd_stats.h"
20#include "opd_sfile.h"
21#include "opd_pipe.h"
22#include "opd_kernel.h"
23#include "opd_trans.h"
24#include "opd_anon.h"
25#include "opd_perfmon.h"
26#include "opd_printf.h"
27#include "opd_extended.h"
28
29#include "op_version.h"
30#include "op_config.h"
31#include "op_deviceio.h"
32#include "op_get_time.h"
33#include "op_libiberty.h"
34#include "op_fileio.h"
35
36#include <fcntl.h>
37#include <stdio.h>
38#include <errno.h>
39#include <limits.h>
40#include <stdlib.h>
41#include <sys/time.h>
42#if ANDROID
43#include <sys/wait.h>
44#else
45#include <wait.h>
46#endif
47#include <string.h>
48
49size_t kernel_pointer_size;
50
51static fd_t devfd;
52static char * sbuf;
53static size_t s_buf_bytesize;
54extern char * session_dir;
55static char start_time_str[32];
56static int jit_conversion_running;
57
58static void opd_sighup(void);
59static void opd_alarm(void);
60static void opd_sigterm(void);
61static void opd_sigchild(void);
62static void opd_do_jitdumps(void);
63
64/**
65 * opd_open_files - open necessary files
66 *
67 * Open the device files and the log file,
68 * and mmap() the hash map.
69 */
70static void opd_open_files(void)
71{
72	devfd = op_open_device("/dev/oprofile/buffer");
73	if (devfd == -1) {
74		if (errno == EINVAL)
75			fprintf(stderr, "Failed to open device. Possibly you have passed incorrect\n"
76				"parameters. Check /var/log/messages.");
77		else
78			perror("Failed to open profile device");
79		exit(EXIT_FAILURE);
80	}
81
82	/* give output before re-opening stdout as the logfile */
83	printf("Using log file %s\n", op_log_file);
84
85	/* set up logfile */
86	close(0);
87	close(1);
88
89	if (open("/dev/null", O_RDONLY) == -1) {
90		perror("oprofiled: couldn't re-open stdin as /dev/null: ");
91		exit(EXIT_FAILURE);
92	}
93
94	opd_open_logfile();
95	opd_create_pipe();
96
97	printf("oprofiled started %s", op_get_time());
98	printf("kernel pointer size: %lu\n",
99		(unsigned long)kernel_pointer_size);
100	fflush(stdout);
101}
102
103
104/** Done writing out the samples, indicate with complete_dump file */
105static void complete_dump(void)
106{
107	FILE * status_file;
108
109retry:
110	status_file = fopen(op_dump_status, "w");
111
112	if (!status_file && errno == EMFILE) {
113		if (sfile_lru_clear()) {
114			printf("LRU cleared but file open fails for %s.\n",
115			       op_dump_status);
116			abort();
117		}
118		goto retry;
119	}
120
121	if (!status_file) {
122		perror("warning: couldn't set complete_dump: ");
123		return;
124	}
125
126	fprintf(status_file, "1\n");
127	fclose(status_file);
128}
129
130
131/**
132 * opd_do_samples - process a sample buffer
133 * @param opd_buf  buffer to process
134 *
135 * Process a buffer of samples.
136 *
137 * If the sample could be processed correctly, it is written
138 * to the relevant sample file.
139 */
140static void opd_do_samples(char const * opd_buf, ssize_t count)
141{
142	size_t num = count / kernel_pointer_size;
143
144	opd_stats[OPD_DUMP_COUNT]++;
145
146	verbprintf(vmisc, "Read buffer of %d entries.\n", (unsigned int)num);
147
148	opd_process_samples(opd_buf, num);
149
150	complete_dump();
151}
152
153static void opd_do_jitdumps(void)
154{
155	pid_t childpid;
156	int arg_num;
157	unsigned long long end_time = 0ULL;
158	struct timeval tv;
159	char end_time_str[32];
160	char opjitconv_path[PATH_MAX + 1];
161	char * exec_args[6];
162
163	if (jit_conversion_running)
164		return;
165	jit_conversion_running = 1;
166
167	childpid = fork();
168	switch (childpid) {
169		case -1:
170			perror("Error forking JIT dump process!");
171			break;
172		case 0:
173			gettimeofday(&tv, NULL);
174			end_time = tv.tv_sec;
175			sprintf(end_time_str, "%llu", end_time);
176			sprintf(opjitconv_path, "%s/%s", OP_BINDIR, "opjitconv");
177			arg_num = 0;
178			exec_args[arg_num++] = "opjitconv";
179			if (vmisc)
180				exec_args[arg_num++] = "-d";
181			exec_args[arg_num++] = session_dir;
182			exec_args[arg_num++] = start_time_str;
183			exec_args[arg_num++] = end_time_str;
184			exec_args[arg_num] = (char *) NULL;
185			execvp(opjitconv_path, exec_args);
186			fprintf(stderr, "Failed to exec %s: %s\n",
187			        exec_args[0], strerror(errno));
188			/* We don't want any cleanup in the child */
189			_exit(EXIT_FAILURE);
190		default:
191			break;
192	}
193
194}
195
196/**
197 * opd_do_read - enter processing loop
198 * @param buf  buffer to read into
199 * @param size  size of buffer
200 *
201 * Read some of a buffer from the device and process
202 * the contents.
203 */
204static void opd_do_read(char * buf, size_t size)
205{
206	opd_open_pipe();
207
208	while (1) {
209		ssize_t count = -1;
210
211		/* loop to handle EINTR */
212		while (count < 0) {
213			count = op_read_device(devfd, buf, size);
214
215			/* we can lose an alarm or a hup but
216			 * we don't care.
217			 */
218			if (signal_alarm) {
219				signal_alarm = 0;
220				opd_alarm();
221			}
222
223			if (signal_hup) {
224				signal_hup = 0;
225				opd_sighup();
226			}
227
228			if (signal_term)
229				opd_sigterm();
230
231			if (signal_child)
232				opd_sigchild();
233
234			if (signal_usr1) {
235				signal_usr1 = 0;
236				perfmon_start();
237			}
238
239			if (signal_usr2) {
240				signal_usr2 = 0;
241				perfmon_stop();
242			}
243
244			if (is_jitconv_requested()) {
245				verbprintf(vmisc, "Start opjitconv was triggered\n");
246				opd_do_jitdumps();
247			}
248		}
249
250		opd_do_samples(buf, count);
251	}
252
253	opd_close_pipe();
254}
255
256
257/** opd_alarm - sync files and report stats */
258static void opd_alarm(void)
259{
260	sfile_sync_files();
261	opd_print_stats();
262	alarm(60 * 10);
263}
264
265
266/** re-open files for logrotate/opcontrol --reset */
267static void opd_sighup(void)
268{
269	printf("Received SIGHUP.\n");
270	/* We just close them, and re-open them lazily as usual. */
271	sfile_close_files();
272	close(1);
273	close(2);
274	opd_open_logfile();
275}
276
277
278static void clean_exit(void)
279{
280	perfmon_exit();
281	unlink(op_lock_file);
282}
283
284
285static void opd_sigterm(void)
286{
287	opd_do_jitdumps();
288	opd_print_stats();
289	printf("oprofiled stopped %s", op_get_time());
290	opd_ext_deinitialize();
291
292	exit(EXIT_FAILURE);
293}
294
295/* SIGCHLD received from JIT dump child process. */
296static void opd_sigchild(void)
297{
298	int child_status;
299	wait(&child_status);
300	jit_conversion_running = 0;
301	if (WIFEXITED(child_status) && (!WEXITSTATUS(child_status))) {
302		verbprintf(vmisc, "JIT dump processing complete.\n");
303	} else {
304		printf("JIT dump processing exited abnormally: %d\n",
305		       WEXITSTATUS(child_status));
306	}
307
308}
309
310static void opd_26_init(void)
311{
312	size_t i;
313	size_t opd_buf_size;
314	unsigned long long start_time = 0ULL;
315	struct timeval tv;
316
317	opd_create_vmlinux(vmlinux, kernel_range);
318	opd_create_xen(xenimage, xen_range);
319
320	opd_buf_size = opd_read_fs_int("/dev/oprofile/", "buffer_size", 1);
321	kernel_pointer_size = opd_read_fs_int("/dev/oprofile/", "pointer_size", 1);
322
323	s_buf_bytesize = opd_buf_size * kernel_pointer_size;
324
325	sbuf = xmalloc(s_buf_bytesize);
326
327	opd_reread_module_info();
328
329	for (i = 0; i < OPD_MAX_STATS; i++)
330		opd_stats[i] = 0;
331
332	perfmon_init();
333
334	cookie_init();
335	sfile_init();
336	anon_init();
337
338	/* must be /after/ perfmon_init() at least */
339	if (atexit(clean_exit)) {
340		perfmon_exit();
341		perror("oprofiled: couldn't set exit cleanup: ");
342		exit(EXIT_FAILURE);
343	}
344
345	/* trigger kernel module setup before returning control to opcontrol */
346	opd_open_files();
347	gettimeofday(&tv, NULL);
348	start_time = 0ULL;
349	start_time = tv.tv_sec;
350	sprintf(start_time_str, "%llu", start_time);
351
352}
353
354
355static void opd_26_start(void)
356{
357	/* simple sleep-then-process loop */
358	opd_do_read(sbuf, s_buf_bytesize);
359}
360
361
362static void opd_26_exit(void)
363{
364	opd_print_stats();
365	printf("oprofiled stopped %s", op_get_time());
366
367	free(sbuf);
368	free(vmlinux);
369	/* FIXME: free kernel images, sfiles etc. */
370}
371
372struct oprofiled_ops opd_26_ops = {
373	.init = opd_26_init,
374	.start = opd_26_start,
375	.exit = opd_26_exit,
376};
377