src/main.c: Make it possible to pass options to the app under test
[memprof.git] / lib / speedintercept.c
bloba5032ba826b84aec18ed8f15d05f7890bd4db278
1 /* -*- mode: C; c-file-style: "linux" -*- */
3 /* MemProf -- memory profiler and leak detector
4 * Copyright 1999, 2000, 2001, Red Hat, Inc.
5 * Copyright 2002, Kristian Rietveld
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 /*====*/
23 #define _GNU_SOURCE
25 #undef ENABLE_DEBUG
27 #ifdef ENABLE_DEBUG
28 #define MI_DEBUG(arg) mi_debug arg
29 #else /* !ENABLE_DEBUG */
30 #define MI_DEBUG(arg) (void)0
31 #endif /* ENABLE_DEBUG */
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/time.h>
39 #include <sys/ioctl.h>
40 #include <unistd.h>
42 #include <linux/rtc.h>
44 #include "intercept.h"
45 #include "memintercept.h"
46 #include "memintercept-utils.h"
47 #include "mi-perfctr.h"
48 #include "stack-frame.h"
50 static void init_sighandler (int signum);
52 static int timer_fd = -1;
53 static int profile_interval = 10000; /* In usecs */
55 typedef enum {
56 SPEED_PROF_RTC,
57 SPEED_PROF_PERFCTR,
58 SPEED_PROF_ITIMER
59 } SpeedProfType;
61 static SpeedProfType profile_type;
63 static MIBool
64 start_rtc_timer (void)
66 int flags;
67 int irq_rate;
69 MI_DEBUG (("Turning on RTC timer\n"));
71 if ((timer_fd = open ("/dev/rtc", O_RDONLY)) < 0) {
72 mi_perror ("Error opening /dev/rtc");
73 return MI_FALSE;
76 flags = fcntl (timer_fd, F_GETFL);
77 if (fcntl (timer_fd, F_SETFL, flags | FASYNC) < 0) {
78 mi_perror ("Error setting FASYNC");
79 goto bail;
81 if (fcntl (timer_fd, F_SETOWN, getpid ()) < 0) {
82 mi_perror ("Error setting owner for SIGIO");
83 goto bail;
85 if (fcntl (timer_fd, F_SETSIG, SIGPROF) < 0) {
86 mi_perror ("Error setting signal for /dev/rtc");
87 goto bail;
90 irq_rate = 1;
91 while (1000000 / irq_rate > profile_interval &&
92 irq_rate < 8192)
93 irq_rate *= 2;
95 if (ioctl (timer_fd, RTC_IRQP_SET, irq_rate) < 0) {
96 mi_perror ("Error setting interrupt rate");
97 goto bail;
99 if (ioctl (timer_fd, RTC_PIE_ON) < 0) {
100 mi_perror ("Error turning on interrupts");
101 goto bail;
104 init_sighandler (SIGPROF);
105 return MI_TRUE;
107 bail:
108 close (timer_fd);
109 return MI_FALSE;
112 static void
113 stop_rtc_timer (void)
115 if (timer_fd != -1) {
116 if (ioctl (timer_fd, RTC_PIE_OFF) < 0)
117 mi_perror ("Error turning off interrupts");
119 if (close (timer_fd) < 0)
120 mi_perror ("Error closing /dev/rtc");
124 static int profile_itimer_type;
126 static MIBool
127 reset_itimer_timer (void)
129 struct itimerval it, tem;
130 it.it_interval.tv_usec = 0;
131 it.it_interval.tv_sec = 0;
132 it.it_value.tv_usec = profile_interval % 1000000;
133 it.it_value.tv_sec = profile_interval / 1000000;
134 if (setitimer (profile_itimer_type, &it, &tem) != 0) {
135 mi_perror ("Error setting up itimer");
136 return MI_FALSE;
137 } else {
138 return MI_TRUE;
142 static MIBool
143 start_itimer_timer (int timer_type)
145 profile_itimer_type = timer_type;
146 if (!reset_itimer_timer ())
147 return MI_FALSE;
149 init_sighandler (timer_type == ITIMER_PROF ? SIGPROF : SIGALRM);
151 return MI_TRUE;
154 /* XXX hmm.. */
155 #define SIGHANDLER_FRAMES 2
157 static void
158 #if defined (__linux__)
159 sigprof_handler (int unused, struct sigcontext ctx)
160 #else
161 sigprof_handler (int unused)
162 #endif
164 int saved_errno = errno;
165 MIInfo info;
167 info.alloc.operation = MI_TIME;
168 info.alloc.old_ptr = NULL;
169 info.alloc.new_ptr = NULL;
170 info.alloc.size = 1;
172 #if defined (__linux__)
173 mi_call_with_signal_backtrace ((void *)ctx.EIPRIP, (void *)ctx.EBPRBP,
174 (void *)ctx.ESPRSP, mi_write_stack, &info);
175 #else
176 mi_call_with_backtrace (SIGHANDLER_FRAMES, saved_pc, mi_write_stack, &info);
177 #endif
179 if (profile_type == SPEED_PROF_ITIMER)
180 reset_itimer_timer ();
181 errno = saved_errno;
184 static void
185 init_sighandler (int signum)
187 struct sigaction sa;
188 sa.sa_handler = (void (*)(int))sigprof_handler;
189 sigemptyset (&sa.sa_mask);
190 sa.sa_flags = SA_RESTART;
191 if (sigaction (signum, &sa, 0) != 0)
192 mi_perror ("Error setting up signal handler");
195 void
196 mi_init (void)
200 void
201 mi_start (void)
203 const char *type_string;
204 const char *interval_string;
205 char *end;
206 int success = 1;
208 interval_string = getenv ("_MEMPROF_INTERVAL");
209 if (!interval_string)
210 interval_string = "10000";
212 profile_interval = strtol (interval_string, &end, 10);
213 if (*interval_string == '\0' || *end != '\0' || profile_interval <= 0) {
214 mi_debug ("Invalid interval %s\n", interval_string);
215 profile_interval = 10000;
218 type_string = getenv ("_MEMPROF_SPEED_TYPE");
219 if (!type_string)
220 type_string = "cycles";
222 if (strcmp (type_string, "time") == 0) {
223 if (start_rtc_timer ())
224 profile_type = SPEED_PROF_RTC;
225 else if (start_itimer_timer (ITIMER_REAL))
226 profile_type = SPEED_PROF_ITIMER;
227 else
228 success = 0;
230 } else if (strcmp (type_string, "cycles") == 0) {
231 if (mi_perfctr_start (profile_interval))
232 profile_type = SPEED_PROF_PERFCTR;
233 else if (start_itimer_timer (ITIMER_PROF))
234 profile_type = SPEED_PROF_ITIMER;
235 else
236 success = 0;
237 } else {
238 mi_debug ("Unknown value for _MEMPROF_SPEED_TYPE: %s\n");
239 _exit (1);
242 if (!success) {
243 mi_debug ("Couldn't start timer\n");
244 _exit (1);
248 void
249 mi_stop (void)
251 switch (profile_type) {
252 case SPEED_PROF_RTC:
253 stop_rtc_timer ();
254 break;
255 case SPEED_PROF_PERFCTR:
256 mi_perfctr_stop ();
257 break;
258 case SPEED_PROF_ITIMER:
259 break;